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