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