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