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