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