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