1 /* $Id: tree.c,v 1.91 2021/09/07 10:59:18 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2013-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Formatting module to let mandoc(1) show 19 * a human readable representation of the syntax tree. 20 */ 21 #include "config.h" 22 23 #include <sys/types.h> 24 25 #include <assert.h> 26 #include <limits.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <time.h> 30 31 #include "mandoc.h" 32 #include "roff.h" 33 #include "mdoc.h" 34 #include "man.h" 35 #include "tbl.h" 36 #include "eqn.h" 37 #include "main.h" 38 39 static void print_attr(const struct roff_node *); 40 static void print_box(const struct eqn_box *, int); 41 static void print_cellt(enum tbl_cellt); 42 static void print_man(const struct roff_node *, int); 43 static void print_meta(const struct roff_meta *); 44 static void print_mdoc(const struct roff_node *, int); 45 static void print_span(const struct tbl_span *, int); 46 47 48 void 49 tree_mdoc(void *arg, const struct roff_meta *mdoc) 50 { 51 print_meta(mdoc); 52 putchar('\n'); 53 print_mdoc(mdoc->first->child, 0); 54 } 55 56 void 57 tree_man(void *arg, const struct roff_meta *man) 58 { 59 print_meta(man); 60 if (man->hasbody == 0) 61 puts("body = empty"); 62 putchar('\n'); 63 print_man(man->first->child, 0); 64 } 65 66 static void 67 print_meta(const struct roff_meta *meta) 68 { 69 if (meta->title != NULL) 70 printf("title = \"%s\"\n", meta->title); 71 if (meta->name != NULL) 72 printf("name = \"%s\"\n", meta->name); 73 if (meta->msec != NULL) 74 printf("sec = \"%s\"\n", meta->msec); 75 if (meta->vol != NULL) 76 printf("vol = \"%s\"\n", meta->vol); 77 if (meta->arch != NULL) 78 printf("arch = \"%s\"\n", meta->arch); 79 if (meta->os != NULL) 80 printf("os = \"%s\"\n", meta->os); 81 if (meta->date != NULL) 82 printf("date = \"%s\"\n", meta->date); 83 } 84 85 static void 86 print_mdoc(const struct roff_node *n, int indent) 87 { 88 const char *p, *t; 89 int i, j; 90 size_t argc; 91 struct mdoc_argv *argv; 92 93 if (n == NULL) 94 return; 95 96 argv = NULL; 97 argc = 0; 98 t = p = NULL; 99 100 switch (n->type) { 101 case ROFFT_ROOT: 102 t = "root"; 103 break; 104 case ROFFT_BLOCK: 105 t = "block"; 106 break; 107 case ROFFT_HEAD: 108 t = "head"; 109 break; 110 case ROFFT_BODY: 111 if (n->end) 112 t = "body-end"; 113 else 114 t = "body"; 115 break; 116 case ROFFT_TAIL: 117 t = "tail"; 118 break; 119 case ROFFT_ELEM: 120 t = "elem"; 121 break; 122 case ROFFT_TEXT: 123 t = "text"; 124 break; 125 case ROFFT_COMMENT: 126 t = "comment"; 127 break; 128 case ROFFT_TBL: 129 break; 130 case ROFFT_EQN: 131 t = "eqn"; 132 break; 133 default: 134 abort(); 135 } 136 137 switch (n->type) { 138 case ROFFT_TEXT: 139 case ROFFT_COMMENT: 140 p = n->string; 141 break; 142 case ROFFT_BODY: 143 p = roff_name[n->tok]; 144 break; 145 case ROFFT_HEAD: 146 p = roff_name[n->tok]; 147 break; 148 case ROFFT_TAIL: 149 p = roff_name[n->tok]; 150 break; 151 case ROFFT_ELEM: 152 p = roff_name[n->tok]; 153 if (n->args) { 154 argv = n->args->argv; 155 argc = n->args->argc; 156 } 157 break; 158 case ROFFT_BLOCK: 159 p = roff_name[n->tok]; 160 if (n->args) { 161 argv = n->args->argv; 162 argc = n->args->argc; 163 } 164 break; 165 case ROFFT_TBL: 166 break; 167 case ROFFT_EQN: 168 p = "EQ"; 169 break; 170 case ROFFT_ROOT: 171 p = "root"; 172 break; 173 default: 174 abort(); 175 } 176 177 if (n->span) { 178 assert(NULL == p && NULL == t); 179 print_span(n->span, indent); 180 } else { 181 for (i = 0; i < indent; i++) 182 putchar(' '); 183 184 printf("%s (%s)", p, t); 185 186 for (i = 0; i < (int)argc; i++) { 187 printf(" -%s", mdoc_argnames[argv[i].arg]); 188 if (argv[i].sz > 0) 189 printf(" ["); 190 for (j = 0; j < (int)argv[i].sz; j++) 191 printf(" [%s]", argv[i].value[j]); 192 if (argv[i].sz > 0) 193 printf(" ]"); 194 } 195 print_attr(n); 196 } 197 if (n->eqn) 198 print_box(n->eqn->first, indent + 4); 199 if (n->child) 200 print_mdoc(n->child, indent + 201 (n->type == ROFFT_BLOCK ? 2 : 4)); 202 if (n->next) 203 print_mdoc(n->next, indent); 204 } 205 206 static void 207 print_man(const struct roff_node *n, int indent) 208 { 209 const char *p, *t; 210 int i; 211 212 if (n == NULL) 213 return; 214 215 t = p = NULL; 216 217 switch (n->type) { 218 case ROFFT_ROOT: 219 t = "root"; 220 break; 221 case ROFFT_ELEM: 222 t = "elem"; 223 break; 224 case ROFFT_TEXT: 225 t = "text"; 226 break; 227 case ROFFT_COMMENT: 228 t = "comment"; 229 break; 230 case ROFFT_BLOCK: 231 t = "block"; 232 break; 233 case ROFFT_HEAD: 234 t = "head"; 235 break; 236 case ROFFT_BODY: 237 t = "body"; 238 break; 239 case ROFFT_TBL: 240 break; 241 case ROFFT_EQN: 242 t = "eqn"; 243 break; 244 default: 245 abort(); 246 } 247 248 switch (n->type) { 249 case ROFFT_TEXT: 250 case ROFFT_COMMENT: 251 p = n->string; 252 break; 253 case ROFFT_ELEM: 254 case ROFFT_BLOCK: 255 case ROFFT_HEAD: 256 case ROFFT_BODY: 257 p = roff_name[n->tok]; 258 break; 259 case ROFFT_ROOT: 260 p = "root"; 261 break; 262 case ROFFT_TBL: 263 break; 264 case ROFFT_EQN: 265 p = "EQ"; 266 break; 267 default: 268 abort(); 269 } 270 271 if (n->span) { 272 assert(NULL == p && NULL == t); 273 print_span(n->span, indent); 274 } else { 275 for (i = 0; i < indent; i++) 276 putchar(' '); 277 printf("%s (%s)", p, t); 278 print_attr(n); 279 } 280 if (n->eqn) 281 print_box(n->eqn->first, indent + 4); 282 if (n->child) 283 print_man(n->child, indent + 284 (n->type == ROFFT_BLOCK ? 2 : 4)); 285 if (n->next) 286 print_man(n->next, indent); 287 } 288 289 static void 290 print_attr(const struct roff_node *n) 291 { 292 putchar(' '); 293 if (n->flags & NODE_DELIMO) 294 putchar('('); 295 if (n->flags & NODE_LINE) 296 putchar('*'); 297 printf("%d:%d", n->line, n->pos + 1); 298 if (n->flags & NODE_DELIMC) 299 putchar(')'); 300 if (n->flags & NODE_EOS) 301 putchar('.'); 302 if (n->flags & NODE_ID) { 303 printf(" ID"); 304 if (n->flags & NODE_HREF) 305 printf("=HREF"); 306 } else if (n->flags & NODE_HREF) 307 printf(" HREF"); 308 else if (n->tag != NULL) 309 printf(" STRAYTAG"); 310 if (n->tag != NULL) 311 printf("=%s", n->tag); 312 if (n->flags & NODE_BROKEN) 313 printf(" BROKEN"); 314 if (n->flags & NODE_NOFILL) 315 printf(" NOFILL"); 316 if (n->flags & NODE_NOSRC) 317 printf(" NOSRC"); 318 if (n->flags & NODE_NOPRT) 319 printf(" NOPRT"); 320 putchar('\n'); 321 } 322 323 static void 324 print_box(const struct eqn_box *ep, int indent) 325 { 326 int i; 327 const char *t; 328 329 static const char *posnames[] = { 330 NULL, "sup", "subsup", "sub", 331 "to", "from", "fromto", 332 "over", "sqrt", NULL }; 333 334 if (NULL == ep) 335 return; 336 for (i = 0; i < indent; i++) 337 putchar(' '); 338 339 t = NULL; 340 switch (ep->type) { 341 case EQN_LIST: 342 t = "eqn-list"; 343 break; 344 case EQN_SUBEXPR: 345 t = "eqn-expr"; 346 break; 347 case EQN_TEXT: 348 t = "eqn-text"; 349 break; 350 case EQN_PILE: 351 t = "eqn-pile"; 352 break; 353 case EQN_MATRIX: 354 t = "eqn-matrix"; 355 break; 356 } 357 358 fputs(t, stdout); 359 if (ep->pos) 360 printf(" pos=%s", posnames[ep->pos]); 361 if (ep->left) 362 printf(" left=\"%s\"", ep->left); 363 if (ep->right) 364 printf(" right=\"%s\"", ep->right); 365 if (ep->top) 366 printf(" top=\"%s\"", ep->top); 367 if (ep->bottom) 368 printf(" bottom=\"%s\"", ep->bottom); 369 if (ep->text) 370 printf(" text=\"%s\"", ep->text); 371 if (ep->font) 372 printf(" font=%d", ep->font); 373 if (ep->size != EQN_DEFSIZE) 374 printf(" size=%d", ep->size); 375 if (ep->expectargs != UINT_MAX && ep->expectargs != ep->args) 376 printf(" badargs=%zu(%zu)", ep->args, ep->expectargs); 377 else if (ep->args) 378 printf(" args=%zu", ep->args); 379 putchar('\n'); 380 381 print_box(ep->first, indent + 4); 382 print_box(ep->next, indent); 383 } 384 385 static void 386 print_cellt(enum tbl_cellt pos) 387 { 388 switch(pos) { 389 case TBL_CELL_LEFT: 390 putchar('L'); 391 break; 392 case TBL_CELL_LONG: 393 putchar('a'); 394 break; 395 case TBL_CELL_CENTRE: 396 putchar('c'); 397 break; 398 case TBL_CELL_RIGHT: 399 putchar('r'); 400 break; 401 case TBL_CELL_NUMBER: 402 putchar('n'); 403 break; 404 case TBL_CELL_SPAN: 405 putchar('s'); 406 break; 407 case TBL_CELL_DOWN: 408 putchar('^'); 409 break; 410 case TBL_CELL_HORIZ: 411 putchar('-'); 412 break; 413 case TBL_CELL_DHORIZ: 414 putchar('='); 415 break; 416 case TBL_CELL_MAX: 417 putchar('#'); 418 break; 419 } 420 } 421 422 static void 423 print_span(const struct tbl_span *sp, int indent) 424 { 425 const struct tbl_dat *dp; 426 const struct tbl_cell *cp; 427 int i; 428 429 if (sp->prev == NULL) { 430 for (i = 0; i < indent; i++) 431 putchar(' '); 432 printf("%d", sp->opts->cols); 433 if (sp->opts->opts & TBL_OPT_CENTRE) 434 fputs(" center", stdout); 435 if (sp->opts->opts & TBL_OPT_EXPAND) 436 fputs(" expand", stdout); 437 if (sp->opts->opts & TBL_OPT_ALLBOX) 438 fputs(" allbox", stdout); 439 if (sp->opts->opts & TBL_OPT_BOX) 440 fputs(" box", stdout); 441 if (sp->opts->opts & TBL_OPT_DBOX) 442 fputs(" doublebox", stdout); 443 if (sp->opts->opts & TBL_OPT_NOKEEP) 444 fputs(" nokeep", stdout); 445 if (sp->opts->opts & TBL_OPT_NOSPACE) 446 fputs(" nospaces", stdout); 447 if (sp->opts->opts & TBL_OPT_NOWARN) 448 fputs(" nowarn", stdout); 449 printf(" (tbl options) %d:1\n", sp->line); 450 } 451 452 for (i = 0; i < indent; i++) 453 putchar(' '); 454 455 switch (sp->pos) { 456 case TBL_SPAN_HORIZ: 457 putchar('-'); 458 putchar(' '); 459 break; 460 case TBL_SPAN_DHORIZ: 461 putchar('='); 462 putchar(' '); 463 break; 464 default: 465 for (cp = sp->layout->first; cp != NULL; cp = cp->next) 466 print_cellt(cp->pos); 467 putchar(' '); 468 for (dp = sp->first; dp; dp = dp->next) { 469 if ((cp = dp->layout) == NULL) 470 putchar('*'); 471 else { 472 printf("%d", cp->col); 473 print_cellt(dp->layout->pos); 474 switch (cp->font) { 475 case ESCAPE_FONTROMAN: 476 break; 477 case ESCAPE_FONTBOLD: 478 putchar('b'); 479 break; 480 case ESCAPE_FONTITALIC: 481 putchar('i'); 482 break; 483 case ESCAPE_FONTBI: 484 fputs("bi", stdout); 485 break; 486 case ESCAPE_FONTCR: 487 putchar('c'); 488 break; 489 case ESCAPE_FONTCB: 490 fputs("cb", stdout); 491 break; 492 case ESCAPE_FONTCI: 493 fputs("ci", stdout); 494 break; 495 default: 496 abort(); 497 } 498 if (cp->flags & TBL_CELL_TALIGN) 499 putchar('t'); 500 if (cp->flags & TBL_CELL_UP) 501 putchar('u'); 502 if (cp->flags & TBL_CELL_BALIGN) 503 putchar('d'); 504 if (cp->flags & TBL_CELL_WIGN) 505 putchar('z'); 506 if (cp->flags & TBL_CELL_EQUAL) 507 putchar('e'); 508 if (cp->flags & TBL_CELL_WMAX) 509 putchar('x'); 510 } 511 switch (dp->pos) { 512 case TBL_DATA_HORIZ: 513 case TBL_DATA_NHORIZ: 514 putchar('-'); 515 break; 516 case TBL_DATA_DHORIZ: 517 case TBL_DATA_NDHORIZ: 518 putchar('='); 519 break; 520 default: 521 putchar(dp->block ? '{' : '['); 522 if (dp->string != NULL) 523 fputs(dp->string, stdout); 524 putchar(dp->block ? '}' : ']'); 525 break; 526 } 527 if (dp->hspans) 528 printf(">%d", dp->hspans); 529 if (dp->vspans) 530 printf("v%d", dp->vspans); 531 putchar(' '); 532 } 533 break; 534 } 535 printf("(tbl) %d:1\n", sp->line); 536 } 537