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