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