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