xref: /freebsd/contrib/mandoc/tree.c (revision ebacd8013fe5f7fdf9f6a5b286f6680dd2891036)
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