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