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