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