Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.137
1.137 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.136 2019/04/24 14:56:51 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.119 schwarze 33: static void pnode_printrefentry(struct format *, struct pnode *);
1.25 kristaps 34:
1.37 kristaps 35:
1.54 schwarze 36: static void
1.93 schwarze 37: pnode_printtext(struct format *f, struct pnode *n)
38: {
1.94 schwarze 39: struct pnode *nn;
1.93 schwarze 40: char *cp;
1.103 schwarze 41: int accept_arg;
1.93 schwarze 42:
1.103 schwarze 43: cp = n->b;
44: accept_arg = f->flags & FMT_ARG;
1.107 schwarze 45: if (f->linestate == LINE_MACRO && !n->spc && !accept_arg) {
1.103 schwarze 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 &&
1.113 schwarze 74: (nn = TAILQ_NEXT(n, child)) != NULL && nn->spc == 0) {
75: switch (pnode_class(nn->node)) {
76: case CLASS_LINE:
77: case CLASS_ENCL:
78: macro_open(f, "Pf");
79: accept_arg = 1;
80: f->flags |= FMT_CHILD;
81: nn->spc = 1;
82: break;
83: default:
84: break;
85: }
1.93 schwarze 86: }
87:
1.103 schwarze 88: switch (f->linestate) {
1.107 schwarze 89: case LINE_NEW:
90: break;
1.103 schwarze 91: case LINE_TEXT:
1.105 schwarze 92: if (n->spc) {
1.137 ! schwarze 93: if (pnode_class(n->node) == CLASS_TEXT)
1.107 schwarze 94: macro_close(f);
95: else
96: putchar(' ');
1.105 schwarze 97: }
1.103 schwarze 98: break;
99: case LINE_MACRO:
1.136 schwarze 100: if (accept_arg == 0)
101: macro_close(f);
102: else if (n->spc || (f->flags & FMT_ARG) == 0 ||
103: (nn = TAILQ_PREV(n, pnodeq, child)) == NULL ||
104: pnode_class(nn->node) != CLASS_TEXT)
1.93 schwarze 105: putchar(' ');
1.103 schwarze 106: break;
1.93 schwarze 107: }
108:
109: if (n->node == NODE_ESCAPE) {
110: fputs(n->b, stdout);
1.107 schwarze 111: if (f->linestate == LINE_NEW)
112: f->linestate = LINE_TEXT;
1.93 schwarze 113: return;
114: }
115:
116: /*
117: * Remove the prefix '-' from <option> elements
118: * because the arguments of .Fl macros do not need it.
119: */
120:
121: if (n->parent != NULL && n->parent->node == NODE_OPTION && *cp == '-')
122: cp++;
123:
1.107 schwarze 124: if (f->linestate == LINE_MACRO)
125: macro_addarg(f, cp, 0);
126: else
127: print_text(f, cp, 0);
1.93 schwarze 128: }
129:
130: static void
1.126 schwarze 131: pnode_printimagedata(struct format *f, struct pnode *n)
132: {
133: const char *cp;
134:
135: if ((cp = pnode_getattr_raw(n, ATTRKEY_FILEREF, NULL)) == NULL)
136: cp = pnode_getattr_raw(n, ATTRKEY_ENTITYREF, NULL);
137: if (cp != NULL) {
138: print_text(f, "[image:", ARG_SPACE);
139: print_text(f, cp, ARG_SPACE);
140: print_text(f, "]", 0);
141: } else
142: print_text(f, "[image]", ARG_SPACE);
143: }
144:
145: static void
1.110 schwarze 146: pnode_printrefnamediv(struct format *f, struct pnode *n)
147: {
148: struct pnode *nc, *nn;
149: int comma;
150:
1.130 schwarze 151: f->parastate = PARA_HAVE;
1.110 schwarze 152: macro_line(f, "Sh NAME");
1.130 schwarze 153: f->parastate = PARA_HAVE;
1.110 schwarze 154: comma = 0;
155: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
156: if (nc->node != NODE_REFNAME)
157: continue;
158: if (comma)
159: macro_addarg(f, ",", ARG_SPACE);
160: macro_open(f, "Nm");
161: macro_addnode(f, nc, ARG_SPACE);
162: pnode_unlink(nc);
163: comma = 1;
164: }
165: macro_close(f);
166: }
167:
1.37 kristaps 168: /*
1.10 kristaps 169: * If the SYNOPSIS macro has a superfluous title, kill it.
1.8 kristaps 170: */
1.1 kristaps 171: static void
1.101 schwarze 172: pnode_printrefsynopsisdiv(struct format *f, struct pnode *n)
1.6 kristaps 173: {
1.101 schwarze 174: struct pnode *nc, *nn;
1.6 kristaps 175:
1.101 schwarze 176: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn)
177: if (nc->node == NODE_TITLE)
178: pnode_unlink(nc);
1.71 schwarze 179:
1.130 schwarze 180: f->parastate = PARA_HAVE;
1.101 schwarze 181: macro_line(f, "Sh SYNOPSIS");
1.130 schwarze 182: f->parastate = PARA_HAVE;
1.6 kristaps 183: }
184:
1.8 kristaps 185: /*
186: * Start a hopefully-named `Sh' section.
187: */
1.6 kristaps 188: static void
1.119 schwarze 189: pnode_printsection(struct format *f, struct pnode *n)
1.1 kristaps 190: {
1.115 schwarze 191: struct pnode *nc, *ncc;
1.52 schwarze 192: const char *title;
193: int flags, level;
194:
1.119 schwarze 195: if (n->parent == NULL) {
196: pnode_printrefentry(f, n);
1.56 schwarze 197: return;
1.119 schwarze 198: }
1.56 schwarze 199:
1.101 schwarze 200: level = ++f->level;
1.72 schwarze 201: flags = ARG_SPACE;
1.121 schwarze 202: switch (n->node) {
203: case NODE_PREFACE:
204: case NODE_SECTION:
205: case NODE_APPENDIX:
206: if (level == 1)
207: flags |= ARG_UPPER;
208: break;
209: case NODE_SIMPLESECT:
210: case NODE_LEGALNOTICE:
211: if (level < 2)
212: level = 2;
213: break;
214: default:
215: if (level < 3)
1.52 schwarze 216: level = 3;
1.121 schwarze 217: break;
1.52 schwarze 218: }
1.1 kristaps 219:
1.101 schwarze 220: TAILQ_FOREACH(nc, &n->childq, child)
221: if (nc->node == NODE_TITLE)
1.1 kristaps 222: break;
223:
1.101 schwarze 224: if (nc == NULL) {
225: switch (n->node) {
1.62 schwarze 226: case NODE_PREFACE:
1.52 schwarze 227: title = "Preface";
228: break;
1.100 schwarze 229: case NODE_APPENDIX:
230: title = "Appendix";
231: break;
232: case NODE_LEGALNOTICE:
233: title = "Legal Notice";
234: break;
1.62 schwarze 235: case NODE_CAUTION:
1.52 schwarze 236: title = "Caution";
237: break;
1.62 schwarze 238: case NODE_NOTE:
1.52 schwarze 239: title = "Note";
240: break;
1.62 schwarze 241: case NODE_TIP:
1.52 schwarze 242: title = "Tip";
243: break;
1.62 schwarze 244: case NODE_WARNING:
1.52 schwarze 245: title = "Warning";
246: break;
247: default:
248: title = "Unknown";
249: break;
250: }
251: }
252:
253: switch (level) {
1.62 schwarze 254: case 1:
1.101 schwarze 255: macro_close(f);
1.130 schwarze 256: f->parastate = PARA_HAVE;
1.101 schwarze 257: macro_open(f, "Sh");
1.29 kristaps 258: break;
1.62 schwarze 259: case 2:
1.101 schwarze 260: macro_close(f);
1.130 schwarze 261: f->parastate = PARA_HAVE;
1.101 schwarze 262: macro_open(f, "Ss");
1.29 kristaps 263: break;
1.52 schwarze 264: default:
1.130 schwarze 265: if (f->parastate == PARA_MID)
266: f->parastate = PARA_WANT;
1.101 schwarze 267: macro_open(f, "Sy");
1.29 kristaps 268: break;
269: }
1.20 kristaps 270:
1.115 schwarze 271: if (nc != NULL)
1.101 schwarze 272: macro_addnode(f, nc, flags);
1.115 schwarze 273: else
1.101 schwarze 274: macro_addarg(f, title, flags | ARG_QUOTED);
275: macro_close(f);
1.115 schwarze 276:
277: /*
278: * DocBook has no equivalent for -split mode,
279: * so just switch the default in the AUTHORS section.
280: */
281:
282: if (nc != NULL) {
1.130 schwarze 283: if (level == 1 &&
284: (ncc = TAILQ_FIRST(&nc->childq)) != NULL &&
285: ncc->node == NODE_TEXT &&
1.115 schwarze 286: strcasecmp(ncc->b, "AUTHORS") == 0)
287: macro_line(f, "An -nosplit");
288: pnode_unlink(nc);
289: }
1.130 schwarze 290: f->parastate = level > 2 ? PARA_WANT : PARA_HAVE;
1.1 kristaps 291: }
292:
1.8 kristaps 293: /*
294: * Start a reference, extracting the title and volume.
295: */
1.1 kristaps 296: static void
1.101 schwarze 297: pnode_printciterefentry(struct format *f, struct pnode *n)
1.1 kristaps 298: {
1.101 schwarze 299: struct pnode *nc, *title, *manvol;
1.1 kristaps 300:
301: title = manvol = NULL;
1.101 schwarze 302: TAILQ_FOREACH(nc, &n->childq, child) {
303: if (nc->node == NODE_MANVOLNUM)
304: manvol = nc;
305: else if (nc->node == NODE_REFENTRYTITLE)
306: title = nc;
1.69 schwarze 307: }
1.101 schwarze 308: macro_open(f, "Xr");
1.69 schwarze 309: if (title == NULL)
1.101 schwarze 310: macro_addarg(f, "unknown", ARG_SPACE);
1.69 schwarze 311: else
1.101 schwarze 312: macro_addnode(f, title, ARG_SPACE | ARG_SINGLE);
1.69 schwarze 313: if (manvol == NULL)
1.101 schwarze 314: macro_addarg(f, "1", ARG_SPACE);
1.64 schwarze 315: else
1.101 schwarze 316: macro_addnode(f, manvol, ARG_SPACE | ARG_SINGLE);
317: pnode_unlinksub(n);
1.1 kristaps 318: }
319:
1.40 kristaps 320: /*
1.41 kristaps 321: * The <mml:mfenced> node is a little peculiar.
322: * First, it can have arbitrary open and closing tokens, which default
323: * to parentheses.
324: * Second, >1 arguments are separated by commas.
325: */
326: static void
1.101 schwarze 327: pnode_printmathfenced(struct format *f, struct pnode *n)
1.41 kristaps 328: {
1.101 schwarze 329: struct pnode *nc;
1.41 kristaps 330:
1.101 schwarze 331: printf("left %s ", pnode_getattr_raw(n, ATTRKEY_OPEN, "("));
1.41 kristaps 332:
1.101 schwarze 333: nc = TAILQ_FIRST(&n->childq);
334: pnode_print(f, nc);
1.41 kristaps 335:
1.101 schwarze 336: while ((nc = TAILQ_NEXT(nc, child)) != NULL) {
1.41 kristaps 337: putchar(',');
1.101 schwarze 338: pnode_print(f, nc);
1.41 kristaps 339: }
1.101 schwarze 340: printf("right %s ", pnode_getattr_raw(n, ATTRKEY_CLOSE, ")"));
341: pnode_unlinksub(n);
1.41 kristaps 342: }
343:
344: /*
1.40 kristaps 345: * These math nodes require special handling because they have infix
346: * syntax, instead of the usual prefix or prefix.
347: * So we need to break up the first and second child node with a
348: * particular eqn(7) word.
349: */
350: static void
1.101 schwarze 351: pnode_printmath(struct format *f, struct pnode *n)
1.40 kristaps 352: {
1.101 schwarze 353: struct pnode *nc;
1.40 kristaps 354:
1.101 schwarze 355: nc = TAILQ_FIRST(&n->childq);
356: pnode_print(f, nc);
1.40 kristaps 357:
1.101 schwarze 358: switch (n->node) {
1.62 schwarze 359: case NODE_MML_MSUP:
1.42 kristaps 360: fputs(" sup ", stdout);
1.40 kristaps 361: break;
1.62 schwarze 362: case NODE_MML_MFRAC:
1.42 kristaps 363: fputs(" over ", stdout);
1.40 kristaps 364: break;
1.62 schwarze 365: case NODE_MML_MSUB:
1.42 kristaps 366: fputs(" sub ", stdout);
1.40 kristaps 367: break;
368: default:
369: break;
370: }
371:
1.101 schwarze 372: nc = TAILQ_NEXT(nc, child);
373: pnode_print(f, nc);
374: pnode_unlinksub(n);
1.40 kristaps 375: }
376:
1.3 kristaps 377: static void
1.101 schwarze 378: pnode_printfuncprototype(struct format *f, struct pnode *n)
1.3 kristaps 379: {
1.132 schwarze 380: struct pnode *fdef, *ftype, *nc, *nn;
1.3 kristaps 381:
1.132 schwarze 382: /*
383: * Extract <funcdef> child and ignore <void> child.
384: * Leave other children in place, to be treated as parameters.
385: */
386:
387: fdef = NULL;
388: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
389: switch (nc->node) {
390: case NODE_FUNCDEF:
391: if (fdef == NULL) {
392: fdef = nc;
393: TAILQ_REMOVE(&n->childq, nc, child);
394: nc->parent = NULL;
395: }
396: break;
397: case NODE_VOID:
398: pnode_unlink(nc);
1.3 kristaps 399: break;
1.132 schwarze 400: default:
401: break;
402: }
403: }
1.3 kristaps 404:
1.132 schwarze 405: /*
406: * If no children are left, the function is void; use .Fn.
407: * Otherwise, use .Fo.
408: */
409:
410: nc = TAILQ_FIRST(&n->childq);
1.86 schwarze 411: if (fdef != NULL) {
1.132 schwarze 412: ftype = TAILQ_FIRST(&fdef->childq);
413: if (ftype != NULL && ftype->node == NODE_TEXT) {
414: macro_argline(f, "Ft", ftype->b);
415: pnode_unlink(ftype);
416: }
417: if (nc == NULL) {
418: macro_open(f, "Fn");
419: macro_addnode(f, fdef, ARG_SPACE | ARG_SINGLE);
420: macro_addarg(f, "void", ARG_SPACE);
421: macro_close(f);
422: } else
423: macro_nodeline(f, "Fo", fdef, ARG_SINGLE);
1.86 schwarze 424: pnode_unlink(fdef);
1.132 schwarze 425: } else if (nc == NULL)
426: macro_line(f, "Fn UNKNOWN void");
427: else
1.101 schwarze 428: macro_line(f, "Fo UNKNOWN");
1.3 kristaps 429:
1.132 schwarze 430: if (nc == NULL)
431: return;
432:
433: while (nc != NULL) {
1.101 schwarze 434: macro_nodeline(f, "Fa", nc, ARG_SINGLE);
1.132 schwarze 435: pnode_unlink(nc);
436: nc = TAILQ_FIRST(&n->childq);
437: }
1.101 schwarze 438: macro_line(f, "Fc");
1.3 kristaps 439: }
440:
1.44 schwarze 441: /*
1.10 kristaps 442: * The <arg> element is more complicated than it should be because text
443: * nodes are treated like ".Ar foo", but non-text nodes need to be
444: * re-sent into the printer (i.e., without the preceding ".Ar").
1.12 kristaps 445: * This also handles the case of "repetition" (or in other words, the
446: * ellipsis following an argument) and optionality.
1.10 kristaps 447: */
1.4 kristaps 448: static void
1.101 schwarze 449: pnode_printarg(struct format *f, struct pnode *n)
1.4 kristaps 450: {
1.101 schwarze 451: struct pnode *nc;
452: struct pattr *a;
1.103 schwarze 453: int isop, isrep, was_impl;
1.12 kristaps 454:
455: isop = 1;
1.103 schwarze 456: isrep = was_impl = 0;
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.12 kristaps 460: isop = 0;
1.101 schwarze 461: else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT)
1.12 kristaps 462: isrep = 1;
463: }
1.103 schwarze 464: if (isop) {
465: if (f->flags & FMT_IMPL) {
466: was_impl = 1;
467: macro_open(f, "Oo");
468: } else {
469: macro_open(f, "Op");
470: f->flags |= FMT_IMPL;
471: }
472: }
1.101 schwarze 473: TAILQ_FOREACH(nc, &n->childq, child) {
474: if (nc->node == NODE_TEXT)
475: macro_open(f, "Ar");
476: pnode_print(f, nc);
1.10 kristaps 477: }
1.117 schwarze 478: if (isrep && f->linestate == LINE_MACRO)
479: macro_addarg(f, "...", ARG_SPACE);
1.103 schwarze 480: if (isop) {
481: if (was_impl)
482: macro_open(f, "Oc");
483: else
484: f->flags &= ~FMT_IMPL;
485: }
1.101 schwarze 486: pnode_unlinksub(n);
1.4 kristaps 487: }
488:
1.24 kristaps 489: static void
1.101 schwarze 490: pnode_printgroup(struct format *f, struct pnode *n)
1.24 kristaps 491: {
1.117 schwarze 492: struct pnode *nc;
1.101 schwarze 493: struct pattr *a;
1.117 schwarze 494: int bar, isop, isrep, was_impl;
1.24 kristaps 495:
496: isop = 1;
1.117 schwarze 497: isrep = was_impl = 0;
498: TAILQ_FOREACH(a, &n->attrq, child) {
1.101 schwarze 499: if (a->key == ATTRKEY_CHOICE &&
1.117 schwarze 500: (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ))
1.24 kristaps 501: isop = 0;
1.117 schwarze 502: else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT)
503: isrep = 1;
504: }
505: if (isop) {
506: if (f->flags & FMT_IMPL) {
507: was_impl = 1;
508: macro_open(f, "Oo");
509: } else {
510: macro_open(f, "Op");
511: f->flags |= FMT_IMPL;
512: }
513: } else if (isrep) {
514: if (f->flags & FMT_IMPL) {
515: was_impl = 1;
516: macro_open(f, "Bro");
517: } else {
518: macro_open(f, "Brq");
519: f->flags |= FMT_IMPL;
1.24 kristaps 520: }
1.117 schwarze 521: }
522: bar = 0;
1.101 schwarze 523: TAILQ_FOREACH(nc, &n->childq, child) {
1.117 schwarze 524: if (bar && f->linestate == LINE_MACRO)
525: macro_addarg(f, "|", ARG_SPACE);
1.101 schwarze 526: pnode_print(f, nc);
1.117 schwarze 527: bar = 1;
528: }
529: if (isop) {
530: if (was_impl)
531: macro_open(f, "Oc");
532: else
533: f->flags &= ~FMT_IMPL;
534: } else if (isrep) {
535: if (was_impl)
536: macro_open(f, "Brc");
537: else
538: f->flags &= ~FMT_IMPL;
1.24 kristaps 539: }
1.117 schwarze 540: if (isrep && f->linestate == LINE_MACRO)
541: macro_addarg(f, "...", ARG_SPACE);
1.101 schwarze 542: pnode_unlinksub(n);
1.24 kristaps 543: }
544:
1.7 kristaps 545: static void
1.118 schwarze 546: pnode_printsystemitem(struct format *f, struct pnode *n)
547: {
548: switch (pnode_getattr(n, ATTRKEY_CLASS)) {
549: case ATTRVAL_IPADDRESS:
550: break;
551: case ATTRVAL_SYSTEMNAME:
552: macro_open(f, "Pa");
553: break;
554: case ATTRVAL_EVENT:
555: default:
556: macro_open(f, "Sy");
557: break;
558: }
559: }
560:
561: static void
1.78 schwarze 562: pnode_printauthor(struct format *f, struct pnode *n)
563: {
1.101 schwarze 564: struct pnode *nc, *nn;
1.78 schwarze 565: int have_contrib, have_name;
566:
567: /*
568: * Print <contrib> children up front, before the .An scope,
569: * and figure out whether we a name of a person.
570: */
571:
572: have_contrib = have_name = 0;
1.101 schwarze 573: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78 schwarze 574: switch (nc->node) {
575: case NODE_CONTRIB:
576: if (have_contrib)
577: print_text(f, ",", 0);
578: print_textnode(f, nc);
579: pnode_unlink(nc);
580: have_contrib = 1;
581: break;
582: case NODE_PERSONNAME:
583: have_name = 1;
584: break;
585: default:
586: break;
587: }
588: }
589: if (TAILQ_FIRST(&n->childq) == NULL)
590: return;
591:
592: if (have_contrib)
593: print_text(f, ":", 0);
594:
595: /*
596: * If we have a name, print it in the .An scope and leave
597: * all other content for child handlers, to print after the
598: * scope. Otherwise, print everything in the scope.
599: */
600:
601: macro_open(f, "An");
1.101 schwarze 602: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78 schwarze 603: if (nc->node == NODE_PERSONNAME || have_name == 0) {
604: macro_addnode(f, nc, ARG_SPACE);
605: pnode_unlink(nc);
606: }
1.80 schwarze 607: }
608:
609: /*
610: * If there is an email address,
611: * print it on the same macro line.
612: */
613:
614: if ((nc = pnode_findfirst(n, NODE_EMAIL)) != NULL) {
1.103 schwarze 615: f->flags |= FMT_CHILD;
1.115 schwarze 616: macro_open(f, "Aq Mt");
617: macro_addnode(f, nc, ARG_SPACE);
1.80 schwarze 618: pnode_unlink(nc);
1.78 schwarze 619: }
620:
621: /*
622: * If there are still unprinted children, end the scope
623: * with a comma. Otherwise, leave the scope open in case
624: * a text node follows that starts with closing punctuation.
625: */
626:
627: if (TAILQ_FIRST(&n->childq) != NULL) {
628: macro_addarg(f, ",", ARG_SPACE);
629: macro_close(f);
630: }
631: }
632:
633: static void
1.129 schwarze 634: pnode_printxref(struct format *f, struct pnode *n)
635: {
636: const char *linkend;
637:
638: linkend = pnode_getattr_raw(n, ATTRKEY_LINKEND, NULL);
639: if (linkend != NULL) {
640: macro_open(f, "Sx");
641: macro_addarg(f, linkend, ARG_SPACE);
642: }
643: }
644:
645: static void
1.96 schwarze 646: pnode_printlink(struct format *f, struct pnode *n)
647: {
1.109 schwarze 648: struct pnode *nc;
1.96 schwarze 649: const char *uri, *text;
650:
651: uri = pnode_getattr_raw(n, ATTRKEY_LINKEND, NULL);
652: if (uri != NULL) {
653: if (TAILQ_FIRST(&n->childq) != NULL) {
1.109 schwarze 654: TAILQ_FOREACH(nc, &n->childq, child)
655: pnode_print(f, nc);
1.96 schwarze 656: text = "";
1.109 schwarze 657: } else if ((text = pnode_getattr_raw(n,
658: ATTRKEY_ENDTERM, NULL)) != NULL) {
659: if (f->linestate == LINE_MACRO && f->flags & FMT_ARG)
660: macro_addarg(f, text, ARG_SPACE);
661: else
1.96 schwarze 662: print_text(f, text, ARG_SPACE);
663: }
1.103 schwarze 664: if (text != NULL) {
1.109 schwarze 665: if (f->flags & FMT_IMPL)
666: macro_open(f, "Po");
667: else {
668: macro_open(f, "Pq");
669: f->flags |= FMT_CHILD;
670: }
1.103 schwarze 671: }
1.96 schwarze 672: macro_open(f, "Sx");
673: macro_addarg(f, uri, ARG_SPACE);
1.109 schwarze 674: if (text != NULL && f->flags & FMT_IMPL)
675: macro_open(f, "Pc");
1.96 schwarze 676: pnode_unlinksub(n);
677: return;
678: }
679: uri = pnode_getattr_raw(n, ATTRKEY_XLINK_HREF, NULL);
680: if (uri == NULL)
681: uri = pnode_getattr_raw(n, ATTRKEY_URL, NULL);
682: if (uri != NULL) {
683: macro_open(f, "Lk");
684: macro_addarg(f, uri, ARG_SPACE | ARG_SINGLE);
685: if (TAILQ_FIRST(&n->childq) != NULL)
686: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
687: pnode_unlinksub(n);
1.128 schwarze 688: }
689: }
690:
691: static void
692: pnode_printolink(struct format *f, struct pnode *n)
693: {
694: const char *uri, *ptr, *local;
695:
696: uri = pnode_getattr_raw(n, ATTRKEY_TARGETDOC, NULL);
697: ptr = pnode_getattr_raw(n, ATTRKEY_TARGETPTR, NULL);
698: local = pnode_getattr_raw(n, ATTRKEY_LOCALINFO, NULL);
699: if (uri == NULL) {
700: uri = ptr;
701: ptr = NULL;
702: }
703: if (uri == NULL) {
704: uri = local;
705: local = NULL;
706: }
707: if (uri == NULL)
1.96 schwarze 708: return;
1.128 schwarze 709:
710: macro_open(f, "Lk");
711: macro_addarg(f, uri, ARG_SPACE | ARG_SINGLE);
712: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
713: if (ptr != NULL || local != NULL) {
714: macro_close(f);
715: macro_open(f, "Pq");
716: if (ptr != NULL)
717: macro_addarg(f, ptr, ARG_SPACE);
718: if (local != NULL)
719: macro_addarg(f, local, ARG_SPACE);
1.96 schwarze 720: }
1.128 schwarze 721: pnode_unlinksub(n);
1.96 schwarze 722: }
723:
724: static void
1.116 schwarze 725: pnode_printprologue(struct format *f, struct pnode *root)
1.7 kristaps 726: {
1.119 schwarze 727: struct pnode *date, *refmeta, *name, *vol, *descr, *nc, *nn;
1.116 schwarze 728: const char *sname;
1.7 kristaps 729:
1.116 schwarze 730: /* Collect information. */
1.9 kristaps 731:
1.116 schwarze 732: if ((date = pnode_takefirst(root, NODE_PUBDATE)) == NULL)
733: date = pnode_takefirst(root, NODE_DATE);
734:
735: name = vol = NULL;
1.119 schwarze 736: if ((refmeta = pnode_findfirst(root, NODE_REFMETA)) != NULL) {
737: TAILQ_FOREACH_SAFE(nc, &refmeta->childq, child, nn) {
1.116 schwarze 738: switch (nc->node) {
739: case NODE_REFENTRYTITLE:
740: name = nc;
741: break;
742: case NODE_MANVOLNUM:
743: vol = nc;
744: break;
745: default:
1.119 schwarze 746: continue;
1.116 schwarze 747: }
1.119 schwarze 748: TAILQ_REMOVE(&refmeta->childq, nc, child);
1.116 schwarze 749: }
750: }
751:
752: if (pnode_findfirst(root, NODE_REFNAMEDIV) == NULL &&
753: ((nc = pnode_findfirst(root, NODE_BOOKINFO)) != NULL ||
754: (nc = pnode_findfirst(root, NODE_REFENTRYINFO)) != NULL))
755: descr = pnode_takefirst(nc, NODE_TITLE);
756: else
757: descr = NULL;
758:
759: /* Print prologue. */
760:
761: if (date == NULL)
762: macro_line(f, "Dd $Mdocdate" "$");
763: else
764: macro_nodeline(f, "Dd", date, 0);
765:
766: macro_open(f, "Dt");
767: if (name == NULL) {
768: sname = pnode_getattr_raw(root, ATTRKEY_ID, "UNKNOWN");
769: macro_addarg(f, sname, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
770: } else
771: macro_addnode(f, name, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
772: if (vol == NULL)
1.101 schwarze 773: macro_addarg(f, "1", ARG_SPACE);
1.116 schwarze 774: else
775: macro_addnode(f, vol, ARG_SPACE | ARG_SINGLE);
776:
1.101 schwarze 777: macro_line(f, "Os");
1.116 schwarze 778:
779: if (descr != NULL) {
780: macro_line(f, "Sh NAME");
781: if (name == NULL)
782: macro_argline(f, "Nm", sname);
783: else
784: macro_nodeline(f, "Nm", name, ARG_SINGLE);
785: macro_nodeline(f, "Nd", descr, 0);
786: }
787:
788: /* Clean up. */
789:
790: pnode_unlink(date);
1.119 schwarze 791: pnode_unlink(name);
792: pnode_unlink(vol);
1.116 schwarze 793: pnode_unlink(descr);
1.130 schwarze 794: f->parastate = PARA_HAVE;
1.7 kristaps 795: }
796:
1.119 schwarze 797: static void
798: pnode_printrefentry(struct format *f, struct pnode *n)
799: {
800: struct pnode *info, *meta, *nc, *title;
801: struct pnode *match, *later;
802:
803: /* Collect nodes that remained behind when writing the prologue. */
804:
805: meta = NULL;
806: info = pnode_takefirst(n, NODE_BOOKINFO);
807: if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) {
808: pnode_unlink(info);
809: info = NULL;
810: }
811: if (info == NULL) {
812: info = pnode_takefirst(n, NODE_REFENTRYINFO);
813: if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) {
814: pnode_unlink(info);
815: info = NULL;
816: }
1.134 schwarze 817: if (info == NULL)
818: info = pnode_takefirst(n, NODE_INFO);
1.119 schwarze 819: meta = pnode_takefirst(n, NODE_REFMETA);
820: if (meta != NULL && TAILQ_FIRST(&meta->childq) == NULL) {
821: pnode_unlink(meta);
822: meta = NULL;
823: }
824: }
825: if (info == NULL && meta == NULL)
826: return;
827:
828: /*
829: * Find the best place to put this information.
830: * Use the last existing AUTHORS node, if any.
831: * Otherwise, put it behind all standard sections that
832: * conventionally precede AUTHORS, and also behind any
833: * non-standard sections that follow the last of these,
834: * but before the next standard section.
835: */
836:
837: match = later = NULL;
838: TAILQ_FOREACH(nc, &n->childq, child) {
839: switch (nc->node) {
840: case NODE_REFENTRY:
841: case NODE_REFNAMEDIV:
842: case NODE_REFSYNOPSISDIV:
843: case NODE_PREFACE:
844: later = NULL;
845: continue;
846: case NODE_APPENDIX:
847: case NODE_INDEX:
848: if (later == NULL)
849: later = nc;
850: continue;
851: default:
852: break;
853: }
854: if ((title = pnode_findfirst(nc, NODE_TITLE)) == NULL ||
855: (title = TAILQ_FIRST(&title->childq)) == NULL ||
856: title->node != NODE_TEXT)
857: continue;
858: if (strcasecmp(title->b, "AUTHORS") == 0 ||
859: strcasecmp(title->b, "AUTHOR") == 0)
860: match = nc;
861: else if (strcasecmp(title->b, "NAME") == 0 ||
862: strcasecmp(title->b, "SYNOPSIS") == 0 ||
863: strcasecmp(title->b, "DESCRIPTION") == 0 ||
864: strcasecmp(title->b, "RETURN VALUES") == 0 ||
865: strcasecmp(title->b, "ENVIRONMENT") == 0 ||
866: strcasecmp(title->b, "FILES") == 0 ||
867: strcasecmp(title->b, "EXIT STATUS") == 0 ||
868: strcasecmp(title->b, "EXAMPLES") == 0 ||
869: strcasecmp(title->b, "DIAGNOSTICS") == 0 ||
870: strcasecmp(title->b, "ERRORS") == 0 ||
871: strcasecmp(title->b, "SEE ALSO") == 0 ||
872: strcasecmp(title->b, "STANDARDS") == 0 ||
873: strcasecmp(title->b, "HISTORY") == 0)
874: later = NULL;
875: else if ((strcasecmp(title->b, "CAVEATS") == 0 ||
876: strcasecmp(title->b, "BUGS") == 0) &&
877: later == NULL)
878: later = nc;
879: }
880:
881: /*
882: * If no AUTHORS section was found, create one from scratch,
883: * and insert that at the place selected earlier.
884: */
885:
886: if (match == NULL) {
887: if ((match = calloc(1, sizeof(*match))) == NULL) {
888: perror(NULL);
889: exit(1);
890: }
891: match->node = NODE_SECTION;
892: match->spc = 1;
893: match->parent = n;
894: TAILQ_INIT(&match->childq);
895: TAILQ_INIT(&match->attrq);
896: if ((nc = pnode_alloc(match)) == NULL) {
897: perror(NULL);
898: exit(1);
899: }
900: nc->node = NODE_TITLE;
901: nc->spc = 1;
902: if ((nc = pnode_alloc(nc)) == NULL) {
903: perror(NULL);
904: exit(1);
905: }
906: nc->node = NODE_TEXT;
907: if ((nc->b = strdup("AUTHORS")) == NULL) {
908: perror(NULL);
909: exit(1);
910: }
911: nc->spc = 1;
912: if (later == NULL)
913: TAILQ_INSERT_TAIL(&n->childq, match, child);
914: else
915: TAILQ_INSERT_BEFORE(later, match, child);
916: }
917:
918: /*
919: * Dump the stuff excised at the beginning
920: * into this AUTHORS section.
921: */
922:
923: if (info != NULL)
924: TAILQ_INSERT_TAIL(&match->childq, info, child);
925: if (meta != NULL)
926: TAILQ_INSERT_TAIL(&match->childq, meta, child);
927: }
928:
1.42 kristaps 929: /*
930: * We can have multiple <term> elements within a <varlistentry>, which
931: * we should comma-separate as list headers.
932: */
1.13 kristaps 933: static void
1.101 schwarze 934: pnode_printvarlistentry(struct format *f, struct pnode *n)
1.13 kristaps 935: {
1.133 schwarze 936: struct pnode *nc, *nn, *ncc;
937: int comma;
1.13 kristaps 938:
1.101 schwarze 939: macro_open(f, "It");
1.130 schwarze 940: f->parastate = PARA_HAVE;
1.103 schwarze 941: f->flags |= FMT_IMPL;
1.133 schwarze 942: comma = -1;
1.108 schwarze 943: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.101 schwarze 944: if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
1.69 schwarze 945: continue;
1.133 schwarze 946: if (comma != -1) {
1.108 schwarze 947: switch (f->linestate) {
948: case LINE_NEW:
949: break;
950: case LINE_TEXT:
951: print_text(f, ",", 0);
952: break;
953: case LINE_MACRO:
1.133 schwarze 954: macro_addarg(f, ",", comma);
1.108 schwarze 955: break;
956: }
957: }
1.130 schwarze 958: f->parastate = PARA_HAVE;
1.133 schwarze 959: comma = (ncc = TAILQ_FIRST(&nc->childq)) == NULL ||
960: pnode_class(ncc->node) == CLASS_TEXT ? 0 : ARG_SPACE;
1.101 schwarze 961: pnode_print(f, nc);
1.108 schwarze 962: pnode_unlink(nc);
1.69 schwarze 963: }
1.101 schwarze 964: macro_close(f);
1.130 schwarze 965: f->parastate = PARA_HAVE;
1.108 schwarze 966: while ((nc = TAILQ_FIRST(&n->childq)) != NULL) {
967: pnode_print(f, nc);
968: pnode_unlink(nc);
969: }
970: macro_close(f);
1.130 schwarze 971: f->parastate = PARA_HAVE;
1.71 schwarze 972: }
973:
974: static void
1.101 schwarze 975: pnode_printtitle(struct format *f, struct pnode *n)
1.71 schwarze 976: {
1.101 schwarze 977: struct pnode *nc, *nn;
1.71 schwarze 978:
1.101 schwarze 979: TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
980: if (nc->node == NODE_TITLE) {
1.130 schwarze 981: if (f->parastate == PARA_MID)
982: f->parastate = PARA_WANT;
983: macro_nodeline(f, "Sy", nc, 0);
1.101 schwarze 984: pnode_unlink(nc);
1.71 schwarze 985: }
986: }
1.13 kristaps 987: }
988:
989: static void
1.101 schwarze 990: pnode_printrow(struct format *f, struct pnode *n)
1.25 kristaps 991: {
1.101 schwarze 992: struct pnode *nc;
1.25 kristaps 993:
1.101 schwarze 994: macro_line(f, "Bl -dash -compact");
995: TAILQ_FOREACH(nc, &n->childq, child) {
996: macro_line(f, "It");
997: pnode_print(f, nc);
1.25 kristaps 998: }
1.101 schwarze 999: macro_line(f, "El");
1000: pnode_unlink(n);
1.25 kristaps 1001: }
1002:
1003: static void
1.83 schwarze 1004: pnode_printtgroup1(struct format *f, struct pnode *n)
1.16 kristaps 1005: {
1.82 schwarze 1006: struct pnode *nc;
1007:
1.83 schwarze 1008: macro_line(f, "Bl -bullet -compact");
1.82 schwarze 1009: while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
1.83 schwarze 1010: macro_line(f, "It");
1.130 schwarze 1011: f->parastate = PARA_HAVE;
1.83 schwarze 1012: pnode_print(f, nc);
1.130 schwarze 1013: f->parastate = PARA_HAVE;
1.82 schwarze 1014: pnode_unlink(nc);
1015: }
1.83 schwarze 1016: macro_line(f, "El");
1017: pnode_unlinksub(n);
1018: }
1019:
1020: static void
1021: pnode_printtgroup2(struct format *f, struct pnode *n)
1022: {
1023: struct pnode *nr, *ne;
1024:
1.130 schwarze 1025: f->parastate = PARA_HAVE;
1.83 schwarze 1026: macro_line(f, "Bl -tag -width Ds");
1027: while ((nr = pnode_findfirst(n, NODE_ROW)) != NULL) {
1028: if ((ne = pnode_findfirst(n, NODE_ENTRY)) == NULL)
1029: break;
1030: macro_open(f, "It");
1.103 schwarze 1031: f->flags |= FMT_IMPL;
1.130 schwarze 1032: f->parastate = PARA_HAVE;
1.83 schwarze 1033: pnode_print(f, ne);
1.84 schwarze 1034: macro_close(f);
1.83 schwarze 1035: pnode_unlink(ne);
1.130 schwarze 1036: f->parastate = PARA_HAVE;
1.83 schwarze 1037: pnode_print(f, nr);
1.130 schwarze 1038: f->parastate = PARA_HAVE;
1.83 schwarze 1039: pnode_unlink(nr);
1040: }
1041: macro_line(f, "El");
1.135 schwarze 1042: f->parastate = PARA_WANT;
1.82 schwarze 1043: pnode_unlinksub(n);
1044: }
1045:
1046: static void
1.83 schwarze 1047: pnode_printtgroup(struct format *f, struct pnode *n)
1.82 schwarze 1048: {
1049: struct pnode *nc;
1050:
1051: switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
1052: case 1:
1.83 schwarze 1053: pnode_printtgroup1(f, n);
1054: return;
1055: case 2:
1056: pnode_printtgroup2(f, n);
1.82 schwarze 1057: return;
1058: default:
1059: break;
1060: }
1.16 kristaps 1061:
1.130 schwarze 1062: f->parastate = PARA_HAVE;
1.83 schwarze 1063: macro_line(f, "Bl -ohang");
1.82 schwarze 1064: while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
1.83 schwarze 1065: macro_line(f, "It Table Row");
1066: pnode_printrow(f, nc);
1.25 kristaps 1067: }
1.83 schwarze 1068: macro_line(f, "El");
1.135 schwarze 1069: f->parastate = PARA_WANT;
1.82 schwarze 1070: pnode_unlinksub(n);
1.25 kristaps 1071: }
1072:
1073: static void
1.101 schwarze 1074: pnode_printlist(struct format *f, struct pnode *n)
1.25 kristaps 1075: {
1.101 schwarze 1076: struct pnode *nc;
1.16 kristaps 1077:
1.101 schwarze 1078: pnode_printtitle(f, n);
1.130 schwarze 1079: f->parastate = PARA_HAVE;
1.101 schwarze 1080: macro_argline(f, "Bl",
1081: n->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1082: TAILQ_FOREACH(nc, &n->childq, child) {
1083: macro_line(f, "It");
1.130 schwarze 1084: f->parastate = PARA_HAVE;
1.101 schwarze 1085: pnode_print(f, nc);
1.130 schwarze 1086: f->parastate = PARA_HAVE;
1.16 kristaps 1087: }
1.101 schwarze 1088: macro_line(f, "El");
1.135 schwarze 1089: f->parastate = PARA_WANT;
1.101 schwarze 1090: pnode_unlinksub(n);
1.16 kristaps 1091: }
1092:
1093: static void
1.101 schwarze 1094: pnode_printvariablelist(struct format *f, struct pnode *n)
1.13 kristaps 1095: {
1.101 schwarze 1096: struct pnode *nc;
1.13 kristaps 1097:
1.101 schwarze 1098: pnode_printtitle(f, n);
1.130 schwarze 1099: f->parastate = PARA_HAVE;
1.101 schwarze 1100: macro_line(f, "Bl -tag -width Ds");
1101: TAILQ_FOREACH(nc, &n->childq, child) {
1102: if (nc->node == NODE_VARLISTENTRY)
1103: pnode_printvarlistentry(f, nc);
1.69 schwarze 1104: else
1.101 schwarze 1105: macro_nodeline(f, "It", nc, 0);
1.69 schwarze 1106: }
1.101 schwarze 1107: macro_line(f, "El");
1.135 schwarze 1108: f->parastate = PARA_WANT;
1.101 schwarze 1109: pnode_unlinksub(n);
1.13 kristaps 1110: }
1111:
1.1 kristaps 1112: /*
1113: * Print a parsed node (or ignore it--whatever).
1114: * This is a recursive function.
1.23 kristaps 1115: * FIXME: if we're in a literal context (<screen> or <programlisting> or
1116: * whatever), don't print inline macros.
1.1 kristaps 1117: */
1118: static void
1.101 schwarze 1119: pnode_print(struct format *f, struct pnode *n)
1.1 kristaps 1120: {
1.102 schwarze 1121: struct pnode *nc, *nn;
1.112 schwarze 1122: int was_impl;
1.1 kristaps 1123:
1.101 schwarze 1124: if (n == NULL)
1.1 kristaps 1125: return;
1126:
1.112 schwarze 1127: was_impl = f->flags & FMT_IMPL;
1.103 schwarze 1128: if (n->spc)
1129: f->flags &= ~FMT_NOSPC;
1130: else
1131: f->flags |= FMT_NOSPC;
1.1 kristaps 1132:
1.101 schwarze 1133: switch (n->node) {
1.62 schwarze 1134: case NODE_ARG:
1.101 schwarze 1135: pnode_printarg(f, n);
1.4 kristaps 1136: break;
1.62 schwarze 1137: case NODE_AUTHOR:
1.101 schwarze 1138: pnode_printauthor(f, n);
1.50 schwarze 1139: break;
1.62 schwarze 1140: case NODE_AUTHORGROUP:
1.101 schwarze 1141: macro_line(f, "An -split");
1.50 schwarze 1142: break;
1.98 schwarze 1143: case NODE_BLOCKQUOTE:
1.130 schwarze 1144: f->parastate = PARA_HAVE;
1.101 schwarze 1145: macro_line(f, "Bd -ragged -offset indent");
1.130 schwarze 1146: f->parastate = PARA_HAVE;
1.98 schwarze 1147: break;
1.62 schwarze 1148: case NODE_CITEREFENTRY:
1.101 schwarze 1149: pnode_printciterefentry(f, n);
1.1 kristaps 1150: break;
1.68 schwarze 1151: case NODE_CITETITLE:
1.101 schwarze 1152: macro_open(f, "%T");
1.68 schwarze 1153: break;
1.62 schwarze 1154: case NODE_COMMAND:
1.101 schwarze 1155: macro_open(f, "Nm");
1.13 kristaps 1156: break;
1.62 schwarze 1157: case NODE_CONSTANT:
1.101 schwarze 1158: macro_open(f, "Dv");
1.33 kristaps 1159: break;
1.121 schwarze 1160: case NODE_COPYRIGHT:
1161: print_text(f, "Copyright", ARG_SPACE);
1162: fputs(" \\(co", stdout);
1163: break;
1.62 schwarze 1164: case NODE_EDITOR:
1.101 schwarze 1165: print_text(f, "editor:", ARG_SPACE);
1.121 schwarze 1166: pnode_printauthor(f, n);
1.50 schwarze 1167: break;
1.65 schwarze 1168: case NODE_EMAIL:
1.115 schwarze 1169: if (was_impl)
1170: macro_open(f, "Ao Mt");
1171: else {
1172: macro_open(f, "Aq Mt");
1173: f->flags |= FMT_IMPL;
1174: }
1.65 schwarze 1175: break;
1.62 schwarze 1176: case NODE_EMPHASIS:
1177: case NODE_FIRSTTERM:
1.99 schwarze 1178: case NODE_GLOSSTERM:
1.123 schwarze 1179: if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1180: pnode_class(nc->node) < CLASS_LINE)
1181: macro_open(f, "Em");
1.130 schwarze 1182: if (n->node == NODE_GLOSSTERM)
1183: f->parastate = PARA_HAVE;
1.1 kristaps 1184: break;
1.62 schwarze 1185: case NODE_ENVAR:
1.101 schwarze 1186: macro_open(f, "Ev");
1.79 schwarze 1187: break;
1.90 schwarze 1188: case NODE_ERRORNAME:
1.101 schwarze 1189: macro_open(f, "Er");
1.90 schwarze 1190: break;
1.62 schwarze 1191: case NODE_FILENAME:
1.101 schwarze 1192: macro_open(f, "Pa");
1.17 kristaps 1193: break;
1.125 schwarze 1194: case NODE_FOOTNOTE:
1195: macro_line(f, "Bo");
1.130 schwarze 1196: f->parastate = PARA_HAVE;
1.125 schwarze 1197: break;
1.62 schwarze 1198: case NODE_FUNCTION:
1.101 schwarze 1199: macro_open(f, "Fn");
1.3 kristaps 1200: break;
1.62 schwarze 1201: case NODE_FUNCPROTOTYPE:
1.101 schwarze 1202: pnode_printfuncprototype(f, n);
1.3 kristaps 1203: break;
1.62 schwarze 1204: case NODE_FUNCSYNOPSISINFO:
1.101 schwarze 1205: macro_open(f, "Fd");
1.126 schwarze 1206: break;
1207: case NODE_IMAGEDATA:
1208: pnode_printimagedata(f, n);
1.16 kristaps 1209: break;
1.62 schwarze 1210: case NODE_INFORMALEQUATION:
1.130 schwarze 1211: f->parastate = PARA_HAVE;
1.111 schwarze 1212: macro_line(f, "Bd -ragged -offset indent");
1.130 schwarze 1213: f->parastate = PARA_HAVE;
1.111 schwarze 1214: /* FALLTHROUGH */
1215: case NODE_INLINEEQUATION:
1.101 schwarze 1216: macro_line(f, "EQ");
1.43 kristaps 1217: break;
1.62 schwarze 1218: case NODE_ITEMIZEDLIST:
1.101 schwarze 1219: pnode_printlist(f, n);
1.24 kristaps 1220: break;
1.62 schwarze 1221: case NODE_GROUP:
1.101 schwarze 1222: pnode_printgroup(f, n);
1.10 kristaps 1223: break;
1.67 schwarze 1224: case NODE_KEYSYM:
1.131 schwarze 1225: case NODE_PRODUCTNAME:
1.101 schwarze 1226: macro_open(f, "Sy");
1.67 schwarze 1227: break;
1.62 schwarze 1228: case NODE_LINK:
1.101 schwarze 1229: pnode_printlink(f, n);
1.96 schwarze 1230: break;
1.62 schwarze 1231: case NODE_LITERAL:
1.122 schwarze 1232: if (n->parent != NULL && n->parent->node == NODE_QUOTE)
1233: macro_open(f, "Li");
1234: else if (was_impl)
1235: macro_open(f, "So Li");
1.112 schwarze 1236: else {
1237: macro_open(f, "Ql");
1238: f->flags |= FMT_IMPL;
1239: }
1.19 kristaps 1240: break;
1.62 schwarze 1241: case NODE_LITERALLAYOUT:
1.101 schwarze 1242: macro_close(f);
1.130 schwarze 1243: f->parastate = PARA_HAVE;
1.101 schwarze 1244: macro_argline(f, "Bd", pnode_getattr(n, ATTRKEY_CLASS) ==
1.66 schwarze 1245: ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.130 schwarze 1246: f->parastate = PARA_HAVE;
1.60 schwarze 1247: break;
1.106 schwarze 1248: case NODE_MARKUP:
1249: macro_open(f, "Ic");
1250: break;
1.62 schwarze 1251: case NODE_MML_MFENCED:
1.101 schwarze 1252: pnode_printmathfenced(f, n);
1.40 kristaps 1253: break;
1.62 schwarze 1254: case NODE_MML_MROW:
1255: case NODE_MML_MI:
1256: case NODE_MML_MN:
1257: case NODE_MML_MO:
1.101 schwarze 1258: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 1259: break;
1260: fputs(" { ", stdout);
1.40 kristaps 1261: break;
1.62 schwarze 1262: case NODE_MML_MFRAC:
1263: case NODE_MML_MSUB:
1264: case NODE_MML_MSUP:
1.101 schwarze 1265: pnode_printmath(f, n);
1.128 schwarze 1266: break;
1267: case NODE_OLINK:
1268: pnode_printolink(f, n);
1.40 kristaps 1269: break;
1.62 schwarze 1270: case NODE_OPTION:
1.123 schwarze 1271: if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1272: pnode_class(nc->node) < CLASS_LINE)
1273: macro_open(f, "Fl");
1.1 kristaps 1274: break;
1.62 schwarze 1275: case NODE_ORDEREDLIST:
1.101 schwarze 1276: pnode_printlist(f, n);
1.25 kristaps 1277: break;
1.62 schwarze 1278: case NODE_PARA:
1.130 schwarze 1279: if (f->parastate == PARA_MID)
1280: f->parastate = PARA_WANT;
1.3 kristaps 1281: break;
1.87 schwarze 1282: case NODE_PARAMDEF:
1.62 schwarze 1283: case NODE_PARAMETER:
1.104 schwarze 1284: /* More often, these appear inside NODE_FUNCPROTOTYPE. */
1285: macro_open(f, "Fa");
1286: macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
1.101 schwarze 1287: pnode_unlinksub(n);
1.1 kristaps 1288: break;
1.62 schwarze 1289: case NODE_QUOTE:
1.127 schwarze 1290: if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1291: nc->node == NODE_FILENAME &&
1292: TAILQ_NEXT(nc, child) == NULL) {
1293: if (n->spc)
1294: nc->spc = 1;
1295: } else if (was_impl)
1.112 schwarze 1296: macro_open(f, "Do");
1297: else {
1298: macro_open(f, "Dq");
1299: f->flags |= FMT_IMPL;
1300: }
1.28 kristaps 1301: break;
1.62 schwarze 1302: case NODE_PROGRAMLISTING:
1303: case NODE_SCREEN:
1.88 schwarze 1304: case NODE_SYNOPSIS:
1.130 schwarze 1305: f->parastate = PARA_HAVE;
1.101 schwarze 1306: macro_line(f, "Bd -literal");
1.130 schwarze 1307: f->parastate = PARA_HAVE;
1.118 schwarze 1308: break;
1309: case NODE_SYSTEMITEM:
1310: pnode_printsystemitem(f, n);
1.15 kristaps 1311: break;
1.119 schwarze 1312: case NODE_REFENTRY:
1313: pnode_printrefentry(f, n);
1.1 kristaps 1314: break;
1.62 schwarze 1315: case NODE_REFNAME:
1.110 schwarze 1316: /* More often, these appear inside NODE_REFNAMEDIV. */
1.101 schwarze 1317: macro_open(f, "Nm");
1.10 kristaps 1318: break;
1.62 schwarze 1319: case NODE_REFNAMEDIV:
1.110 schwarze 1320: pnode_printrefnamediv(f, n);
1.1 kristaps 1321: break;
1.62 schwarze 1322: case NODE_REFPURPOSE:
1.101 schwarze 1323: macro_open(f, "Nd");
1.10 kristaps 1324: break;
1.62 schwarze 1325: case NODE_REFSYNOPSISDIV:
1.101 schwarze 1326: pnode_printrefsynopsisdiv(f, n);
1.1 kristaps 1327: break;
1.62 schwarze 1328: case NODE_PREFACE:
1329: case NODE_SECTION:
1.121 schwarze 1330: case NODE_SIMPLESECT:
1.100 schwarze 1331: case NODE_APPENDIX:
1332: case NODE_LEGALNOTICE:
1.62 schwarze 1333: case NODE_NOTE:
1334: case NODE_TIP:
1335: case NODE_CAUTION:
1336: case NODE_WARNING:
1.119 schwarze 1337: pnode_printsection(f, n);
1.1 kristaps 1338: break;
1.62 schwarze 1339: case NODE_REPLACEABLE:
1.101 schwarze 1340: macro_open(f, "Ar");
1.13 kristaps 1341: break;
1.62 schwarze 1342: case NODE_SBR:
1.130 schwarze 1343: if (f->parastate == PARA_MID)
1344: macro_line(f, "br");
1.124 schwarze 1345: break;
1346: case NODE_SUBSCRIPT:
1347: if (f->linestate == LINE_MACRO)
1348: macro_addarg(f, "_", 0);
1349: else
1350: print_text(f, "_", 0);
1351: if ((nc = TAILQ_FIRST(&n->childq)) != NULL)
1352: nc->spc = 0;
1353: break;
1354: case NODE_SUPERSCRIPT:
1355: fputs("\\(ha", stdout);
1356: if ((nc = TAILQ_FIRST(&n->childq)) != NULL)
1357: nc->spc = 0;
1.25 kristaps 1358: break;
1.62 schwarze 1359: case NODE_TEXT:
1.93 schwarze 1360: case NODE_ESCAPE:
1.101 schwarze 1361: pnode_printtext(f, n);
1.1 kristaps 1362: break;
1.82 schwarze 1363: case NODE_TGROUP:
1.101 schwarze 1364: pnode_printtgroup(f, n);
1.82 schwarze 1365: break;
1.62 schwarze 1366: case NODE_TITLE:
1.121 schwarze 1367: case NODE_SUBTITLE:
1.130 schwarze 1368: if (f->parastate == PARA_MID)
1369: f->parastate = PARA_WANT;
1.101 schwarze 1370: macro_nodeline(f, "Sy", n, 0);
1371: pnode_unlinksub(n);
1.50 schwarze 1372: break;
1.62 schwarze 1373: case NODE_TYPE:
1.101 schwarze 1374: macro_open(f, "Vt");
1.39 kristaps 1375: break;
1.62 schwarze 1376: case NODE_VARIABLELIST:
1.101 schwarze 1377: pnode_printvariablelist(f, n);
1.13 kristaps 1378: break;
1.62 schwarze 1379: case NODE_VARNAME:
1.101 schwarze 1380: macro_open(f, "Va");
1.132 schwarze 1381: break;
1382: case NODE_VOID:
1383: print_text(f, "void", ARG_SPACE);
1.129 schwarze 1384: break;
1385: case NODE_XREF:
1386: pnode_printxref(f, n);
1.23 kristaps 1387: break;
1.1 kristaps 1388: default:
1389: break;
1390: }
1391:
1.102 schwarze 1392: TAILQ_FOREACH(nc, &n->childq, child)
1393: pnode_print(f, nc);
1.1 kristaps 1394:
1.101 schwarze 1395: switch (n->node) {
1.115 schwarze 1396: case NODE_EMAIL:
1397: if (was_impl) {
1398: f->flags &= ~FMT_NOSPC;
1399: macro_open(f, "Ac");
1400: } else
1401: f->flags &= ~FMT_IMPL;
1402: break;
1.103 schwarze 1403: case NODE_ESCAPE:
1404: case NODE_TERM:
1405: case NODE_TEXT:
1406: /* Accept more arguments to the previous macro. */
1407: return;
1.125 schwarze 1408: case NODE_FOOTNOTE:
1.130 schwarze 1409: f->parastate = PARA_HAVE;
1.125 schwarze 1410: macro_line(f, "Bc");
1411: break;
1.130 schwarze 1412: case NODE_GLOSSTERM:
1413: f->parastate = PARA_HAVE;
1414: break;
1.62 schwarze 1415: case NODE_INFORMALEQUATION:
1.101 schwarze 1416: macro_line(f, "EN");
1.111 schwarze 1417: macro_line(f, "Ed");
1.40 kristaps 1418: break;
1.62 schwarze 1419: case NODE_INLINEEQUATION:
1.111 schwarze 1420: macro_line(f, "EN");
1.43 kristaps 1421: break;
1.112 schwarze 1422: case NODE_LITERAL:
1.122 schwarze 1423: if (n->parent != NULL && n->parent->node == NODE_QUOTE)
1424: /* nothing */;
1425: else if (was_impl) {
1.115 schwarze 1426: f->flags &= ~FMT_NOSPC;
1.112 schwarze 1427: macro_open(f, "Sc");
1.115 schwarze 1428: } else
1.112 schwarze 1429: f->flags &= ~FMT_IMPL;
1430: break;
1.91 schwarze 1431: case NODE_MEMBER:
1.102 schwarze 1432: if ((nn = TAILQ_NEXT(n, child)) != NULL &&
1433: nn->node != NODE_MEMBER)
1434: nn = NULL;
1.101 schwarze 1435: switch (f->linestate) {
1.91 schwarze 1436: case LINE_TEXT:
1.102 schwarze 1437: if (nn != NULL)
1.101 schwarze 1438: print_text(f, ",", 0);
1.91 schwarze 1439: break;
1440: case LINE_MACRO:
1.102 schwarze 1441: if (nn != NULL)
1.101 schwarze 1442: macro_addarg(f, ",", ARG_SPACE);
1443: macro_close(f);
1.91 schwarze 1444: break;
1445: case LINE_NEW:
1446: break;
1447: }
1448: break;
1.62 schwarze 1449: case NODE_MML_MROW:
1450: case NODE_MML_MI:
1451: case NODE_MML_MN:
1452: case NODE_MML_MO:
1.101 schwarze 1453: if (TAILQ_EMPTY(&n->childq))
1.43 kristaps 1454: break;
1455: fputs(" } ", stdout);
1.40 kristaps 1456: break;
1.130 schwarze 1457: case NODE_PARA:
1458: if (f->parastate == PARA_MID)
1459: f->parastate = PARA_WANT;
1460: break;
1.62 schwarze 1461: case NODE_QUOTE:
1.127 schwarze 1462: if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1463: nc->node == NODE_FILENAME &&
1464: TAILQ_NEXT(nc, child) == NULL)
1465: /* nothing */;
1466: else if (was_impl) {
1.115 schwarze 1467: f->flags &= ~FMT_NOSPC;
1.112 schwarze 1468: macro_open(f, "Dc");
1.115 schwarze 1469: } else
1.112 schwarze 1470: f->flags &= ~FMT_IMPL;
1.52 schwarze 1471: break;
1.62 schwarze 1472: case NODE_PREFACE:
1473: case NODE_SECTION:
1.100 schwarze 1474: case NODE_APPENDIX:
1475: case NODE_LEGALNOTICE:
1.62 schwarze 1476: case NODE_NOTE:
1477: case NODE_TIP:
1478: case NODE_CAUTION:
1479: case NODE_WARNING:
1.101 schwarze 1480: f->level--;
1.12 kristaps 1481: break;
1.98 schwarze 1482: case NODE_BLOCKQUOTE:
1.62 schwarze 1483: case NODE_LITERALLAYOUT:
1484: case NODE_PROGRAMLISTING:
1485: case NODE_SCREEN:
1.88 schwarze 1486: case NODE_SYNOPSIS:
1.130 schwarze 1487: f->parastate = PARA_HAVE;
1.101 schwarze 1488: macro_line(f, "Ed");
1.135 schwarze 1489: f->parastate = PARA_WANT;
1.50 schwarze 1490: break;
1.130 schwarze 1491: case NODE_TITLE:
1492: case NODE_SUBTITLE:
1493: f->parastate = PARA_WANT;
1494: break;
1.1 kristaps 1495: default:
1496: break;
1497: }
1.103 schwarze 1498: f->flags &= ~FMT_ARG;
1.1 kristaps 1499: }
1500:
1.74 schwarze 1501: void
1.114 schwarze 1502: ptree_print_mdoc(struct ptree *tree)
1.74 schwarze 1503: {
1504: struct format formatter;
1.1 kristaps 1505:
1.74 schwarze 1506: formatter.level = 0;
1507: formatter.linestate = LINE_NEW;
1.130 schwarze 1508: formatter.parastate = PARA_HAVE;
1.116 schwarze 1509: pnode_printprologue(&formatter, tree->root);
1.74 schwarze 1510: pnode_print(&formatter, tree->root);
1511: if (formatter.linestate != LINE_NEW)
1512: putchar('\n');
1.1 kristaps 1513: }
CVSweb