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