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