Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.96
1.96 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.95 2019/04/07 15:06:56 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.96 ! schwarze 524: pnode_printlink(struct format *f, struct pnode *n)
! 525: {
! 526: const char *uri, *text;
! 527:
! 528: uri = pnode_getattr_raw(n, ATTRKEY_LINKEND, NULL);
! 529: if (uri != NULL) {
! 530: if (TAILQ_FIRST(&n->childq) != NULL) {
! 531: print_textnode(f, n);
! 532: text = "";
! 533: } else {
! 534: text = pnode_getattr_raw(n, ATTRKEY_ENDTERM, NULL);
! 535: if (text != NULL)
! 536: print_text(f, text, ARG_SPACE);
! 537: }
! 538: if (text != NULL)
! 539: macro_open(f, "Pq");
! 540: macro_open(f, "Sx");
! 541: macro_addarg(f, uri, ARG_SPACE);
! 542: pnode_unlinksub(n);
! 543: return;
! 544: }
! 545: uri = pnode_getattr_raw(n, ATTRKEY_XLINK_HREF, NULL);
! 546: if (uri == NULL)
! 547: uri = pnode_getattr_raw(n, ATTRKEY_URL, NULL);
! 548: if (uri != NULL) {
! 549: macro_open(f, "Lk");
! 550: macro_addarg(f, uri, ARG_SPACE | ARG_SINGLE);
! 551: if (TAILQ_FIRST(&n->childq) != NULL)
! 552: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
! 553: pnode_unlinksub(n);
! 554: return;
! 555: }
! 556: }
! 557:
! 558: static void
1.74 schwarze 559: pnode_printprologue(struct format *p, struct ptree *tree)
1.7 kristaps 560: {
1.74 schwarze 561: struct pnode *refmeta;
1.7 kristaps 562:
1.74 schwarze 563: refmeta = tree->root == NULL ? NULL :
564: pnode_findfirst(tree->root, NODE_REFMETA);
1.9 kristaps 565:
1.69 schwarze 566: macro_line(p, "Dd $Mdocdate" "$");
1.74 schwarze 567: if (refmeta == NULL) {
1.69 schwarze 568: macro_open(p, "Dt");
569: macro_addarg(p,
1.74 schwarze 570: pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"),
1.72 schwarze 571: ARG_SPACE | ARG_SINGLE | ARG_UPPER);
572: macro_addarg(p, "1", ARG_SPACE);
1.69 schwarze 573: macro_close(p);
1.74 schwarze 574: } else
575: pnode_printrefmeta(p, refmeta);
1.69 schwarze 576: macro_line(p, "Os");
1.43 kristaps 577:
1.74 schwarze 578: if (tree->flags & TREE_EQN) {
1.69 schwarze 579: macro_line(p, "EQ");
1.78 schwarze 580: print_text(p, "delim $$", 0);
1.69 schwarze 581: macro_line(p, "EN");
1.43 kristaps 582: }
1.7 kristaps 583: }
584:
1.42 kristaps 585: /*
586: * We can have multiple <term> elements within a <varlistentry>, which
587: * we should comma-separate as list headers.
588: */
1.13 kristaps 589: static void
1.74 schwarze 590: pnode_printvarlistentry(struct format *p, struct pnode *pn)
1.13 kristaps 591: {
592: struct pnode *pp;
1.42 kristaps 593: int first = 1;
1.13 kristaps 594:
1.95 schwarze 595: macro_close(p);
1.69 schwarze 596: macro_open(p, "It");
597: TAILQ_FOREACH(pp, &pn->childq, child) {
598: if (pp->node != NODE_TERM)
599: continue;
600: if ( ! first)
1.72 schwarze 601: macro_addarg(p, ",", 0);
1.69 schwarze 602: pnode_print(p, pp);
603: first = 0;
604: }
605: macro_close(p);
1.13 kristaps 606: TAILQ_FOREACH(pp, &pn->childq, child)
1.69 schwarze 607: if (pp->node != NODE_TERM)
1.13 kristaps 608: pnode_print(p, pp);
1.71 schwarze 609: pnode_unlinksub(pn);
610: }
611:
612: static void
1.74 schwarze 613: pnode_printtitle(struct format *p, struct pnode *pn)
1.71 schwarze 614: {
615: struct pnode *pp, *pq;
616:
617: TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq) {
618: if (pp->node == NODE_TITLE) {
619: pnode_printpara(p, pp);
620: pnode_print(p, pp);
621: pnode_unlink(pp);
622: }
623: }
1.13 kristaps 624: }
625:
626: static void
1.74 schwarze 627: pnode_printrow(struct format *p, struct pnode *pn)
1.25 kristaps 628: {
629: struct pnode *pp;
630:
1.69 schwarze 631: macro_line(p, "Bl -dash -compact");
1.25 kristaps 632: TAILQ_FOREACH(pp, &pn->childq, child) {
1.69 schwarze 633: macro_line(p, "It");
1.25 kristaps 634: pnode_print(p, pp);
635: }
1.69 schwarze 636: macro_line(p, "El");
1.71 schwarze 637: pnode_unlink(pn);
1.25 kristaps 638: }
639:
640: static void
1.83 schwarze 641: pnode_printtgroup1(struct format *f, struct pnode *n)
1.16 kristaps 642: {
1.82 schwarze 643: struct pnode *nc;
644:
1.83 schwarze 645: macro_line(f, "Bl -bullet -compact");
1.82 schwarze 646: while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
1.83 schwarze 647: macro_line(f, "It");
648: pnode_print(f, nc);
1.82 schwarze 649: pnode_unlink(nc);
650: }
1.83 schwarze 651: macro_line(f, "El");
652: pnode_unlinksub(n);
653: }
654:
655: static void
656: pnode_printtgroup2(struct format *f, struct pnode *n)
657: {
658: struct pnode *nr, *ne;
659:
660: macro_line(f, "Bl -tag -width Ds");
661: while ((nr = pnode_findfirst(n, NODE_ROW)) != NULL) {
662: if ((ne = pnode_findfirst(n, NODE_ENTRY)) == NULL)
663: break;
1.95 schwarze 664: macro_close(f);
1.83 schwarze 665: macro_open(f, "It");
666: pnode_print(f, ne);
1.84 schwarze 667: macro_close(f);
1.83 schwarze 668: pnode_unlink(ne);
669: pnode_print(f, nr);
670: pnode_unlink(nr);
671: }
672: macro_line(f, "El");
1.82 schwarze 673: pnode_unlinksub(n);
674: }
675:
676: static void
1.83 schwarze 677: pnode_printtgroup(struct format *f, struct pnode *n)
1.82 schwarze 678: {
679: struct pnode *nc;
680:
681: switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
682: case 1:
1.83 schwarze 683: pnode_printtgroup1(f, n);
684: return;
685: case 2:
686: pnode_printtgroup2(f, n);
1.82 schwarze 687: return;
688: default:
689: break;
690: }
1.16 kristaps 691:
1.83 schwarze 692: macro_line(f, "Bl -ohang");
1.82 schwarze 693: while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
1.83 schwarze 694: macro_line(f, "It Table Row");
695: pnode_printrow(f, nc);
1.25 kristaps 696: }
1.83 schwarze 697: macro_line(f, "El");
1.82 schwarze 698: pnode_unlinksub(n);
1.25 kristaps 699: }
700:
701: static void
1.74 schwarze 702: pnode_printlist(struct format *p, struct pnode *pn)
1.25 kristaps 703: {
704: struct pnode *pp;
1.16 kristaps 705:
1.71 schwarze 706: pnode_printtitle(p, pn);
1.69 schwarze 707: macro_argline(p, "Bl",
708: pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1.16 kristaps 709: TAILQ_FOREACH(pp, &pn->childq, child) {
1.69 schwarze 710: macro_line(p, "It");
1.16 kristaps 711: pnode_print(p, pp);
712: }
1.69 schwarze 713: macro_line(p, "El");
1.71 schwarze 714: pnode_unlinksub(pn);
1.16 kristaps 715: }
716:
717: static void
1.74 schwarze 718: pnode_printvariablelist(struct format *p, struct pnode *pn)
1.13 kristaps 719: {
720: struct pnode *pp;
721:
1.71 schwarze 722: pnode_printtitle(p, pn);
1.69 schwarze 723: macro_line(p, "Bl -tag -width Ds");
724: TAILQ_FOREACH(pp, &pn->childq, child) {
725: if (pp->node == NODE_VARLISTENTRY)
1.85 schwarze 726: pnode_printvarlistentry(p, pp);
1.69 schwarze 727: else
1.72 schwarze 728: macro_nodeline(p, "It", pp, 0);
1.69 schwarze 729: }
730: macro_line(p, "El");
1.71 schwarze 731: pnode_unlinksub(pn);
1.13 kristaps 732: }
733:
1.1 kristaps 734: /*
735: * Print a parsed node (or ignore it--whatever).
736: * This is a recursive function.
1.23 kristaps 737: * FIXME: if we're in a literal context (<screen> or <programlisting> or
738: * whatever), don't print inline macros.
1.1 kristaps 739: */
740: static void
1.74 schwarze 741: pnode_print(struct format *p, struct pnode *pn)
1.1 kristaps 742: {
743: struct pnode *pp;
1.69 schwarze 744: enum linestate sv;
1.1 kristaps 745:
1.64 schwarze 746: if (pn == NULL)
1.1 kristaps 747: return;
748:
1.92 schwarze 749: p->spc = pn->spc;
1.69 schwarze 750: sv = p->linestate;
1.1 kristaps 751:
752: switch (pn->node) {
1.62 schwarze 753: case NODE_APPLICATION:
1.69 schwarze 754: macro_open(p, "Nm");
1.27 kristaps 755: break;
1.62 schwarze 756: case NODE_ARG:
1.10 kristaps 757: pnode_printarg(p, pn);
1.4 kristaps 758: break;
1.62 schwarze 759: case NODE_AUTHOR:
1.78 schwarze 760: pnode_printauthor(p, pn);
1.50 schwarze 761: break;
1.62 schwarze 762: case NODE_AUTHORGROUP:
1.69 schwarze 763: macro_line(p, "An -split");
1.50 schwarze 764: break;
1.62 schwarze 765: case NODE_BOOKINFO:
1.69 schwarze 766: macro_line(p, "Sh NAME");
1.50 schwarze 767: break;
1.62 schwarze 768: case NODE_CITEREFENTRY:
1.1 kristaps 769: pnode_printciterefentry(p, pn);
770: break;
1.68 schwarze 771: case NODE_CITETITLE:
1.69 schwarze 772: macro_open(p, "%T");
1.68 schwarze 773: break;
1.62 schwarze 774: case NODE_COMMAND:
1.69 schwarze 775: macro_open(p, "Nm");
1.13 kristaps 776: break;
1.62 schwarze 777: case NODE_CONSTANT:
1.69 schwarze 778: macro_open(p, "Dv");
1.33 kristaps 779: break;
1.62 schwarze 780: case NODE_EDITOR:
1.78 schwarze 781: print_text(p, "editor:", ARG_SPACE);
1.92 schwarze 782: sv = LINE_TEXT;
1.69 schwarze 783: macro_open(p, "An");
1.50 schwarze 784: break;
1.65 schwarze 785: case NODE_EMAIL:
1.69 schwarze 786: macro_open(p, "Aq Mt");
1.65 schwarze 787: break;
1.62 schwarze 788: case NODE_EMPHASIS:
789: case NODE_FIRSTTERM:
1.69 schwarze 790: macro_open(p, "Em");
1.1 kristaps 791: break;
1.62 schwarze 792: case NODE_ENVAR:
1.69 schwarze 793: macro_open(p, "Ev");
1.79 schwarze 794: break;
1.90 schwarze 795: case NODE_ERRORNAME:
796: macro_open(p, "Er");
797: break;
1.62 schwarze 798: case NODE_FILENAME:
1.69 schwarze 799: macro_open(p, "Pa");
1.17 kristaps 800: break;
1.62 schwarze 801: case NODE_FUNCTION:
1.69 schwarze 802: macro_open(p, "Fn");
1.3 kristaps 803: break;
1.62 schwarze 804: case NODE_FUNCPROTOTYPE:
1.3 kristaps 805: pnode_printfuncprototype(p, pn);
806: break;
1.62 schwarze 807: case NODE_FUNCSYNOPSISINFO:
1.69 schwarze 808: macro_open(p, "Fd");
1.16 kristaps 809: break;
1.62 schwarze 810: case NODE_INFORMALEQUATION:
1.69 schwarze 811: macro_line(p, "EQ");
1.43 kristaps 812: break;
1.62 schwarze 813: case NODE_INLINEEQUATION:
1.69 schwarze 814: if (p->linestate == LINE_NEW)
815: p->linestate = LINE_TEXT;
816: putchar('$');
1.43 kristaps 817: break;
1.62 schwarze 818: case NODE_ITEMIZEDLIST:
1.25 kristaps 819: pnode_printlist(p, pn);
1.24 kristaps 820: break;
1.62 schwarze 821: case NODE_GROUP:
1.24 kristaps 822: pnode_printgroup(p, pn);
1.10 kristaps 823: break;
1.67 schwarze 824: case NODE_KEYSYM:
1.69 schwarze 825: macro_open(p, "Sy");
1.67 schwarze 826: break;
1.62 schwarze 827: case NODE_LEGALNOTICE:
1.69 schwarze 828: macro_line(p, "Sh LEGAL NOTICE");
1.50 schwarze 829: break;
1.62 schwarze 830: case NODE_LINK:
1.96 ! schwarze 831: pnode_printlink(p, pn);
! 832: break;
1.62 schwarze 833: case NODE_LITERAL:
1.90 schwarze 834: macro_open(p, "Ql");
1.19 kristaps 835: break;
1.62 schwarze 836: case NODE_LITERALLAYOUT:
1.95 schwarze 837: macro_close(p);
1.69 schwarze 838: macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
1.66 schwarze 839: ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60 schwarze 840: break;
1.62 schwarze 841: case NODE_MML_MFENCED:
1.41 kristaps 842: pnode_printmathfenced(p, pn);
1.40 kristaps 843: break;
1.62 schwarze 844: case NODE_MML_MROW:
845: case NODE_MML_MI:
846: case NODE_MML_MN:
847: case NODE_MML_MO:
1.43 kristaps 848: if (TAILQ_EMPTY(&pn->childq))
849: break;
850: fputs(" { ", stdout);
1.40 kristaps 851: break;
1.62 schwarze 852: case NODE_MML_MFRAC:
853: case NODE_MML_MSUB:
854: case NODE_MML_MSUP:
1.40 kristaps 855: pnode_printmath(p, pn);
856: break;
1.62 schwarze 857: case NODE_OPTION:
1.69 schwarze 858: macro_open(p, "Fl");
1.1 kristaps 859: break;
1.62 schwarze 860: case NODE_ORDEREDLIST:
1.25 kristaps 861: pnode_printlist(p, pn);
862: break;
1.62 schwarze 863: case NODE_PARA:
1.54 schwarze 864: pnode_printpara(p, pn);
1.3 kristaps 865: break;
1.87 schwarze 866: case NODE_PARAMDEF:
1.62 schwarze 867: case NODE_PARAMETER:
1.72 schwarze 868: macro_nodeline(p, "Fa", pn, ARG_SINGLE);
1.4 kristaps 869: pnode_unlinksub(pn);
1.1 kristaps 870: break;
1.62 schwarze 871: case NODE_QUOTE:
1.69 schwarze 872: macro_open(p, "Qo");
1.28 kristaps 873: break;
1.62 schwarze 874: case NODE_PROGRAMLISTING:
875: case NODE_SCREEN:
1.88 schwarze 876: case NODE_SYNOPSIS:
1.69 schwarze 877: macro_line(p, "Bd -literal");
1.15 kristaps 878: break;
1.62 schwarze 879: case NODE_REFENTRYINFO:
1.15 kristaps 880: /* Suppress. */
881: pnode_unlinksub(pn);
1.1 kristaps 882: break;
1.62 schwarze 883: case NODE_REFNAME:
1.10 kristaps 884: /* Suppress non-text children... */
1.70 schwarze 885: macro_open(p, "Nm");
1.72 schwarze 886: macro_addnode(p, pn, ARG_SPACE | ARG_SINGLE);
1.4 kristaps 887: pnode_unlinksub(pn);
1.10 kristaps 888: break;
1.62 schwarze 889: case NODE_REFNAMEDIV:
1.69 schwarze 890: macro_line(p, "Sh NAME");
1.1 kristaps 891: break;
1.62 schwarze 892: case NODE_REFPURPOSE:
1.69 schwarze 893: macro_open(p, "Nd");
1.10 kristaps 894: break;
1.62 schwarze 895: case NODE_REFSYNOPSISDIV:
1.6 kristaps 896: pnode_printrefsynopsisdiv(p, pn);
1.1 kristaps 897: break;
1.62 schwarze 898: case NODE_PREFACE:
899: case NODE_SECTION:
900: case NODE_NOTE:
901: case NODE_TIP:
902: case NODE_CAUTION:
903: case NODE_WARNING:
1.1 kristaps 904: pnode_printrefsect(p, pn);
905: break;
1.62 schwarze 906: case NODE_REPLACEABLE:
1.69 schwarze 907: macro_open(p, "Ar");
1.13 kristaps 908: break;
1.62 schwarze 909: case NODE_SBR:
1.69 schwarze 910: macro_line(p, "br");
1.19 kristaps 911: break;
1.62 schwarze 912: case NODE_SGMLTAG:
1.90 schwarze 913: macro_open(p, "Ic");
1.25 kristaps 914: break;
1.62 schwarze 915: case NODE_TEXT:
1.93 schwarze 916: case NODE_ESCAPE:
917: pnode_printtext(p, pn);
1.1 kristaps 918: break;
1.82 schwarze 919: case NODE_TGROUP:
920: pnode_printtgroup(p, pn);
921: break;
1.62 schwarze 922: case NODE_TITLE:
1.82 schwarze 923: if (pn->parent->node == NODE_BOOKINFO) {
1.69 schwarze 924: macro_open(p, "Nd");
1.82 schwarze 925: break;
926: }
927: pnode_printpara(p, pn);
928: macro_nodeline(p, "Sy", pn, 0);
929: pnode_unlinksub(pn);
1.50 schwarze 930: break;
1.62 schwarze 931: case NODE_TYPE:
1.69 schwarze 932: macro_open(p, "Vt");
1.39 kristaps 933: break;
1.62 schwarze 934: case NODE_VARIABLELIST:
1.13 kristaps 935: pnode_printvariablelist(p, pn);
936: break;
1.62 schwarze 937: case NODE_VARNAME:
1.69 schwarze 938: macro_open(p, "Va");
1.23 kristaps 939: break;
1.1 kristaps 940: default:
941: break;
942: }
943:
944: TAILQ_FOREACH(pp, &pn->childq, child)
945: pnode_print(p, pp);
946:
947: switch (pn->node) {
1.62 schwarze 948: case NODE_INFORMALEQUATION:
1.69 schwarze 949: macro_line(p, "EN");
1.40 kristaps 950: break;
1.62 schwarze 951: case NODE_INLINEEQUATION:
1.43 kristaps 952: fputs("$ ", stdout);
1.69 schwarze 953: p->linestate = sv;
1.43 kristaps 954: break;
1.91 schwarze 955: case NODE_MEMBER:
956: if ((pp = TAILQ_NEXT(pn, child)) != NULL &&
957: pp->node != NODE_MEMBER)
958: pp = NULL;
959: switch (p->linestate) {
960: case LINE_TEXT:
961: if (pp != NULL)
962: print_text(p, ",", 0);
963: break;
964: case LINE_MACRO:
965: if (pp != NULL)
966: macro_addarg(p, ",", ARG_SPACE);
967: macro_close(p);
968: break;
969: case LINE_NEW:
970: break;
971: }
972: break;
1.62 schwarze 973: case NODE_MML_MROW:
974: case NODE_MML_MI:
975: case NODE_MML_MN:
976: case NODE_MML_MO:
1.43 kristaps 977: if (TAILQ_EMPTY(&pn->childq))
978: break;
979: fputs(" } ", stdout);
1.40 kristaps 980: break;
1.62 schwarze 981: case NODE_APPLICATION:
982: case NODE_ARG:
983: case NODE_AUTHOR:
984: case NODE_CITEREFENTRY:
1.68 schwarze 985: case NODE_CITETITLE:
1.62 schwarze 986: case NODE_COMMAND:
987: case NODE_CONSTANT:
988: case NODE_EDITOR:
1.65 schwarze 989: case NODE_EMAIL:
1.62 schwarze 990: case NODE_EMPHASIS:
991: case NODE_ENVAR:
1.90 schwarze 992: case NODE_ERRORNAME:
1.62 schwarze 993: case NODE_FILENAME:
994: case NODE_FIRSTTERM:
995: case NODE_FUNCTION:
996: case NODE_FUNCSYNOPSISINFO:
1.67 schwarze 997: case NODE_KEYSYM:
1.96 ! schwarze 998: case NODE_LINK:
1.62 schwarze 999: case NODE_LITERAL:
1000: case NODE_OPTION:
1001: case NODE_PARAMETER:
1002: case NODE_REPLACEABLE:
1003: case NODE_REFPURPOSE:
1004: case NODE_SGMLTAG:
1005: case NODE_TYPE:
1006: case NODE_VARNAME:
1.91 schwarze 1007: if (sv != LINE_MACRO && p->linestate == LINE_MACRO &&
1008: (pn->parent == NULL || pn->parent->node != NODE_MEMBER))
1.69 schwarze 1009: macro_closepunct(p, pn);
1.28 kristaps 1010: break;
1.62 schwarze 1011: case NODE_QUOTE:
1.69 schwarze 1012: if (sv == LINE_NEW)
1013: macro_close(p);
1014: sv = p->linestate;
1015: macro_open(p, "Qc");
1016: if (sv == LINE_NEW)
1017: macro_close(p);
1.10 kristaps 1018: break;
1.62 schwarze 1019: case NODE_REFNAME:
1.12 kristaps 1020: /*
1021: * If we're in the NAME macro and we have multiple
1022: * <refname> macros in sequence, then print out a
1023: * trailing comma before the newline.
1024: */
1.64 schwarze 1025: if (pn->parent != NULL &&
1026: pn->parent->node == NODE_REFNAMEDIV &&
1027: TAILQ_NEXT(pn, child) != NULL &&
1028: TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
1.72 schwarze 1029: macro_addarg(p, ",", ARG_SPACE);
1.69 schwarze 1030: if (sv == LINE_NEW)
1031: macro_close(p);
1.52 schwarze 1032: break;
1.62 schwarze 1033: case NODE_PREFACE:
1034: case NODE_SECTION:
1035: case NODE_NOTE:
1036: case NODE_TIP:
1037: case NODE_CAUTION:
1038: case NODE_WARNING:
1.52 schwarze 1039: p->level--;
1.12 kristaps 1040: break;
1.62 schwarze 1041: case NODE_LITERALLAYOUT:
1042: case NODE_PROGRAMLISTING:
1043: case NODE_SCREEN:
1.88 schwarze 1044: case NODE_SYNOPSIS:
1.69 schwarze 1045: macro_line(p, "Ed");
1.50 schwarze 1046: break;
1.62 schwarze 1047: case NODE_TITLE:
1.69 schwarze 1048: if (pn->parent->node == NODE_BOOKINFO)
1049: macro_line(p, "Sh AUTHORS");
1.1 kristaps 1050: break;
1051: default:
1052: break;
1053: }
1054: }
1055:
1.74 schwarze 1056: void
1057: ptree_print(struct ptree *tree)
1058: {
1059: struct format formatter;
1.1 kristaps 1060:
1.74 schwarze 1061: formatter.level = 0;
1062: formatter.linestate = LINE_NEW;
1063: pnode_printprologue(&formatter, tree);
1064: pnode_print(&formatter, tree->root);
1065: if (formatter.linestate != LINE_NEW)
1066: putchar('\n');
1.1 kristaps 1067: }
CVSweb