xref: /openbsd/usr.bin/mandoc/tree.c (revision 09467b48)
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