1 /* $OpenBSD: tree.c,v 1.59 2022/01/12 04:53:57 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2013-2015, 2017-2022 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 <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
tree_mdoc(void * arg,const struct roff_meta * mdoc)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
tree_man(void * arg,const struct roff_meta * man)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
print_meta(const struct roff_meta * meta)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
print_mdoc(const struct roff_node * n,int indent)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
print_man(const struct roff_node * n,int indent)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
print_attr(const struct roff_node * n)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
print_box(const struct eqn_box * ep,int indent)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
print_cellt(enum tbl_cellt pos)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
print_span(const struct tbl_span * sp,int indent)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 switch (cp->font) {
473 case ESCAPE_FONTROMAN:
474 break;
475 case ESCAPE_FONTBOLD:
476 putchar('b');
477 break;
478 case ESCAPE_FONTITALIC:
479 putchar('i');
480 break;
481 case ESCAPE_FONTBI:
482 fputs("bi", stdout);
483 break;
484 case ESCAPE_FONTCR:
485 putchar('c');
486 break;
487 case ESCAPE_FONTCB:
488 fputs("cb", stdout);
489 break;
490 case ESCAPE_FONTCI:
491 fputs("ci", stdout);
492 break;
493 default:
494 abort();
495 }
496 if (cp->flags & TBL_CELL_TALIGN)
497 putchar('t');
498 if (cp->flags & TBL_CELL_UP)
499 putchar('u');
500 if (cp->flags & TBL_CELL_BALIGN)
501 putchar('d');
502 if (cp->flags & TBL_CELL_WIGN)
503 putchar('z');
504 if (cp->flags & TBL_CELL_EQUAL)
505 putchar('e');
506 if (cp->flags & TBL_CELL_WMAX)
507 putchar('x');
508 }
509 switch (dp->pos) {
510 case TBL_DATA_NHORIZ:
511 putchar('\\');
512 /* FALLTHROUGH */
513 case TBL_DATA_HORIZ:
514 putchar('_');
515 break;
516 case TBL_DATA_NDHORIZ:
517 putchar('\\');
518 /* FALLTHROUGH */
519 case TBL_DATA_DHORIZ:
520 putchar('=');
521 break;
522 default:
523 putchar(dp->block ? '{' : '[');
524 if (dp->string != NULL)
525 fputs(dp->string, stdout);
526 putchar(dp->block ? '}' : ']');
527 break;
528 }
529 if (dp->hspans)
530 printf(">%d", dp->hspans);
531 if (dp->vspans)
532 printf("v%d", dp->vspans);
533 putchar(' ');
534 }
535 break;
536 }
537 printf("(tbl) %d:1\n", sp->line);
538 }
539