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