Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.95
1.95 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.94 2019/04/07 14:49:26 schwarze Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2014 Kristaps Dzonsons <kristaps@bsd.lv>
1.50 schwarze 4: * Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 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 <assert.h>
19: #include <ctype.h>
20: #include <stdio.h>
21: #include <stdlib.h>
22:
1.74 schwarze 23: #include "node.h"
1.75 schwarze 24: #include "macro.h"
1.74 schwarze 25: #include "format.h"
26:
27: /*
28: * The implementation of the mdoc(7) formatter.
29: */
1.12 kristaps 30:
1.74 schwarze 31: static void pnode_print(struct format *, struct pnode *);
1.25 kristaps 32:
1.37 kristaps 33:
1.54 schwarze 34: static void
1.93 schwarze 35: pnode_printtext(struct format *f, struct pnode *n)
36: {
1.94 schwarze 37: struct pnode *nn;
1.93 schwarze 38: char *cp;
39: char last;
40:
41: if (n->bsz == 0) {
42: assert(n->real < n->b);
43: return;
1.94 schwarze 44: }
45:
46: /*
47: * Text preceding a macro without intervening whitespace
48: * requires a .Pf macro.
49: * Set the spacing flag to avoid a redundant .Ns macro.
50: */
51:
52: if (f->linestate != LINE_MACRO &&
53: (nn = TAILQ_NEXT(n, child)) != NULL && nn->spc == 0 &&
54: (nn->node != NODE_TEXT && nn->node != NODE_ESCAPE)) {
55: macro_open(f, "Pf");
56: nn->spc = 1;
1.93 schwarze 57: }
58:
59: if (f->linestate == LINE_NEW) {
60: last = '\n';
61: f->linestate = LINE_TEXT;
62: } else {
63: last = ' ';
64: if (n->spc || f->linestate == LINE_MACRO)
65: putchar(' ');
66: }
67:
68: if (n->node == NODE_ESCAPE) {
69: fputs(n->b, stdout);
70: return;
71: }
72:
73: /*
74: * Remove the prefix '-' from <option> elements
75: * because the arguments of .Fl macros do not need it.
76: */
77:
78: cp = n->b;
79: if (n->parent != NULL && n->parent->node == NODE_OPTION && *cp == '-')
80: cp++;
81:
82: /*
83: * Print the text, skipping whitespace on new lines,
84: * escaping control characters on new lines,
85: * and escaping backslashes.
86: */
87:
88: for (; *cp != '\0'; cp++) {
89: if (last == '\n') {
90: if (isspace((unsigned char)*cp))
91: continue;
92: if (*cp == '\'' || *cp == '.')
93: fputs("\\&", stdout);
94: }
95: putchar(last = *cp);
96: if (last == '\\')
97: putchar('e');
98: }
99: }
100:
101: static void
1.74 schwarze 102: pnode_printpara(struct format *p, struct pnode *pn)
1.54 schwarze 103: {
104: struct pnode *pp;
105:
1.61 schwarze 106: if ((pp = TAILQ_PREV(pn, pnodeq, child)) == NULL &&
107: (pp = pn->parent) == NULL)
1.54 schwarze 108: return;
109:
1.61 schwarze 110: switch (pp->node) {
111: case NODE_ENTRY:
112: case NODE_LISTITEM:
113: return;
114: case NODE_PREFACE:
115: case NODE_SECTION:
116: if (p->level < 3)
117: return;
118: break;
119: default:
120: break;
121: }
1.69 schwarze 122: macro_line(p, "Pp");
1.54 schwarze 123: }
124:
1.37 kristaps 125: /*
1.10 kristaps 126: * If the SYNOPSIS macro has a superfluous title, kill it.
1.8 kristaps 127: */
1.1 kristaps 128: static void
1.74 schwarze 129: pnode_printrefsynopsisdiv(struct format *p, struct pnode *pn)
1.6 kristaps 130: {
1.71 schwarze 131: struct pnode *pp, *pq;
1.6 kristaps 132:
1.71 schwarze 133: TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq)
134: if (pp->node == NODE_TITLE)
1.6 kristaps 135: pnode_unlink(pp);
1.71 schwarze 136:
137: macro_line(p, "Sh SYNOPSIS");
1.6 kristaps 138: }
139:
1.8 kristaps 140: /*
141: * Start a hopefully-named `Sh' section.
142: */
1.6 kristaps 143: static void
1.74 schwarze 144: pnode_printrefsect(struct format *p, struct pnode *pn)
1.1 kristaps 145: {
146: struct pnode *pp;
1.52 schwarze 147: const char *title;
148: int flags, level;
149:
1.64 schwarze 150: if (pn->parent == NULL)
1.56 schwarze 151: return;
152:
1.52 schwarze 153: level = ++p->level;
1.72 schwarze 154: flags = ARG_SPACE;
155: if (level == 1)
156: flags |= ARG_UPPER;
1.64 schwarze 157: if (level < 3) {
1.52 schwarze 158: switch (pn->node) {
1.62 schwarze 159: case NODE_CAUTION:
160: case NODE_NOTE:
161: case NODE_TIP:
162: case NODE_WARNING:
1.52 schwarze 163: level = 3;
164: break;
165: default:
166: break;
167: }
168: }
1.1 kristaps 169:
170: TAILQ_FOREACH(pp, &pn->childq, child)
1.64 schwarze 171: if (pp->node == NODE_TITLE)
1.1 kristaps 172: break;
173:
1.64 schwarze 174: if (pp == NULL) {
1.52 schwarze 175: switch (pn->node) {
1.62 schwarze 176: case NODE_PREFACE:
1.52 schwarze 177: title = "Preface";
178: break;
1.62 schwarze 179: case NODE_CAUTION:
1.52 schwarze 180: title = "Caution";
181: break;
1.62 schwarze 182: case NODE_NOTE:
1.52 schwarze 183: title = "Note";
184: break;
1.62 schwarze 185: case NODE_TIP:
1.52 schwarze 186: title = "Tip";
187: break;
1.62 schwarze 188: case NODE_WARNING:
1.52 schwarze 189: title = "Warning";
190: break;
191: default:
192: title = "Unknown";
193: break;
194: }
195: }
196:
197: switch (level) {
1.62 schwarze 198: case 1:
1.95 ! schwarze 199: macro_close(p);
1.69 schwarze 200: macro_open(p, "Sh");
1.29 kristaps 201: break;
1.62 schwarze 202: case 2:
1.95 ! schwarze 203: macro_close(p);
1.69 schwarze 204: macro_open(p, "Ss");
1.29 kristaps 205: break;
1.52 schwarze 206: default:
1.54 schwarze 207: pnode_printpara(p, pn);
1.69 schwarze 208: macro_open(p, "Sy");
1.29 kristaps 209: break;
210: }
1.20 kristaps 211:
1.64 schwarze 212: if (pp != NULL) {
1.69 schwarze 213: macro_addnode(p, pp, flags);
1.5 kristaps 214: pnode_unlink(pp);
1.52 schwarze 215: } else
1.72 schwarze 216: macro_addarg(p, title, ARG_SPACE | ARG_QUOTED);
1.69 schwarze 217: macro_close(p);
1.1 kristaps 218: }
219:
1.8 kristaps 220: /*
221: * Start a reference, extracting the title and volume.
222: */
1.1 kristaps 223: static void
1.74 schwarze 224: pnode_printciterefentry(struct format *p, struct pnode *pn)
1.1 kristaps 225: {
226: struct pnode *pp, *title, *manvol;
227:
228: title = manvol = NULL;
1.69 schwarze 229: TAILQ_FOREACH(pp, &pn->childq, child) {
1.64 schwarze 230: if (pp->node == NODE_MANVOLNUM)
1.1 kristaps 231: manvol = pp;
1.64 schwarze 232: else if (pp->node == NODE_REFENTRYTITLE)
1.1 kristaps 233: title = pp;
1.69 schwarze 234: }
235: macro_open(p, "Xr");
236: if (title == NULL)
1.72 schwarze 237: macro_addarg(p, "unknown", ARG_SPACE);
1.69 schwarze 238: else
1.72 schwarze 239: macro_addnode(p, title, ARG_SPACE | ARG_SINGLE);
1.69 schwarze 240: if (manvol == NULL)
1.72 schwarze 241: macro_addarg(p, "1", ARG_SPACE);
1.64 schwarze 242: else
1.72 schwarze 243: macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.71 schwarze 244: pnode_unlinksub(pn);
1.1 kristaps 245: }
246:
247: static void
1.74 schwarze 248: pnode_printrefmeta(struct format *p, struct pnode *pn)
1.1 kristaps 249: {
250: struct pnode *pp, *title, *manvol;
251:
252: title = manvol = NULL;
1.69 schwarze 253: TAILQ_FOREACH(pp, &pn->childq, child) {
1.64 schwarze 254: if (pp->node == NODE_MANVOLNUM)
1.1 kristaps 255: manvol = pp;
1.64 schwarze 256: else if (pp->node == NODE_REFENTRYTITLE)
1.1 kristaps 257: title = pp;
1.69 schwarze 258: }
1.95 ! schwarze 259: macro_close(p);
1.69 schwarze 260: macro_open(p, "Dt");
261: if (title == NULL)
1.72 schwarze 262: macro_addarg(p, "UNKNOWN", ARG_SPACE);
1.69 schwarze 263: else
1.72 schwarze 264: macro_addnode(p, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
1.69 schwarze 265: if (manvol == NULL)
1.72 schwarze 266: macro_addarg(p, "1", ARG_SPACE);
1.13 kristaps 267: else
1.72 schwarze 268: macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.69 schwarze 269: macro_close(p);
1.71 schwarze 270: pnode_unlink(pn);
1.1 kristaps 271: }
272:
1.3 kristaps 273: static void
1.86 schwarze 274: pnode_printfuncdef(struct format *f, struct pnode *n)
1.3 kristaps 275: {
1.86 schwarze 276: struct pnode *nc;
1.3 kristaps 277:
1.86 schwarze 278: nc = TAILQ_FIRST(&n->childq);
279: if (nc != NULL && nc->node == NODE_TEXT) {
280: macro_argline(f, "Ft", nc->b);
281: pnode_unlink(nc);
1.13 kristaps 282: }
1.86 schwarze 283: macro_nodeline(f, "Fo", n, ARG_SINGLE);
284: pnode_unlinksub(n);
1.3 kristaps 285: }
286:
1.40 kristaps 287: /*
1.41 kristaps 288: * The <mml:mfenced> node is a little peculiar.
289: * First, it can have arbitrary open and closing tokens, which default
290: * to parentheses.
291: * Second, >1 arguments are separated by commas.
292: */
293: static void
1.74 schwarze 294: pnode_printmathfenced(struct format *p, struct pnode *pn)
1.41 kristaps 295: {
296: struct pnode *pp;
297:
1.59 schwarze 298: printf("left %s ", pnode_getattr_raw(pn, ATTRKEY_OPEN, "("));
1.41 kristaps 299:
300: pp = TAILQ_FIRST(&pn->childq);
301: pnode_print(p, pp);
302:
1.64 schwarze 303: while ((pp = TAILQ_NEXT(pp, child)) != NULL) {
1.41 kristaps 304: putchar(',');
305: pnode_print(p, pp);
306: }
1.59 schwarze 307: printf("right %s ", pnode_getattr_raw(pn, ATTRKEY_CLOSE, ")"));
1.71 schwarze 308: pnode_unlinksub(pn);
1.41 kristaps 309: }
310:
311: /*
1.40 kristaps 312: * These math nodes require special handling because they have infix
313: * syntax, instead of the usual prefix or prefix.
314: * So we need to break up the first and second child node with a
315: * particular eqn(7) word.
316: */
317: static void
1.74 schwarze 318: pnode_printmath(struct format *p, struct pnode *pn)
1.40 kristaps 319: {
320: struct pnode *pp;
321:
322: pp = TAILQ_FIRST(&pn->childq);
323: pnode_print(p, pp);
324:
325: switch (pn->node) {
1.62 schwarze 326: case NODE_MML_MSUP:
1.42 kristaps 327: fputs(" sup ", stdout);
1.40 kristaps 328: break;
1.62 schwarze 329: case NODE_MML_MFRAC:
1.42 kristaps 330: fputs(" over ", stdout);
1.40 kristaps 331: break;
1.62 schwarze 332: case NODE_MML_MSUB:
1.42 kristaps 333: fputs(" sub ", stdout);
1.40 kristaps 334: break;
335: default:
336: break;
337: }
338:
339: pp = TAILQ_NEXT(pp, child);
340: pnode_print(p, pp);
1.71 schwarze 341: pnode_unlinksub(pn);
1.40 kristaps 342: }
343:
1.3 kristaps 344: static void
1.74 schwarze 345: pnode_printfuncprototype(struct format *p, struct pnode *pn)
1.3 kristaps 346: {
347: struct pnode *pp, *fdef;
348:
349: TAILQ_FOREACH(fdef, &pn->childq, child)
1.64 schwarze 350: if (fdef->node == NODE_FUNCDEF)
1.3 kristaps 351: break;
352:
1.86 schwarze 353: if (fdef != NULL) {
1.3 kristaps 354: pnode_printfuncdef(p, fdef);
1.86 schwarze 355: pnode_unlink(fdef);
356: } else
1.69 schwarze 357: macro_line(p, "Fo UNKNOWN");
1.3 kristaps 358:
1.44 schwarze 359: TAILQ_FOREACH(pp, &pn->childq, child)
1.86 schwarze 360: macro_nodeline(p, "Fa", pp, ARG_SINGLE);
1.3 kristaps 361:
1.69 schwarze 362: macro_line(p, "Fc");
1.71 schwarze 363: pnode_unlinksub(pn);
1.3 kristaps 364: }
365:
1.44 schwarze 366: /*
1.10 kristaps 367: * The <arg> element is more complicated than it should be because text
368: * nodes are treated like ".Ar foo", but non-text nodes need to be
369: * re-sent into the printer (i.e., without the preceding ".Ar").
1.12 kristaps 370: * This also handles the case of "repetition" (or in other words, the
371: * ellipsis following an argument) and optionality.
1.10 kristaps 372: */
1.4 kristaps 373: static void
1.74 schwarze 374: pnode_printarg(struct format *p, struct pnode *pn)
1.4 kristaps 375: {
376: struct pnode *pp;
1.12 kristaps 377: struct pattr *ap;
378: int isop, isrep;
379:
380: isop = 1;
381: isrep = 0;
1.69 schwarze 382: TAILQ_FOREACH(ap, &pn->attrq, child) {
1.64 schwarze 383: if (ap->key == ATTRKEY_CHOICE &&
384: (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ))
1.12 kristaps 385: isop = 0;
1.64 schwarze 386: else if (ap->key == ATTRKEY_REP && ap->val == ATTRVAL_REPEAT)
1.12 kristaps 387: isrep = 1;
388: }
1.69 schwarze 389: if (isop)
390: macro_open(p, "Op");
1.4 kristaps 391:
1.10 kristaps 392: TAILQ_FOREACH(pp, &pn->childq, child) {
1.69 schwarze 393: if (pp->node == NODE_TEXT)
394: macro_open(p, "Ar");
1.10 kristaps 395: pnode_print(p, pp);
1.64 schwarze 396: if (isrep && pp->node == NODE_TEXT)
1.72 schwarze 397: macro_addarg(p, "...", ARG_SPACE);
1.10 kristaps 398: }
1.71 schwarze 399: pnode_unlinksub(pn);
1.4 kristaps 400: }
401:
1.24 kristaps 402: static void
1.74 schwarze 403: pnode_printgroup(struct format *p, struct pnode *pn)
1.24 kristaps 404: {
405: struct pnode *pp, *np;
406: struct pattr *ap;
407: int isop, sv;
408:
409: isop = 1;
1.44 schwarze 410: TAILQ_FOREACH(ap, &pn->attrq, child)
1.64 schwarze 411: if (ap->key == ATTRKEY_CHOICE &&
412: (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ)) {
1.24 kristaps 413: isop = 0;
414: break;
415: }
416:
1.44 schwarze 417: /*
1.24 kristaps 418: * Make sure we're on a macro line.
419: * This will prevent pnode_print() for putting us on a
420: * subsequent line.
421: */
1.69 schwarze 422: sv = p->linestate == LINE_NEW;
1.44 schwarze 423: if (isop)
1.69 schwarze 424: macro_open(p, "Op");
1.24 kristaps 425: else if (sv)
1.69 schwarze 426: macro_open(p, "No");
1.24 kristaps 427:
428: /*
429: * Keep on printing text separated by the vertical bar as long
430: * as we're within the same origin node as the group.
431: * This is kind of a nightmare.
432: * Eh, DocBook...
433: * FIXME: if there's a "Fl", we don't cut off the leading "-"
434: * like we do in pnode_print().
435: */
436: TAILQ_FOREACH(pp, &pn->childq, child) {
437: pnode_print(p, pp);
438: np = TAILQ_NEXT(pp, child);
1.64 schwarze 439: while (np != NULL) {
1.24 kristaps 440: if (pp->node != np->node)
441: break;
1.72 schwarze 442: macro_addarg(p, "|", ARG_SPACE);
443: macro_addnode(p, np, ARG_SPACE);
1.24 kristaps 444: pp = np;
445: np = TAILQ_NEXT(np, child);
446: }
447: }
1.69 schwarze 448: if (sv)
449: macro_close(p);
1.71 schwarze 450: pnode_unlinksub(pn);
1.24 kristaps 451: }
452:
1.7 kristaps 453: static void
1.78 schwarze 454: pnode_printauthor(struct format *f, struct pnode *n)
455: {
456: struct pnode *nc, *ncn;
457: int have_contrib, have_name;
458:
459: /*
460: * Print <contrib> children up front, before the .An scope,
461: * and figure out whether we a name of a person.
462: */
463:
464: have_contrib = have_name = 0;
465: TAILQ_FOREACH_SAFE(nc, &n->childq, child, ncn) {
466: switch (nc->node) {
467: case NODE_CONTRIB:
468: if (have_contrib)
469: print_text(f, ",", 0);
470: print_textnode(f, nc);
471: pnode_unlink(nc);
472: have_contrib = 1;
473: break;
474: case NODE_PERSONNAME:
475: have_name = 1;
476: break;
477: default:
478: break;
479: }
480: }
481: if (TAILQ_FIRST(&n->childq) == NULL)
482: return;
483:
484: if (have_contrib)
485: print_text(f, ":", 0);
486:
487: /*
488: * If we have a name, print it in the .An scope and leave
489: * all other content for child handlers, to print after the
490: * scope. Otherwise, print everything in the scope.
491: */
492:
493: macro_open(f, "An");
494: TAILQ_FOREACH_SAFE(nc, &n->childq, child, ncn) {
495: if (nc->node == NODE_PERSONNAME || have_name == 0) {
496: macro_addnode(f, nc, ARG_SPACE);
497: pnode_unlink(nc);
498: }
1.80 schwarze 499: }
500:
501: /*
502: * If there is an email address,
503: * print it on the same macro line.
504: */
505:
506: if ((nc = pnode_findfirst(n, NODE_EMAIL)) != NULL) {
507: pnode_print(f, nc);
508: pnode_unlink(nc);
1.78 schwarze 509: }
510:
511: /*
512: * If there are still unprinted children, end the scope
513: * with a comma. Otherwise, leave the scope open in case
514: * a text node follows that starts with closing punctuation.
515: */
516:
517: if (TAILQ_FIRST(&n->childq) != NULL) {
518: macro_addarg(f, ",", ARG_SPACE);
519: macro_close(f);
520: }
521: }
522:
523: static void
1.74 schwarze 524: pnode_printprologue(struct format *p, struct ptree *tree)
1.7 kristaps 525: {
1.74 schwarze 526: struct pnode *refmeta;
1.7 kristaps 527:
1.74 schwarze 528: refmeta = tree->root == NULL ? NULL :
529: pnode_findfirst(tree->root, NODE_REFMETA);
1.9 kristaps 530:
1.69 schwarze 531: macro_line(p, "Dd $Mdocdate" "$");
1.74 schwarze 532: if (refmeta == NULL) {
1.69 schwarze 533: macro_open(p, "Dt");
534: macro_addarg(p,
1.74 schwarze 535: pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"),
1.72 schwarze 536: ARG_SPACE | ARG_SINGLE | ARG_UPPER);
537: macro_addarg(p, "1", ARG_SPACE);
1.69 schwarze 538: macro_close(p);
1.74 schwarze 539: } else
540: pnode_printrefmeta(p, refmeta);
1.69 schwarze 541: macro_line(p, "Os");
1.43 kristaps 542:
1.74 schwarze 543: if (tree->flags & TREE_EQN) {
1.69 schwarze 544: macro_line(p, "EQ");
1.78 schwarze 545: print_text(p, "delim $$", 0);
1.69 schwarze 546: macro_line(p, "EN");
1.43 kristaps 547: }
1.7 kristaps 548: }
549:
1.42 kristaps 550: /*
551: * We can have multiple <term> elements within a <varlistentry>, which
552: * we should comma-separate as list headers.
553: */
1.13 kristaps 554: static void
1.74 schwarze 555: pnode_printvarlistentry(struct format *p, struct pnode *pn)
1.13 kristaps 556: {
557: struct pnode *pp;
1.42 kristaps 558: int first = 1;
1.13 kristaps 559:
1.95 ! schwarze 560: macro_close(p);
1.69 schwarze 561: macro_open(p, "It");
562: TAILQ_FOREACH(pp, &pn->childq, child) {
563: if (pp->node != NODE_TERM)
564: continue;
565: if ( ! first)
1.72 schwarze 566: macro_addarg(p, ",", 0);
1.69 schwarze 567: pnode_print(p, pp);
568: first = 0;
569: }
570: macro_close(p);
1.13 kristaps 571: TAILQ_FOREACH(pp, &pn->childq, child)
1.69 schwarze 572: if (pp->node != NODE_TERM)
1.13 kristaps 573: pnode_print(p, pp);
1.71 schwarze 574: pnode_unlinksub(pn);
575: }
576:
577: static void
1.74 schwarze 578: pnode_printtitle(struct format *p, struct pnode *pn)
1.71 schwarze 579: {
580: struct pnode *pp, *pq;
581:
582: TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq) {
583: if (pp->node == NODE_TITLE) {
584: pnode_printpara(p, pp);
585: pnode_print(p, pp);
586: pnode_unlink(pp);
587: }
588: }
1.13 kristaps 589: }
590:
591: static void
1.74 schwarze 592: pnode_printrow(struct format *p, struct pnode *pn)
1.25 kristaps 593: {
594: struct pnode *pp;
595:
1.69 schwarze 596: macro_line(p, "Bl -dash -compact");
1.25 kristaps 597: TAILQ_FOREACH(pp, &pn->childq, child) {
1.69 schwarze 598: macro_line(p, "It");
1.25 kristaps 599: pnode_print(p, pp);
600: }
1.69 schwarze 601: macro_line(p, "El");
1.71 schwarze 602: pnode_unlink(pn);
1.25 kristaps 603: }
604:
605: static void
1.83 schwarze 606: pnode_printtgroup1(struct format *f, struct pnode *n)
1.16 kristaps 607: {
1.82 schwarze 608: struct pnode *nc;
609:
1.83 schwarze 610: macro_line(f, "Bl -bullet -compact");
1.82 schwarze 611: while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
1.83 schwarze 612: macro_line(f, "It");
613: pnode_print(f, nc);
1.82 schwarze 614: pnode_unlink(nc);
615: }
1.83 schwarze 616: macro_line(f, "El");
617: pnode_unlinksub(n);
618: }
619:
620: static void
621: pnode_printtgroup2(struct format *f, struct pnode *n)
622: {
623: struct pnode *nr, *ne;
624:
625: macro_line(f, "Bl -tag -width Ds");
626: while ((nr = pnode_findfirst(n, NODE_ROW)) != NULL) {
627: if ((ne = pnode_findfirst(n, NODE_ENTRY)) == NULL)
628: break;
1.95 ! schwarze 629: macro_close(f);
1.83 schwarze 630: macro_open(f, "It");
631: pnode_print(f, ne);
1.84 schwarze 632: macro_close(f);
1.83 schwarze 633: pnode_unlink(ne);
634: pnode_print(f, nr);
635: pnode_unlink(nr);
636: }
637: macro_line(f, "El");
1.82 schwarze 638: pnode_unlinksub(n);
639: }
640:
641: static void
1.83 schwarze 642: pnode_printtgroup(struct format *f, struct pnode *n)
1.82 schwarze 643: {
644: struct pnode *nc;
645:
646: switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
647: case 1:
1.83 schwarze 648: pnode_printtgroup1(f, n);
649: return;
650: case 2:
651: pnode_printtgroup2(f, n);
1.82 schwarze 652: return;
653: default:
654: break;
655: }
1.16 kristaps 656:
1.83 schwarze 657: macro_line(f, "Bl -ohang");
1.82 schwarze 658: while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
1.83 schwarze 659: macro_line(f, "It Table Row");
660: pnode_printrow(f, nc);
1.25 kristaps 661: }
1.83 schwarze 662: macro_line(f, "El");
1.82 schwarze 663: pnode_unlinksub(n);
1.25 kristaps 664: }
665:
666: static void
1.74 schwarze 667: pnode_printlist(struct format *p, struct pnode *pn)
1.25 kristaps 668: {
669: struct pnode *pp;
1.16 kristaps 670:
1.71 schwarze 671: pnode_printtitle(p, pn);
1.69 schwarze 672: macro_argline(p, "Bl",
673: pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1.16 kristaps 674: TAILQ_FOREACH(pp, &pn->childq, child) {
1.69 schwarze 675: macro_line(p, "It");
1.16 kristaps 676: pnode_print(p, pp);
677: }
1.69 schwarze 678: macro_line(p, "El");
1.71 schwarze 679: pnode_unlinksub(pn);
1.16 kristaps 680: }
681:
682: static void
1.74 schwarze 683: pnode_printvariablelist(struct format *p, struct pnode *pn)
1.13 kristaps 684: {
685: struct pnode *pp;
686:
1.71 schwarze 687: pnode_printtitle(p, pn);
1.69 schwarze 688: macro_line(p, "Bl -tag -width Ds");
689: TAILQ_FOREACH(pp, &pn->childq, child) {
690: if (pp->node == NODE_VARLISTENTRY)
1.85 schwarze 691: pnode_printvarlistentry(p, pp);
1.69 schwarze 692: else
1.72 schwarze 693: macro_nodeline(p, "It", pp, 0);
1.69 schwarze 694: }
695: macro_line(p, "El");
1.71 schwarze 696: pnode_unlinksub(pn);
1.13 kristaps 697: }
698:
1.1 kristaps 699: /*
700: * Print a parsed node (or ignore it--whatever).
701: * This is a recursive function.
1.23 kristaps 702: * FIXME: if we're in a literal context (<screen> or <programlisting> or
703: * whatever), don't print inline macros.
1.1 kristaps 704: */
705: static void
1.74 schwarze 706: pnode_print(struct format *p, struct pnode *pn)
1.1 kristaps 707: {
708: struct pnode *pp;
1.59 schwarze 709: const char *ccp;
1.69 schwarze 710: enum linestate sv;
1.1 kristaps 711:
1.64 schwarze 712: if (pn == NULL)
1.1 kristaps 713: return;
714:
1.92 schwarze 715: p->spc = pn->spc;
1.69 schwarze 716: sv = p->linestate;
1.1 kristaps 717:
718: switch (pn->node) {
1.62 schwarze 719: case NODE_APPLICATION:
1.69 schwarze 720: macro_open(p, "Nm");
1.27 kristaps 721: break;
1.62 schwarze 722: case NODE_ARG:
1.10 kristaps 723: pnode_printarg(p, pn);
1.4 kristaps 724: break;
1.62 schwarze 725: case NODE_AUTHOR:
1.78 schwarze 726: pnode_printauthor(p, pn);
1.50 schwarze 727: break;
1.62 schwarze 728: case NODE_AUTHORGROUP:
1.69 schwarze 729: macro_line(p, "An -split");
1.50 schwarze 730: break;
1.62 schwarze 731: case NODE_BOOKINFO:
1.69 schwarze 732: macro_line(p, "Sh NAME");
1.50 schwarze 733: break;
1.62 schwarze 734: case NODE_CITEREFENTRY:
1.1 kristaps 735: pnode_printciterefentry(p, pn);
736: break;
1.68 schwarze 737: case NODE_CITETITLE:
1.69 schwarze 738: macro_open(p, "%T");
1.68 schwarze 739: break;
1.62 schwarze 740: case NODE_COMMAND:
1.69 schwarze 741: macro_open(p, "Nm");
1.13 kristaps 742: break;
1.62 schwarze 743: case NODE_CONSTANT:
1.69 schwarze 744: macro_open(p, "Dv");
1.33 kristaps 745: break;
1.62 schwarze 746: case NODE_EDITOR:
1.78 schwarze 747: print_text(p, "editor:", ARG_SPACE);
1.92 schwarze 748: sv = LINE_TEXT;
1.69 schwarze 749: macro_open(p, "An");
1.50 schwarze 750: break;
1.65 schwarze 751: case NODE_EMAIL:
1.69 schwarze 752: macro_open(p, "Aq Mt");
1.65 schwarze 753: break;
1.62 schwarze 754: case NODE_EMPHASIS:
755: case NODE_FIRSTTERM:
1.69 schwarze 756: macro_open(p, "Em");
1.1 kristaps 757: break;
1.62 schwarze 758: case NODE_ENVAR:
1.69 schwarze 759: macro_open(p, "Ev");
1.79 schwarze 760: break;
1.90 schwarze 761: case NODE_ERRORNAME:
762: macro_open(p, "Er");
763: break;
1.62 schwarze 764: case NODE_FILENAME:
1.69 schwarze 765: macro_open(p, "Pa");
1.17 kristaps 766: break;
1.62 schwarze 767: case NODE_FUNCTION:
1.69 schwarze 768: macro_open(p, "Fn");
1.3 kristaps 769: break;
1.62 schwarze 770: case NODE_FUNCPROTOTYPE:
1.3 kristaps 771: pnode_printfuncprototype(p, pn);
772: break;
1.62 schwarze 773: case NODE_FUNCSYNOPSISINFO:
1.69 schwarze 774: macro_open(p, "Fd");
1.16 kristaps 775: break;
1.62 schwarze 776: case NODE_INFORMALEQUATION:
1.69 schwarze 777: macro_line(p, "EQ");
1.43 kristaps 778: break;
1.62 schwarze 779: case NODE_INLINEEQUATION:
1.69 schwarze 780: if (p->linestate == LINE_NEW)
781: p->linestate = LINE_TEXT;
782: putchar('$');
1.43 kristaps 783: break;
1.62 schwarze 784: case NODE_ITEMIZEDLIST:
1.25 kristaps 785: pnode_printlist(p, pn);
1.24 kristaps 786: break;
1.62 schwarze 787: case NODE_GROUP:
1.24 kristaps 788: pnode_printgroup(p, pn);
1.10 kristaps 789: break;
1.67 schwarze 790: case NODE_KEYSYM:
1.69 schwarze 791: macro_open(p, "Sy");
1.67 schwarze 792: break;
1.62 schwarze 793: case NODE_LEGALNOTICE:
1.69 schwarze 794: macro_line(p, "Sh LEGAL NOTICE");
1.50 schwarze 795: break;
1.62 schwarze 796: case NODE_LINK:
1.59 schwarze 797: ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
1.64 schwarze 798: if (ccp == NULL)
1.58 schwarze 799: break;
1.69 schwarze 800: macro_argline(p, "Sx", ccp);
1.58 schwarze 801: return;
1.62 schwarze 802: case NODE_LITERAL:
1.90 schwarze 803: macro_open(p, "Ql");
1.19 kristaps 804: break;
1.62 schwarze 805: case NODE_LITERALLAYOUT:
1.95 ! schwarze 806: macro_close(p);
1.69 schwarze 807: macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
1.66 schwarze 808: ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60 schwarze 809: break;
1.62 schwarze 810: case NODE_MML_MFENCED:
1.41 kristaps 811: pnode_printmathfenced(p, pn);
1.40 kristaps 812: break;
1.62 schwarze 813: case NODE_MML_MROW:
814: case NODE_MML_MI:
815: case NODE_MML_MN:
816: case NODE_MML_MO:
1.43 kristaps 817: if (TAILQ_EMPTY(&pn->childq))
818: break;
819: fputs(" { ", stdout);
1.40 kristaps 820: break;
1.62 schwarze 821: case NODE_MML_MFRAC:
822: case NODE_MML_MSUB:
823: case NODE_MML_MSUP:
1.40 kristaps 824: pnode_printmath(p, pn);
825: break;
1.62 schwarze 826: case NODE_OPTION:
1.69 schwarze 827: macro_open(p, "Fl");
1.1 kristaps 828: break;
1.62 schwarze 829: case NODE_ORDEREDLIST:
1.25 kristaps 830: pnode_printlist(p, pn);
831: break;
1.62 schwarze 832: case NODE_PARA:
1.54 schwarze 833: pnode_printpara(p, pn);
1.3 kristaps 834: break;
1.87 schwarze 835: case NODE_PARAMDEF:
1.62 schwarze 836: case NODE_PARAMETER:
1.72 schwarze 837: macro_nodeline(p, "Fa", pn, ARG_SINGLE);
1.4 kristaps 838: pnode_unlinksub(pn);
1.1 kristaps 839: break;
1.62 schwarze 840: case NODE_QUOTE:
1.69 schwarze 841: macro_open(p, "Qo");
1.28 kristaps 842: break;
1.62 schwarze 843: case NODE_PROGRAMLISTING:
844: case NODE_SCREEN:
1.88 schwarze 845: case NODE_SYNOPSIS:
1.69 schwarze 846: macro_line(p, "Bd -literal");
1.15 kristaps 847: break;
1.62 schwarze 848: case NODE_REFENTRYINFO:
1.15 kristaps 849: /* Suppress. */
850: pnode_unlinksub(pn);
1.1 kristaps 851: break;
1.62 schwarze 852: case NODE_REFNAME:
1.10 kristaps 853: /* Suppress non-text children... */
1.70 schwarze 854: macro_open(p, "Nm");
1.72 schwarze 855: macro_addnode(p, pn, ARG_SPACE | ARG_SINGLE);
1.4 kristaps 856: pnode_unlinksub(pn);
1.10 kristaps 857: break;
1.62 schwarze 858: case NODE_REFNAMEDIV:
1.69 schwarze 859: macro_line(p, "Sh NAME");
1.1 kristaps 860: break;
1.62 schwarze 861: case NODE_REFPURPOSE:
1.69 schwarze 862: macro_open(p, "Nd");
1.10 kristaps 863: break;
1.62 schwarze 864: case NODE_REFSYNOPSISDIV:
1.6 kristaps 865: pnode_printrefsynopsisdiv(p, pn);
1.1 kristaps 866: break;
1.62 schwarze 867: case NODE_PREFACE:
868: case NODE_SECTION:
869: case NODE_NOTE:
870: case NODE_TIP:
871: case NODE_CAUTION:
872: case NODE_WARNING:
1.1 kristaps 873: pnode_printrefsect(p, pn);
874: break;
1.62 schwarze 875: case NODE_REPLACEABLE:
1.69 schwarze 876: macro_open(p, "Ar");
1.13 kristaps 877: break;
1.62 schwarze 878: case NODE_SBR:
1.69 schwarze 879: macro_line(p, "br");
1.19 kristaps 880: break;
1.62 schwarze 881: case NODE_SGMLTAG:
1.90 schwarze 882: macro_open(p, "Ic");
1.25 kristaps 883: break;
1.62 schwarze 884: case NODE_TEXT:
1.93 schwarze 885: case NODE_ESCAPE:
886: pnode_printtext(p, pn);
1.1 kristaps 887: break;
1.82 schwarze 888: case NODE_TGROUP:
889: pnode_printtgroup(p, pn);
890: break;
1.62 schwarze 891: case NODE_TITLE:
1.82 schwarze 892: if (pn->parent->node == NODE_BOOKINFO) {
1.69 schwarze 893: macro_open(p, "Nd");
1.82 schwarze 894: break;
895: }
896: pnode_printpara(p, pn);
897: macro_nodeline(p, "Sy", pn, 0);
898: pnode_unlinksub(pn);
1.50 schwarze 899: break;
1.62 schwarze 900: case NODE_TYPE:
1.69 schwarze 901: macro_open(p, "Vt");
1.39 kristaps 902: break;
1.62 schwarze 903: case NODE_VARIABLELIST:
1.13 kristaps 904: pnode_printvariablelist(p, pn);
905: break;
1.62 schwarze 906: case NODE_VARNAME:
1.69 schwarze 907: macro_open(p, "Va");
1.23 kristaps 908: break;
1.1 kristaps 909: default:
910: break;
911: }
912:
913: TAILQ_FOREACH(pp, &pn->childq, child)
914: pnode_print(p, pp);
915:
916: switch (pn->node) {
1.62 schwarze 917: case NODE_INFORMALEQUATION:
1.69 schwarze 918: macro_line(p, "EN");
1.40 kristaps 919: break;
1.62 schwarze 920: case NODE_INLINEEQUATION:
1.43 kristaps 921: fputs("$ ", stdout);
1.69 schwarze 922: p->linestate = sv;
1.43 kristaps 923: break;
1.91 schwarze 924: case NODE_MEMBER:
925: if ((pp = TAILQ_NEXT(pn, child)) != NULL &&
926: pp->node != NODE_MEMBER)
927: pp = NULL;
928: switch (p->linestate) {
929: case LINE_TEXT:
930: if (pp != NULL)
931: print_text(p, ",", 0);
932: break;
933: case LINE_MACRO:
934: if (pp != NULL)
935: macro_addarg(p, ",", ARG_SPACE);
936: macro_close(p);
937: break;
938: case LINE_NEW:
939: break;
940: }
941: break;
1.62 schwarze 942: case NODE_MML_MROW:
943: case NODE_MML_MI:
944: case NODE_MML_MN:
945: case NODE_MML_MO:
1.43 kristaps 946: if (TAILQ_EMPTY(&pn->childq))
947: break;
948: fputs(" } ", stdout);
1.40 kristaps 949: break;
1.62 schwarze 950: case NODE_APPLICATION:
951: case NODE_ARG:
952: case NODE_AUTHOR:
953: case NODE_CITEREFENTRY:
1.68 schwarze 954: case NODE_CITETITLE:
1.62 schwarze 955: case NODE_COMMAND:
956: case NODE_CONSTANT:
957: case NODE_EDITOR:
1.65 schwarze 958: case NODE_EMAIL:
1.62 schwarze 959: case NODE_EMPHASIS:
960: case NODE_ENVAR:
1.90 schwarze 961: case NODE_ERRORNAME:
1.62 schwarze 962: case NODE_FILENAME:
963: case NODE_FIRSTTERM:
964: case NODE_FUNCTION:
965: case NODE_FUNCSYNOPSISINFO:
1.67 schwarze 966: case NODE_KEYSYM:
1.62 schwarze 967: case NODE_LITERAL:
968: case NODE_OPTION:
969: case NODE_PARAMETER:
970: case NODE_REPLACEABLE:
971: case NODE_REFPURPOSE:
972: case NODE_SGMLTAG:
973: case NODE_TYPE:
974: case NODE_VARNAME:
1.91 schwarze 975: if (sv != LINE_MACRO && p->linestate == LINE_MACRO &&
976: (pn->parent == NULL || pn->parent->node != NODE_MEMBER))
1.69 schwarze 977: macro_closepunct(p, pn);
1.28 kristaps 978: break;
1.62 schwarze 979: case NODE_QUOTE:
1.69 schwarze 980: if (sv == LINE_NEW)
981: macro_close(p);
982: sv = p->linestate;
983: macro_open(p, "Qc");
984: if (sv == LINE_NEW)
985: macro_close(p);
1.10 kristaps 986: break;
1.62 schwarze 987: case NODE_REFNAME:
1.12 kristaps 988: /*
989: * If we're in the NAME macro and we have multiple
990: * <refname> macros in sequence, then print out a
991: * trailing comma before the newline.
992: */
1.64 schwarze 993: if (pn->parent != NULL &&
994: pn->parent->node == NODE_REFNAMEDIV &&
995: TAILQ_NEXT(pn, child) != NULL &&
996: TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
1.72 schwarze 997: macro_addarg(p, ",", ARG_SPACE);
1.69 schwarze 998: if (sv == LINE_NEW)
999: macro_close(p);
1.52 schwarze 1000: break;
1.62 schwarze 1001: case NODE_PREFACE:
1002: case NODE_SECTION:
1003: case NODE_NOTE:
1004: case NODE_TIP:
1005: case NODE_CAUTION:
1006: case NODE_WARNING:
1.52 schwarze 1007: p->level--;
1.12 kristaps 1008: break;
1.62 schwarze 1009: case NODE_LITERALLAYOUT:
1010: case NODE_PROGRAMLISTING:
1011: case NODE_SCREEN:
1.88 schwarze 1012: case NODE_SYNOPSIS:
1.69 schwarze 1013: macro_line(p, "Ed");
1.50 schwarze 1014: break;
1.62 schwarze 1015: case NODE_TITLE:
1.69 schwarze 1016: if (pn->parent->node == NODE_BOOKINFO)
1017: macro_line(p, "Sh AUTHORS");
1.1 kristaps 1018: break;
1019: default:
1020: break;
1021: }
1022: }
1023:
1.74 schwarze 1024: void
1025: ptree_print(struct ptree *tree)
1026: {
1027: struct format formatter;
1.1 kristaps 1028:
1.74 schwarze 1029: formatter.level = 0;
1030: formatter.linestate = LINE_NEW;
1031: pnode_printprologue(&formatter, tree);
1032: pnode_print(&formatter, tree->root);
1033: if (formatter.linestate != LINE_NEW)
1034: putchar('\n');
1.1 kristaps 1035: }
CVSweb