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