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
39static	void	print_attr(const struct roff_node *);
40static	void	print_box(const struct eqn_box *, int);
41static	void	print_cellt(enum tbl_cellt);
42static	void	print_man(const struct roff_node *, int);
43static	void	print_meta(const struct roff_meta *);
44static	void	print_mdoc(const struct roff_node *, int);
45static	void	print_span(const struct tbl_span *, int);
46
47
48void
49tree_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
56void
57tree_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
66static void
67print_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
85static void
86print_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
206static void
207print_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
289static void
290print_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
323static void
324print_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
385static void
386print_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
422static void
423print_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