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