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