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