Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.107
1.107 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.106 2019/04/12 07:05:19 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.101 schwarze 644: struct pnode *nc;
1.42 kristaps 645: int first = 1;
1.13 kristaps 646:
1.101 schwarze 647: macro_close(f);
648: macro_open(f, "It");
1.103 schwarze 649: f->flags |= FMT_IMPL;
1.101 schwarze 650: TAILQ_FOREACH(nc, &n->childq, child) {
651: if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
1.69 schwarze 652: continue;
653: if ( ! first)
1.101 schwarze 654: macro_addarg(f, ",", 0);
655: pnode_print(f, nc);
1.69 schwarze 656: first = 0;
657: }
1.101 schwarze 658: macro_close(f);
659: TAILQ_FOREACH(nc, &n->childq, child)
660: if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
661: pnode_print(f, nc);
662: pnode_unlinksub(n);
1.71 schwarze 663: }
664:
665: static void
1.101 schwarze 666: pnode_printtitle(struct format *f, struct pnode *n)
1.71 schwarze 667: {
1.101 schwarze 668: struct pnode *nc, *nn;
1.71 schwarze 669:
1.101 schwarze 670: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
671: if (nc->node == NODE_TITLE) {
672: pnode_printpara(f, nc);
673: pnode_print(f, nc);
674: pnode_unlink(nc);
1.71 schwarze 675: }
676: }
1.13 kristaps 677: }
678:
679: static void
1.101 schwarze 680: pnode_printrow(struct format *f, struct pnode *n)
1.25 kristaps 681: {
1.101 schwarze 682: struct pnode *nc;
1.25 kristaps 683:
1.101 schwarze 684: macro_line(f, "Bl -dash -compact");
685: TAILQ_FOREACH(nc, &n->childq, child) {
686: macro_line(f, "It");
687: pnode_print(f, nc);
1.25 kristaps 688: }
1.101 schwarze 689: macro_line(f, "El");
690: pnode_unlink(n);
1.25 kristaps 691: }
692:
693: static void
1.83 schwarze 694: pnode_printtgroup1(struct format *f, struct pnode *n)
1.16 kristaps 695: {
1.82 schwarze 696: struct pnode *nc;
697:
1.83 schwarze 698: macro_line(f, "Bl -bullet -compact");
1.82 schwarze 699: while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
1.83 schwarze 700: macro_line(f, "It");
701: pnode_print(f, nc);
1.82 schwarze 702: pnode_unlink(nc);
703: }
1.83 schwarze 704: macro_line(f, "El");
705: pnode_unlinksub(n);
706: }
707:
708: static void
709: pnode_printtgroup2(struct format *f, struct pnode *n)
710: {
711: struct pnode *nr, *ne;
712:
713: macro_line(f, "Bl -tag -width Ds");
714: while ((nr = pnode_findfirst(n, NODE_ROW)) != NULL) {
715: if ((ne = pnode_findfirst(n, NODE_ENTRY)) == NULL)
716: break;
717: macro_open(f, "It");
1.103 schwarze 718: f->flags |= FMT_IMPL;
1.83 schwarze 719: pnode_print(f, ne);
1.84 schwarze 720: macro_close(f);
1.83 schwarze 721: pnode_unlink(ne);
722: pnode_print(f, nr);
723: pnode_unlink(nr);
724: }
725: macro_line(f, "El");
1.82 schwarze 726: pnode_unlinksub(n);
727: }
728:
729: static void
1.83 schwarze 730: pnode_printtgroup(struct format *f, struct pnode *n)
1.82 schwarze 731: {
732: struct pnode *nc;
733:
734: switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
735: case 1:
1.83 schwarze 736: pnode_printtgroup1(f, n);
737: return;
738: case 2:
739: pnode_printtgroup2(f, n);
1.82 schwarze 740: return;
741: default:
742: break;
743: }
1.16 kristaps 744:
1.83 schwarze 745: macro_line(f, "Bl -ohang");
1.82 schwarze 746: while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
1.83 schwarze 747: macro_line(f, "It Table Row");
748: pnode_printrow(f, nc);
1.25 kristaps 749: }
1.83 schwarze 750: macro_line(f, "El");
1.82 schwarze 751: pnode_unlinksub(n);
1.25 kristaps 752: }
753:
754: static void
1.101 schwarze 755: pnode_printlist(struct format *f, struct pnode *n)
1.25 kristaps 756: {
1.101 schwarze 757: struct pnode *nc;
1.16 kristaps 758:
1.101 schwarze 759: pnode_printtitle(f, n);
760: macro_argline(f, "Bl",
761: n->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
762: TAILQ_FOREACH(nc, &n->childq, child) {
763: macro_line(f, "It");
764: pnode_print(f, nc);
1.16 kristaps 765: }
1.101 schwarze 766: macro_line(f, "El");
767: pnode_unlinksub(n);
1.16 kristaps 768: }
769:
770: static void
1.101 schwarze 771: pnode_printvariablelist(struct format *f, struct pnode *n)
1.13 kristaps 772: {
1.101 schwarze 773: struct pnode *nc;
1.13 kristaps 774:
1.101 schwarze 775: pnode_printtitle(f, n);
776: macro_line(f, "Bl -tag -width Ds");
777: TAILQ_FOREACH(nc, &n->childq, child) {
778: if (nc->node == NODE_VARLISTENTRY)
779: pnode_printvarlistentry(f, nc);
1.69 schwarze 780: else
1.101 schwarze 781: macro_nodeline(f, "It", nc, 0);
1.69 schwarze 782: }
1.101 schwarze 783: macro_line(f, "El");
784: pnode_unlinksub(n);
1.13 kristaps 785: }
786:
1.1 kristaps 787: /*
788: * Print a parsed node (or ignore it--whatever).
789: * This is a recursive function.
1.23 kristaps 790: * FIXME: if we're in a literal context (<screen> or <programlisting> or
791: * whatever), don't print inline macros.
1.1 kristaps 792: */
793: static void
1.101 schwarze 794: pnode_print(struct format *f, struct pnode *n)
1.1 kristaps 795: {
1.102 schwarze 796: struct pnode *nc, *nn;
1.69 schwarze 797: enum linestate sv;
1.1 kristaps 798:
1.101 schwarze 799: if (n == NULL)
1.1 kristaps 800: return;
801:
1.101 schwarze 802: sv = f->linestate;
1.103 schwarze 803: if (n->spc)
804: f->flags &= ~FMT_NOSPC;
805: else
806: f->flags |= FMT_NOSPC;
1.1 kristaps 807:
1.101 schwarze 808: switch (n->node) {
1.62 schwarze 809: case NODE_APPLICATION:
1.101 schwarze 810: macro_open(f, "Nm");
1.27 kristaps 811: break;
1.62 schwarze 812: case NODE_ARG:
1.101 schwarze 813: pnode_printarg(f, n);
1.4 kristaps 814: break;
1.62 schwarze 815: case NODE_AUTHOR:
1.101 schwarze 816: pnode_printauthor(f, n);
1.50 schwarze 817: break;
1.62 schwarze 818: case NODE_AUTHORGROUP:
1.101 schwarze 819: macro_line(f, "An -split");
1.50 schwarze 820: break;
1.98 schwarze 821: case NODE_BLOCKQUOTE:
1.101 schwarze 822: macro_line(f, "Bd -ragged -offset indent");
1.98 schwarze 823: break;
1.62 schwarze 824: case NODE_BOOKINFO:
1.101 schwarze 825: macro_line(f, "Sh NAME");
1.50 schwarze 826: break;
1.62 schwarze 827: case NODE_CITEREFENTRY:
1.101 schwarze 828: pnode_printciterefentry(f, n);
1.1 kristaps 829: break;
1.68 schwarze 830: case NODE_CITETITLE:
1.101 schwarze 831: macro_open(f, "%T");
1.68 schwarze 832: break;
1.62 schwarze 833: case NODE_COMMAND:
1.101 schwarze 834: macro_open(f, "Nm");
1.13 kristaps 835: break;
1.62 schwarze 836: case NODE_CONSTANT:
1.101 schwarze 837: macro_open(f, "Dv");
1.33 kristaps 838: break;
1.62 schwarze 839: case NODE_EDITOR:
1.101 schwarze 840: print_text(f, "editor:", ARG_SPACE);
1.92 schwarze 841: sv = LINE_TEXT;
1.101 schwarze 842: macro_open(f, "An");
1.50 schwarze 843: break;
1.65 schwarze 844: case NODE_EMAIL:
1.101 schwarze 845: macro_open(f, "Aq Mt");
1.65 schwarze 846: break;
1.62 schwarze 847: case NODE_EMPHASIS:
848: case NODE_FIRSTTERM:
1.99 schwarze 849: case NODE_GLOSSTERM:
1.101 schwarze 850: macro_open(f, "Em");
1.1 kristaps 851: break;
1.62 schwarze 852: case NODE_ENVAR:
1.101 schwarze 853: macro_open(f, "Ev");
1.79 schwarze 854: break;
1.90 schwarze 855: case NODE_ERRORNAME:
1.101 schwarze 856: macro_open(f, "Er");
1.90 schwarze 857: break;
1.62 schwarze 858: case NODE_FILENAME:
1.101 schwarze 859: macro_open(f, "Pa");
1.17 kristaps 860: break;
1.62 schwarze 861: case NODE_FUNCTION:
1.101 schwarze 862: macro_open(f, "Fn");
1.3 kristaps 863: break;
1.62 schwarze 864: case NODE_FUNCPROTOTYPE:
1.101 schwarze 865: pnode_printfuncprototype(f, n);
1.3 kristaps 866: break;
1.62 schwarze 867: case NODE_FUNCSYNOPSISINFO:
1.101 schwarze 868: macro_open(f, "Fd");
1.16 kristaps 869: break;
1.62 schwarze 870: case NODE_INFORMALEQUATION:
1.101 schwarze 871: macro_line(f, "EQ");
1.43 kristaps 872: break;
1.62 schwarze 873: case NODE_INLINEEQUATION:
1.101 schwarze 874: if (f->linestate == LINE_NEW)
875: f->linestate = LINE_TEXT;
1.69 schwarze 876: putchar('$');
1.43 kristaps 877: break;
1.62 schwarze 878: case NODE_ITEMIZEDLIST:
1.101 schwarze 879: pnode_printlist(f, n);
1.24 kristaps 880: break;
1.62 schwarze 881: case NODE_GROUP:
1.101 schwarze 882: pnode_printgroup(f, n);
1.10 kristaps 883: break;
1.67 schwarze 884: case NODE_KEYSYM:
1.101 schwarze 885: macro_open(f, "Sy");
1.67 schwarze 886: break;
1.62 schwarze 887: case NODE_LINK:
1.101 schwarze 888: pnode_printlink(f, n);
1.96 schwarze 889: break;
1.62 schwarze 890: case NODE_LITERAL:
1.101 schwarze 891: macro_open(f, "Ql");
1.19 kristaps 892: break;
1.62 schwarze 893: case NODE_LITERALLAYOUT:
1.101 schwarze 894: macro_close(f);
895: macro_argline(f, "Bd", pnode_getattr(n, ATTRKEY_CLASS) ==
1.66 schwarze 896: ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60 schwarze 897: break;
1.106 schwarze 898: case NODE_MARKUP:
899: macro_open(f, "Ic");
900: break;
1.62 schwarze 901: case NODE_MML_MFENCED:
1.101 schwarze 902: pnode_printmathfenced(f, n);
1.40 kristaps 903: break;
1.62 schwarze 904: case NODE_MML_MROW:
905: case NODE_MML_MI:
906: case NODE_MML_MN:
907: case NODE_MML_MO:
1.101 schwarze 908: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 909: break;
910: fputs(" { ", stdout);
1.40 kristaps 911: break;
1.62 schwarze 912: case NODE_MML_MFRAC:
913: case NODE_MML_MSUB:
914: case NODE_MML_MSUP:
1.101 schwarze 915: pnode_printmath(f, n);
1.40 kristaps 916: break;
1.62 schwarze 917: case NODE_OPTION:
1.101 schwarze 918: macro_open(f, "Fl");
1.1 kristaps 919: break;
1.62 schwarze 920: case NODE_ORDEREDLIST:
1.101 schwarze 921: pnode_printlist(f, n);
1.25 kristaps 922: break;
1.62 schwarze 923: case NODE_PARA:
1.101 schwarze 924: pnode_printpara(f, n);
1.3 kristaps 925: break;
1.87 schwarze 926: case NODE_PARAMDEF:
1.62 schwarze 927: case NODE_PARAMETER:
1.104 schwarze 928: /* More often, these appear inside NODE_FUNCPROTOTYPE. */
929: macro_open(f, "Fa");
930: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
1.101 schwarze 931: pnode_unlinksub(n);
1.1 kristaps 932: break;
1.62 schwarze 933: case NODE_QUOTE:
1.101 schwarze 934: macro_open(f, "Qo");
1.28 kristaps 935: break;
1.62 schwarze 936: case NODE_PROGRAMLISTING:
937: case NODE_SCREEN:
1.88 schwarze 938: case NODE_SYNOPSIS:
1.101 schwarze 939: macro_line(f, "Bd -literal");
1.15 kristaps 940: break;
1.62 schwarze 941: case NODE_REFENTRYINFO:
1.15 kristaps 942: /* Suppress. */
1.101 schwarze 943: pnode_unlinksub(n);
1.1 kristaps 944: break;
1.62 schwarze 945: case NODE_REFNAME:
1.10 kristaps 946: /* Suppress non-text children... */
1.101 schwarze 947: macro_open(f, "Nm");
948: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
949: pnode_unlinksub(n);
1.10 kristaps 950: break;
1.62 schwarze 951: case NODE_REFNAMEDIV:
1.101 schwarze 952: macro_line(f, "Sh NAME");
1.1 kristaps 953: break;
1.62 schwarze 954: case NODE_REFPURPOSE:
1.101 schwarze 955: macro_open(f, "Nd");
1.10 kristaps 956: break;
1.62 schwarze 957: case NODE_REFSYNOPSISDIV:
1.101 schwarze 958: pnode_printrefsynopsisdiv(f, n);
1.1 kristaps 959: break;
1.62 schwarze 960: case NODE_PREFACE:
961: case NODE_SECTION:
1.100 schwarze 962: case NODE_APPENDIX:
963: case NODE_LEGALNOTICE:
1.62 schwarze 964: case NODE_NOTE:
965: case NODE_TIP:
966: case NODE_CAUTION:
967: case NODE_WARNING:
1.101 schwarze 968: pnode_printrefsect(f, n);
1.1 kristaps 969: break;
1.62 schwarze 970: case NODE_REPLACEABLE:
1.101 schwarze 971: macro_open(f, "Ar");
1.13 kristaps 972: break;
1.62 schwarze 973: case NODE_SBR:
1.101 schwarze 974: macro_line(f, "br");
1.25 kristaps 975: break;
1.62 schwarze 976: case NODE_TEXT:
1.93 schwarze 977: case NODE_ESCAPE:
1.101 schwarze 978: pnode_printtext(f, n);
1.1 kristaps 979: break;
1.82 schwarze 980: case NODE_TGROUP:
1.101 schwarze 981: pnode_printtgroup(f, n);
1.82 schwarze 982: break;
1.62 schwarze 983: case NODE_TITLE:
1.101 schwarze 984: if (n->parent != NULL &&
985: n->parent->node == NODE_BOOKINFO) {
986: macro_open(f, "Nd");
1.82 schwarze 987: break;
988: }
1.101 schwarze 989: pnode_printpara(f, n);
990: macro_nodeline(f, "Sy", n, 0);
991: pnode_unlinksub(n);
1.50 schwarze 992: break;
1.62 schwarze 993: case NODE_TYPE:
1.101 schwarze 994: macro_open(f, "Vt");
1.39 kristaps 995: break;
1.62 schwarze 996: case NODE_VARIABLELIST:
1.101 schwarze 997: pnode_printvariablelist(f, n);
1.13 kristaps 998: break;
1.62 schwarze 999: case NODE_VARNAME:
1.101 schwarze 1000: macro_open(f, "Va");
1.23 kristaps 1001: break;
1.1 kristaps 1002: default:
1003: break;
1004: }
1005:
1.102 schwarze 1006: TAILQ_FOREACH(nc, &n->childq, child)
1007: pnode_print(f, nc);
1.1 kristaps 1008:
1.101 schwarze 1009: switch (n->node) {
1.103 schwarze 1010: case NODE_ESCAPE:
1011: case NODE_TERM:
1012: case NODE_TEXT:
1013: /* Accept more arguments to the previous macro. */
1014: return;
1.62 schwarze 1015: case NODE_INFORMALEQUATION:
1.101 schwarze 1016: macro_line(f, "EN");
1.40 kristaps 1017: break;
1.62 schwarze 1018: case NODE_INLINEEQUATION:
1.43 kristaps 1019: fputs("$ ", stdout);
1.101 schwarze 1020: f->linestate = sv;
1.43 kristaps 1021: break;
1.91 schwarze 1022: case NODE_MEMBER:
1.102 schwarze 1023: if ((nn = TAILQ_NEXT(n, child)) != NULL &&
1024: nn->node != NODE_MEMBER)
1025: nn = NULL;
1.101 schwarze 1026: switch (f->linestate) {
1.91 schwarze 1027: case LINE_TEXT:
1.102 schwarze 1028: if (nn != NULL)
1.101 schwarze 1029: print_text(f, ",", 0);
1.91 schwarze 1030: break;
1031: case LINE_MACRO:
1.102 schwarze 1032: if (nn != NULL)
1.101 schwarze 1033: macro_addarg(f, ",", ARG_SPACE);
1034: macro_close(f);
1.91 schwarze 1035: break;
1036: case LINE_NEW:
1037: break;
1038: }
1039: break;
1.62 schwarze 1040: case NODE_MML_MROW:
1041: case NODE_MML_MI:
1042: case NODE_MML_MN:
1043: case NODE_MML_MO:
1.101 schwarze 1044: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 1045: break;
1046: fputs(" } ", stdout);
1.40 kristaps 1047: break;
1.62 schwarze 1048: case NODE_QUOTE:
1.69 schwarze 1049: if (sv == LINE_NEW)
1.101 schwarze 1050: macro_close(f);
1051: sv = f->linestate;
1052: macro_open(f, "Qc");
1.69 schwarze 1053: if (sv == LINE_NEW)
1.101 schwarze 1054: macro_close(f);
1.10 kristaps 1055: break;
1.62 schwarze 1056: case NODE_REFNAME:
1.12 kristaps 1057: /*
1058: * If we're in the NAME macro and we have multiple
1059: * <refname> macros in sequence, then print out a
1060: * trailing comma before the newline.
1061: */
1.101 schwarze 1062: if (n->parent != NULL &&
1063: n->parent->node == NODE_REFNAMEDIV &&
1064: TAILQ_NEXT(n, child) != NULL &&
1065: TAILQ_NEXT(n, child)->node == NODE_REFNAME)
1066: macro_addarg(f, ",", ARG_SPACE);
1.69 schwarze 1067: if (sv == LINE_NEW)
1.101 schwarze 1068: macro_close(f);
1.52 schwarze 1069: break;
1.62 schwarze 1070: case NODE_PREFACE:
1071: case NODE_SECTION:
1.100 schwarze 1072: case NODE_APPENDIX:
1073: case NODE_LEGALNOTICE:
1.62 schwarze 1074: case NODE_NOTE:
1075: case NODE_TIP:
1076: case NODE_CAUTION:
1077: case NODE_WARNING:
1.101 schwarze 1078: f->level--;
1.12 kristaps 1079: break;
1.98 schwarze 1080: case NODE_BLOCKQUOTE:
1.62 schwarze 1081: case NODE_LITERALLAYOUT:
1082: case NODE_PROGRAMLISTING:
1083: case NODE_SCREEN:
1.88 schwarze 1084: case NODE_SYNOPSIS:
1.101 schwarze 1085: macro_line(f, "Ed");
1.50 schwarze 1086: break;
1.62 schwarze 1087: case NODE_TITLE:
1.101 schwarze 1088: if (n->parent != NULL &&
1089: n->parent->node == NODE_BOOKINFO)
1090: macro_line(f, "Sh AUTHORS");
1.1 kristaps 1091: break;
1092: default:
1093: break;
1094: }
1.103 schwarze 1095: f->flags &= ~FMT_ARG;
1.1 kristaps 1096: }
1097:
1.74 schwarze 1098: void
1099: ptree_print(struct ptree *tree)
1100: {
1101: struct format formatter;
1.1 kristaps 1102:
1.74 schwarze 1103: formatter.level = 0;
1104: formatter.linestate = LINE_NEW;
1105: pnode_printprologue(&formatter, tree);
1106: pnode_print(&formatter, tree->root);
1107: if (formatter.linestate != LINE_NEW)
1108: putchar('\n');
1.1 kristaps 1109: }
CVSweb