Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.101
1.101 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.100 2019/04/07 19:33:27 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.101 ! schwarze 102: pnode_printpara(struct format *f, struct pnode *n)
1.54 schwarze 103: {
1.101 ! schwarze 104: struct pnode *np;
1.54 schwarze 105:
1.101 ! schwarze 106: if (n->parent == NULL)
1.54 schwarze 107: return;
108:
1.101 ! schwarze 109: if ((np = TAILQ_PREV(n, pnodeq, child)) == NULL)
! 110: np = n->parent;
1.97 schwarze 111:
1.101 ! schwarze 112: switch (np->node) {
1.61 schwarze 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:
1.101 ! schwarze 122: if (f->level < 3)
1.61 schwarze 123: return;
124: break;
125: default:
126: break;
127: }
1.101 ! schwarze 128: macro_line(f, "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.101 ! schwarze 135: pnode_printrefsynopsisdiv(struct format *f, struct pnode *n)
1.6 kristaps 136: {
1.101 ! schwarze 137: struct pnode *nc, *nn;
1.6 kristaps 138:
1.101 ! schwarze 139: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn)
! 140: if (nc->node == NODE_TITLE)
! 141: pnode_unlink(nc);
1.71 schwarze 142:
1.101 ! schwarze 143: macro_line(f, "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.101 ! schwarze 150: pnode_printrefsect(struct format *f, struct pnode *n)
1.1 kristaps 151: {
1.101 ! schwarze 152: struct pnode *nc;
1.52 schwarze 153: const char *title;
154: int flags, level;
155:
1.101 ! schwarze 156: if (n->parent == NULL)
1.56 schwarze 157: return;
158:
1.101 ! schwarze 159: level = ++f->level;
1.72 schwarze 160: flags = ARG_SPACE;
161: if (level == 1)
162: flags |= ARG_UPPER;
1.64 schwarze 163: if (level < 3) {
1.101 ! schwarze 164: switch (n->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:
1.101 ! schwarze 176: TAILQ_FOREACH(nc, &n->childq, child)
! 177: if (nc->node == NODE_TITLE)
1.1 kristaps 178: break;
179:
1.101 ! schwarze 180: if (nc == NULL) {
! 181: switch (n->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.101 ! schwarze 211: macro_close(f);
! 212: macro_open(f, "Sh");
1.29 kristaps 213: break;
1.62 schwarze 214: case 2:
1.101 ! schwarze 215: macro_close(f);
! 216: macro_open(f, "Ss");
1.29 kristaps 217: break;
1.52 schwarze 218: default:
1.101 ! schwarze 219: pnode_printpara(f, n);
! 220: macro_open(f, "Sy");
1.29 kristaps 221: break;
222: }
1.20 kristaps 223:
1.101 ! schwarze 224: if (nc != NULL) {
! 225: macro_addnode(f, nc, flags);
! 226: pnode_unlink(nc);
1.52 schwarze 227: } else
1.101 ! schwarze 228: macro_addarg(f, title, flags | ARG_QUOTED);
! 229: macro_close(f);
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.101 ! schwarze 236: pnode_printciterefentry(struct format *f, struct pnode *n)
1.1 kristaps 237: {
1.101 ! schwarze 238: struct pnode *nc, *title, *manvol;
1.1 kristaps 239:
240: title = manvol = NULL;
1.101 ! schwarze 241: TAILQ_FOREACH(nc, &n->childq, child) {
! 242: if (nc->node == NODE_MANVOLNUM)
! 243: manvol = nc;
! 244: else if (nc->node == NODE_REFENTRYTITLE)
! 245: title = nc;
1.69 schwarze 246: }
1.101 ! schwarze 247: macro_open(f, "Xr");
1.69 schwarze 248: if (title == NULL)
1.101 ! schwarze 249: macro_addarg(f, "unknown", ARG_SPACE);
1.69 schwarze 250: else
1.101 ! schwarze 251: macro_addnode(f, title, ARG_SPACE | ARG_SINGLE);
1.69 schwarze 252: if (manvol == NULL)
1.101 ! schwarze 253: macro_addarg(f, "1", ARG_SPACE);
1.64 schwarze 254: else
1.101 ! schwarze 255: macro_addnode(f, manvol, ARG_SPACE | ARG_SINGLE);
! 256: pnode_unlinksub(n);
1.1 kristaps 257: }
258:
259: static void
1.101 ! schwarze 260: pnode_printrefmeta(struct format *f, struct pnode *n)
1.1 kristaps 261: {
1.101 ! schwarze 262: struct pnode *nc, *title, *manvol;
1.1 kristaps 263:
264: title = manvol = NULL;
1.101 ! schwarze 265: TAILQ_FOREACH(nc, &n->childq, child) {
! 266: if (nc->node == NODE_MANVOLNUM)
! 267: manvol = nc;
! 268: else if (nc->node == NODE_REFENTRYTITLE)
! 269: title = nc;
1.69 schwarze 270: }
1.101 ! schwarze 271: macro_close(f);
! 272: macro_open(f, "Dt");
1.69 schwarze 273: if (title == NULL)
1.101 ! schwarze 274: macro_addarg(f, "UNKNOWN", ARG_SPACE);
1.69 schwarze 275: else
1.101 ! schwarze 276: macro_addnode(f, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
1.69 schwarze 277: if (manvol == NULL)
1.101 ! schwarze 278: macro_addarg(f, "1", ARG_SPACE);
1.13 kristaps 279: else
1.101 ! schwarze 280: macro_addnode(f, manvol, ARG_SPACE | ARG_SINGLE);
! 281: macro_close(f);
! 282: pnode_unlink(n);
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.101 ! schwarze 306: pnode_printmathfenced(struct format *f, struct pnode *n)
1.41 kristaps 307: {
1.101 ! schwarze 308: struct pnode *nc;
1.41 kristaps 309:
1.101 ! schwarze 310: printf("left %s ", pnode_getattr_raw(n, ATTRKEY_OPEN, "("));
1.41 kristaps 311:
1.101 ! schwarze 312: nc = TAILQ_FIRST(&n->childq);
! 313: pnode_print(f, nc);
1.41 kristaps 314:
1.101 ! schwarze 315: while ((nc = TAILQ_NEXT(nc, child)) != NULL) {
1.41 kristaps 316: putchar(',');
1.101 ! schwarze 317: pnode_print(f, nc);
1.41 kristaps 318: }
1.101 ! schwarze 319: printf("right %s ", pnode_getattr_raw(n, ATTRKEY_CLOSE, ")"));
! 320: pnode_unlinksub(n);
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.101 ! schwarze 330: pnode_printmath(struct format *f, struct pnode *n)
1.40 kristaps 331: {
1.101 ! schwarze 332: struct pnode *nc;
1.40 kristaps 333:
1.101 ! schwarze 334: nc = TAILQ_FIRST(&n->childq);
! 335: pnode_print(f, nc);
1.40 kristaps 336:
1.101 ! schwarze 337: switch (n->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:
1.101 ! schwarze 351: nc = TAILQ_NEXT(nc, child);
! 352: pnode_print(f, nc);
! 353: pnode_unlinksub(n);
1.40 kristaps 354: }
355:
1.3 kristaps 356: static void
1.101 ! schwarze 357: pnode_printfuncprototype(struct format *f, struct pnode *n)
1.3 kristaps 358: {
1.101 ! schwarze 359: struct pnode *nc, *fdef;
1.3 kristaps 360:
1.101 ! schwarze 361: TAILQ_FOREACH(fdef, &n->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.101 ! schwarze 366: pnode_printfuncdef(f, fdef);
1.86 schwarze 367: pnode_unlink(fdef);
368: } else
1.101 ! schwarze 369: macro_line(f, "Fo UNKNOWN");
1.3 kristaps 370:
1.101 ! schwarze 371: TAILQ_FOREACH(nc, &n->childq, child)
! 372: macro_nodeline(f, "Fa", nc, ARG_SINGLE);
1.3 kristaps 373:
1.101 ! schwarze 374: macro_line(f, "Fc");
! 375: pnode_unlinksub(n);
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.101 ! schwarze 386: pnode_printarg(struct format *f, struct pnode *n)
1.4 kristaps 387: {
1.101 ! schwarze 388: struct pnode *nc;
! 389: struct pattr *a;
1.12 kristaps 390: int isop, isrep;
391:
392: isop = 1;
393: isrep = 0;
1.101 ! schwarze 394: TAILQ_FOREACH(a, &n->attrq, child) {
! 395: if (a->key == ATTRKEY_CHOICE &&
! 396: (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ))
1.12 kristaps 397: isop = 0;
1.101 ! schwarze 398: else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT)
1.12 kristaps 399: isrep = 1;
400: }
1.69 schwarze 401: if (isop)
1.101 ! schwarze 402: macro_open(f, "Op");
1.4 kristaps 403:
1.101 ! schwarze 404: TAILQ_FOREACH(nc, &n->childq, child) {
! 405: if (nc->node == NODE_TEXT)
! 406: macro_open(f, "Ar");
! 407: pnode_print(f, nc);
! 408: if (isrep && nc->node == NODE_TEXT)
! 409: macro_addarg(f, "...", ARG_SPACE);
1.10 kristaps 410: }
1.101 ! schwarze 411: pnode_unlinksub(n);
1.4 kristaps 412: }
413:
1.24 kristaps 414: static void
1.101 ! schwarze 415: pnode_printgroup(struct format *f, struct pnode *n)
1.24 kristaps 416: {
1.101 ! schwarze 417: struct pnode *nc, *nn;
! 418: struct pattr *a;
1.24 kristaps 419: int isop, sv;
420:
421: isop = 1;
1.101 ! schwarze 422: TAILQ_FOREACH(a, &n->attrq, child)
! 423: if (a->key == ATTRKEY_CHOICE &&
! 424: (a->val == ATTRVAL_PLAIN || a->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.101 ! schwarze 434: sv = f->linestate == LINE_NEW;
1.44 schwarze 435: if (isop)
1.101 ! schwarze 436: macro_open(f, "Op");
1.24 kristaps 437: else if (sv)
1.101 ! schwarze 438: macro_open(f, "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: */
1.101 ! schwarze 448: TAILQ_FOREACH(nc, &n->childq, child) {
! 449: pnode_print(f, nc);
! 450: nn = TAILQ_NEXT(nc, child);
! 451: while (nn != NULL) {
! 452: if (nc->node != nn->node)
1.24 kristaps 453: break;
1.101 ! schwarze 454: macro_addarg(f, "|", ARG_SPACE);
! 455: macro_addnode(f, nn, ARG_SPACE);
! 456: nc = nn;
! 457: nn = TAILQ_NEXT(nn, child);
1.24 kristaps 458: }
459: }
1.69 schwarze 460: if (sv)
1.101 ! schwarze 461: macro_close(f);
! 462: pnode_unlinksub(n);
1.24 kristaps 463: }
464:
1.7 kristaps 465: static void
1.78 schwarze 466: pnode_printauthor(struct format *f, struct pnode *n)
467: {
1.101 ! schwarze 468: struct pnode *nc, *nn;
1.78 schwarze 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;
1.101 ! schwarze 477: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78 schwarze 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");
1.101 ! schwarze 506: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78 schwarze 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.101 ! schwarze 571: pnode_printprologue(struct format *f, 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.101 ! schwarze 578: macro_line(f, "Dd $Mdocdate" "$");
1.74 schwarze 579: if (refmeta == NULL) {
1.101 ! schwarze 580: macro_open(f, "Dt");
! 581: macro_addarg(f,
1.74 schwarze 582: pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"),
1.72 schwarze 583: ARG_SPACE | ARG_SINGLE | ARG_UPPER);
1.101 ! schwarze 584: macro_addarg(f, "1", ARG_SPACE);
! 585: macro_close(f);
1.74 schwarze 586: } else
1.101 ! schwarze 587: pnode_printrefmeta(f, refmeta);
! 588: macro_line(f, "Os");
1.43 kristaps 589:
1.74 schwarze 590: if (tree->flags & TREE_EQN) {
1.101 ! schwarze 591: macro_line(f, "EQ");
! 592: print_text(f, "delim $$", 0);
! 593: macro_line(f, "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.101 ! schwarze 602: pnode_printvarlistentry(struct format *f, struct pnode *n)
1.13 kristaps 603: {
1.101 ! schwarze 604: struct pnode *nc;
1.42 kristaps 605: int first = 1;
1.13 kristaps 606:
1.101 ! schwarze 607: macro_close(f);
! 608: macro_open(f, "It");
! 609: TAILQ_FOREACH(nc, &n->childq, child) {
! 610: if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
1.69 schwarze 611: continue;
612: if ( ! first)
1.101 ! schwarze 613: macro_addarg(f, ",", 0);
! 614: pnode_print(f, nc);
1.69 schwarze 615: first = 0;
616: }
1.101 ! schwarze 617: macro_close(f);
! 618: TAILQ_FOREACH(nc, &n->childq, child)
! 619: if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
! 620: pnode_print(f, nc);
! 621: pnode_unlinksub(n);
1.71 schwarze 622: }
623:
624: static void
1.101 ! schwarze 625: pnode_printtitle(struct format *f, struct pnode *n)
1.71 schwarze 626: {
1.101 ! schwarze 627: struct pnode *nc, *nn;
1.71 schwarze 628:
1.101 ! schwarze 629: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
! 630: if (nc->node == NODE_TITLE) {
! 631: pnode_printpara(f, nc);
! 632: pnode_print(f, nc);
! 633: pnode_unlink(nc);
1.71 schwarze 634: }
635: }
1.13 kristaps 636: }
637:
638: static void
1.101 ! schwarze 639: pnode_printrow(struct format *f, struct pnode *n)
1.25 kristaps 640: {
1.101 ! schwarze 641: struct pnode *nc;
1.25 kristaps 642:
1.101 ! schwarze 643: macro_line(f, "Bl -dash -compact");
! 644: TAILQ_FOREACH(nc, &n->childq, child) {
! 645: macro_line(f, "It");
! 646: pnode_print(f, nc);
1.25 kristaps 647: }
1.101 ! schwarze 648: macro_line(f, "El");
! 649: pnode_unlink(n);
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.101 ! schwarze 714: pnode_printlist(struct format *f, struct pnode *n)
1.25 kristaps 715: {
1.101 ! schwarze 716: struct pnode *nc;
1.16 kristaps 717:
1.101 ! schwarze 718: pnode_printtitle(f, n);
! 719: macro_argline(f, "Bl",
! 720: n->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
! 721: TAILQ_FOREACH(nc, &n->childq, child) {
! 722: macro_line(f, "It");
! 723: pnode_print(f, nc);
1.16 kristaps 724: }
1.101 ! schwarze 725: macro_line(f, "El");
! 726: pnode_unlinksub(n);
1.16 kristaps 727: }
728:
729: static void
1.101 ! schwarze 730: pnode_printvariablelist(struct format *f, struct pnode *n)
1.13 kristaps 731: {
1.101 ! schwarze 732: struct pnode *nc;
1.13 kristaps 733:
1.101 ! schwarze 734: pnode_printtitle(f, n);
! 735: macro_line(f, "Bl -tag -width Ds");
! 736: TAILQ_FOREACH(nc, &n->childq, child) {
! 737: if (nc->node == NODE_VARLISTENTRY)
! 738: pnode_printvarlistentry(f, nc);
1.69 schwarze 739: else
1.101 ! schwarze 740: macro_nodeline(f, "It", nc, 0);
1.69 schwarze 741: }
1.101 ! schwarze 742: macro_line(f, "El");
! 743: pnode_unlinksub(n);
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.101 ! schwarze 753: pnode_print(struct format *f, struct pnode *n)
1.1 kristaps 754: {
755: struct pnode *pp;
1.69 schwarze 756: enum linestate sv;
1.1 kristaps 757:
1.101 ! schwarze 758: if (n == NULL)
1.1 kristaps 759: return;
760:
1.101 ! schwarze 761: f->spc = n->spc;
! 762: sv = f->linestate;
1.1 kristaps 763:
1.101 ! schwarze 764: switch (n->node) {
1.62 schwarze 765: case NODE_APPLICATION:
1.101 ! schwarze 766: macro_open(f, "Nm");
1.27 kristaps 767: break;
1.62 schwarze 768: case NODE_ARG:
1.101 ! schwarze 769: pnode_printarg(f, n);
1.4 kristaps 770: break;
1.62 schwarze 771: case NODE_AUTHOR:
1.101 ! schwarze 772: pnode_printauthor(f, n);
1.50 schwarze 773: break;
1.62 schwarze 774: case NODE_AUTHORGROUP:
1.101 ! schwarze 775: macro_line(f, "An -split");
1.50 schwarze 776: break;
1.98 schwarze 777: case NODE_BLOCKQUOTE:
1.101 ! schwarze 778: macro_line(f, "Bd -ragged -offset indent");
1.98 schwarze 779: break;
1.62 schwarze 780: case NODE_BOOKINFO:
1.101 ! schwarze 781: macro_line(f, "Sh NAME");
1.50 schwarze 782: break;
1.62 schwarze 783: case NODE_CITEREFENTRY:
1.101 ! schwarze 784: pnode_printciterefentry(f, n);
1.1 kristaps 785: break;
1.68 schwarze 786: case NODE_CITETITLE:
1.101 ! schwarze 787: macro_open(f, "%T");
1.68 schwarze 788: break;
1.62 schwarze 789: case NODE_COMMAND:
1.101 ! schwarze 790: macro_open(f, "Nm");
1.13 kristaps 791: break;
1.62 schwarze 792: case NODE_CONSTANT:
1.101 ! schwarze 793: macro_open(f, "Dv");
1.33 kristaps 794: break;
1.62 schwarze 795: case NODE_EDITOR:
1.101 ! schwarze 796: print_text(f, "editor:", ARG_SPACE);
1.92 schwarze 797: sv = LINE_TEXT;
1.101 ! schwarze 798: macro_open(f, "An");
1.50 schwarze 799: break;
1.65 schwarze 800: case NODE_EMAIL:
1.101 ! schwarze 801: macro_open(f, "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.101 ! schwarze 806: macro_open(f, "Em");
1.1 kristaps 807: break;
1.62 schwarze 808: case NODE_ENVAR:
1.101 ! schwarze 809: macro_open(f, "Ev");
1.79 schwarze 810: break;
1.90 schwarze 811: case NODE_ERRORNAME:
1.101 ! schwarze 812: macro_open(f, "Er");
1.90 schwarze 813: break;
1.62 schwarze 814: case NODE_FILENAME:
1.101 ! schwarze 815: macro_open(f, "Pa");
1.17 kristaps 816: break;
1.62 schwarze 817: case NODE_FUNCTION:
1.101 ! schwarze 818: macro_open(f, "Fn");
1.3 kristaps 819: break;
1.62 schwarze 820: case NODE_FUNCPROTOTYPE:
1.101 ! schwarze 821: pnode_printfuncprototype(f, n);
1.3 kristaps 822: break;
1.62 schwarze 823: case NODE_FUNCSYNOPSISINFO:
1.101 ! schwarze 824: macro_open(f, "Fd");
1.16 kristaps 825: break;
1.62 schwarze 826: case NODE_INFORMALEQUATION:
1.101 ! schwarze 827: macro_line(f, "EQ");
1.43 kristaps 828: break;
1.62 schwarze 829: case NODE_INLINEEQUATION:
1.101 ! schwarze 830: if (f->linestate == LINE_NEW)
! 831: f->linestate = LINE_TEXT;
1.69 schwarze 832: putchar('$');
1.43 kristaps 833: break;
1.62 schwarze 834: case NODE_ITEMIZEDLIST:
1.101 ! schwarze 835: pnode_printlist(f, n);
1.24 kristaps 836: break;
1.62 schwarze 837: case NODE_GROUP:
1.101 ! schwarze 838: pnode_printgroup(f, n);
1.10 kristaps 839: break;
1.67 schwarze 840: case NODE_KEYSYM:
1.101 ! schwarze 841: macro_open(f, "Sy");
1.67 schwarze 842: break;
1.62 schwarze 843: case NODE_LINK:
1.101 ! schwarze 844: pnode_printlink(f, n);
1.96 schwarze 845: break;
1.62 schwarze 846: case NODE_LITERAL:
1.101 ! schwarze 847: macro_open(f, "Ql");
1.19 kristaps 848: break;
1.62 schwarze 849: case NODE_LITERALLAYOUT:
1.101 ! schwarze 850: macro_close(f);
! 851: macro_argline(f, "Bd", pnode_getattr(n, ATTRKEY_CLASS) ==
1.66 schwarze 852: ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60 schwarze 853: break;
1.62 schwarze 854: case NODE_MML_MFENCED:
1.101 ! schwarze 855: pnode_printmathfenced(f, n);
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.101 ! schwarze 861: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 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.101 ! schwarze 868: pnode_printmath(f, n);
1.40 kristaps 869: break;
1.62 schwarze 870: case NODE_OPTION:
1.101 ! schwarze 871: macro_open(f, "Fl");
1.1 kristaps 872: break;
1.62 schwarze 873: case NODE_ORDEREDLIST:
1.101 ! schwarze 874: pnode_printlist(f, n);
1.25 kristaps 875: break;
1.62 schwarze 876: case NODE_PARA:
1.101 ! schwarze 877: pnode_printpara(f, n);
1.3 kristaps 878: break;
1.87 schwarze 879: case NODE_PARAMDEF:
1.62 schwarze 880: case NODE_PARAMETER:
1.101 ! schwarze 881: macro_nodeline(f, "Fa", n, ARG_SINGLE);
! 882: pnode_unlinksub(n);
1.1 kristaps 883: break;
1.62 schwarze 884: case NODE_QUOTE:
1.101 ! schwarze 885: macro_open(f, "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.101 ! schwarze 890: macro_line(f, "Bd -literal");
1.15 kristaps 891: break;
1.62 schwarze 892: case NODE_REFENTRYINFO:
1.15 kristaps 893: /* Suppress. */
1.101 ! schwarze 894: pnode_unlinksub(n);
1.1 kristaps 895: break;
1.62 schwarze 896: case NODE_REFNAME:
1.10 kristaps 897: /* Suppress non-text children... */
1.101 ! schwarze 898: macro_open(f, "Nm");
! 899: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
! 900: pnode_unlinksub(n);
1.10 kristaps 901: break;
1.62 schwarze 902: case NODE_REFNAMEDIV:
1.101 ! schwarze 903: macro_line(f, "Sh NAME");
1.1 kristaps 904: break;
1.62 schwarze 905: case NODE_REFPURPOSE:
1.101 ! schwarze 906: macro_open(f, "Nd");
1.10 kristaps 907: break;
1.62 schwarze 908: case NODE_REFSYNOPSISDIV:
1.101 ! schwarze 909: pnode_printrefsynopsisdiv(f, n);
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.101 ! schwarze 919: pnode_printrefsect(f, n);
1.1 kristaps 920: break;
1.62 schwarze 921: case NODE_REPLACEABLE:
1.101 ! schwarze 922: macro_open(f, "Ar");
1.13 kristaps 923: break;
1.62 schwarze 924: case NODE_SBR:
1.101 ! schwarze 925: macro_line(f, "br");
1.19 kristaps 926: break;
1.62 schwarze 927: case NODE_SGMLTAG:
1.101 ! schwarze 928: macro_open(f, "Ic");
1.25 kristaps 929: break;
1.62 schwarze 930: case NODE_TEXT:
1.93 schwarze 931: case NODE_ESCAPE:
1.101 ! schwarze 932: pnode_printtext(f, n);
1.1 kristaps 933: break;
1.82 schwarze 934: case NODE_TGROUP:
1.101 ! schwarze 935: pnode_printtgroup(f, n);
1.82 schwarze 936: break;
1.62 schwarze 937: case NODE_TITLE:
1.101 ! schwarze 938: if (n->parent != NULL &&
! 939: n->parent->node == NODE_BOOKINFO) {
! 940: macro_open(f, "Nd");
1.82 schwarze 941: break;
942: }
1.101 ! schwarze 943: pnode_printpara(f, n);
! 944: macro_nodeline(f, "Sy", n, 0);
! 945: pnode_unlinksub(n);
1.50 schwarze 946: break;
1.62 schwarze 947: case NODE_TYPE:
1.101 ! schwarze 948: macro_open(f, "Vt");
1.39 kristaps 949: break;
1.62 schwarze 950: case NODE_VARIABLELIST:
1.101 ! schwarze 951: pnode_printvariablelist(f, n);
1.13 kristaps 952: break;
1.62 schwarze 953: case NODE_VARNAME:
1.101 ! schwarze 954: macro_open(f, "Va");
1.23 kristaps 955: break;
1.1 kristaps 956: default:
957: break;
958: }
959:
1.101 ! schwarze 960: TAILQ_FOREACH(pp, &n->childq, child)
! 961: pnode_print(f, pp);
1.1 kristaps 962:
1.101 ! schwarze 963: switch (n->node) {
1.62 schwarze 964: case NODE_INFORMALEQUATION:
1.101 ! schwarze 965: macro_line(f, "EN");
1.40 kristaps 966: break;
1.62 schwarze 967: case NODE_INLINEEQUATION:
1.43 kristaps 968: fputs("$ ", stdout);
1.101 ! schwarze 969: f->linestate = sv;
1.43 kristaps 970: break;
1.91 schwarze 971: case NODE_MEMBER:
1.101 ! schwarze 972: if ((pp = TAILQ_NEXT(n, child)) != NULL &&
1.91 schwarze 973: pp->node != NODE_MEMBER)
974: pp = NULL;
1.101 ! schwarze 975: switch (f->linestate) {
1.91 schwarze 976: case LINE_TEXT:
977: if (pp != NULL)
1.101 ! schwarze 978: print_text(f, ",", 0);
1.91 schwarze 979: break;
980: case LINE_MACRO:
981: if (pp != NULL)
1.101 ! schwarze 982: macro_addarg(f, ",", ARG_SPACE);
! 983: macro_close(f);
1.91 schwarze 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.101 ! schwarze 993: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 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.101 ! schwarze 1023: if (sv != LINE_MACRO && f->linestate == LINE_MACRO &&
! 1024: (n->parent == NULL || n->parent->node != NODE_MEMBER))
! 1025: macro_closepunct(f, n);
1.28 kristaps 1026: break;
1.62 schwarze 1027: case NODE_QUOTE:
1.69 schwarze 1028: if (sv == LINE_NEW)
1.101 ! schwarze 1029: macro_close(f);
! 1030: sv = f->linestate;
! 1031: macro_open(f, "Qc");
1.69 schwarze 1032: if (sv == LINE_NEW)
1.101 ! schwarze 1033: macro_close(f);
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.101 ! schwarze 1041: if (n->parent != NULL &&
! 1042: n->parent->node == NODE_REFNAMEDIV &&
! 1043: TAILQ_NEXT(n, child) != NULL &&
! 1044: TAILQ_NEXT(n, child)->node == NODE_REFNAME)
! 1045: macro_addarg(f, ",", ARG_SPACE);
1.69 schwarze 1046: if (sv == LINE_NEW)
1.101 ! schwarze 1047: macro_close(f);
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.101 ! schwarze 1057: f->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.101 ! schwarze 1064: macro_line(f, "Ed");
1.50 schwarze 1065: break;
1.62 schwarze 1066: case NODE_TITLE:
1.101 ! schwarze 1067: if (n->parent != NULL &&
! 1068: n->parent->node == NODE_BOOKINFO)
! 1069: macro_line(f, "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