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