Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.31
1.31 ! kristaps 1: /* $Id: docbook2mdoc.c,v 1.30 2014/04/02 12:12:41 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/queue.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
21: #include <expat.h>
22: #include <fcntl.h>
23: #include <getopt.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
1.7 kristaps 27: #include <unistd.h>
1.1 kristaps 28:
1.13 kristaps 29: #include "extern.h"
1.12 kristaps 30:
31: /*
1.1 kristaps 32: * Global parse state.
33: * Keep this as simple and small as possible.
34: */
35: struct parse {
1.12 kristaps 36: XML_Parser xml;
1.1 kristaps 37: enum nodeid node; /* current (NODE_ROOT if pre-tree) */
1.12 kristaps 38: const char *fname; /* filename */
1.1 kristaps 39: int stop; /* should we stop now? */
40: struct pnode *root; /* root of parse tree */
41: struct pnode *cur; /* current node in tree */
1.8 kristaps 42: char *b; /* nil-terminated buffer for pre-print */
43: size_t bsz; /* current length of b */
44: size_t mbsz; /* max bsz allocation */
1.10 kristaps 45: int newln; /* output: are we on a fresh line */
1.1 kristaps 46: };
47:
48: struct node {
1.8 kristaps 49: const char *name; /* docbook element name */
1.1 kristaps 50: unsigned int flags;
51: #define NODE_IGNTEXT 1 /* ignore all contained text */
52: };
53:
54: TAILQ_HEAD(pnodeq, pnode);
1.12 kristaps 55: TAILQ_HEAD(pattrq, pattr);
56:
57: struct pattr {
58: enum attrkey key;
59: enum attrval val;
60: char *rawval;
61: TAILQ_ENTRY(pattr) child;
62: };
1.1 kristaps 63:
64: struct pnode {
65: enum nodeid node; /* node type */
66: char *b; /* binary data buffer */
67: size_t bsz; /* data buffer size */
68: struct pnode *parent; /* parent (or NULL if top) */
69: struct pnodeq childq; /* queue of children */
1.12 kristaps 70: struct pattrq attrq; /* attributes of node */
1.1 kristaps 71: TAILQ_ENTRY(pnode) child;
72: };
73:
1.12 kristaps 74: static const char *attrkeys[ATTRKEY__MAX] = {
75: "choice",
76: "id",
77: "rep"
78: };
79:
80: static const char *attrvals[ATTRVAL__MAX] = {
81: "norepeat",
82: "opt",
83: "plain",
84: "repeat",
85: "req"
86: };
87:
1.1 kristaps 88: static const struct node nodes[NODE__MAX] = {
89: { NULL, 0 },
1.21 kristaps 90: { "acronym", 0 },
1.30 kristaps 91: { "anchor", NODE_IGNTEXT },
1.27 kristaps 92: { "application", 0 },
1.4 kristaps 93: { "arg", 0 },
1.29 kristaps 94: { "caution", NODE_IGNTEXT },
1.1 kristaps 95: { "citerefentry", NODE_IGNTEXT },
1.4 kristaps 96: { "cmdsynopsis", NODE_IGNTEXT },
1.1 kristaps 97: { "code", 0 },
1.4 kristaps 98: { "command", 0 },
1.15 kristaps 99: { "date", 0 },
1.13 kristaps 100: { "emphasis", 0 },
1.25 kristaps 101: { "entry", 0 },
1.21 kristaps 102: { "envar", 0 },
1.17 kristaps 103: { "filename", 0 },
1.3 kristaps 104: { "funcdef", 0 },
105: { "funcprototype", NODE_IGNTEXT },
1.1 kristaps 106: { "funcsynopsis", NODE_IGNTEXT },
107: { "funcsynopsisinfo", 0 },
1.3 kristaps 108: { "function", 0 },
1.16 kristaps 109: { "itemizedlist", NODE_IGNTEXT },
1.24 kristaps 110: { "group", NODE_IGNTEXT },
1.14 kristaps 111: { "link", 0 },
1.13 kristaps 112: { "listitem", NODE_IGNTEXT },
1.19 kristaps 113: { "literal", 0 },
1.1 kristaps 114: { "manvolnum", 0 },
1.29 kristaps 115: { "note", NODE_IGNTEXT },
1.4 kristaps 116: { "option", 0 },
1.21 kristaps 117: { "orderedlist", NODE_IGNTEXT },
1.1 kristaps 118: { "para", 0 },
1.3 kristaps 119: { "paramdef", 0 },
120: { "parameter", 0 },
1.1 kristaps 121: { "programlisting", 0 },
1.22 kristaps 122: { "prompt", 0 },
1.28 kristaps 123: { "quote", 0 },
1.1 kristaps 124: { "refclass", NODE_IGNTEXT },
125: { "refdescriptor", NODE_IGNTEXT },
126: { "refentry", NODE_IGNTEXT },
1.15 kristaps 127: { "refentryinfo", NODE_IGNTEXT },
1.1 kristaps 128: { "refentrytitle", 0 },
129: { "refmeta", NODE_IGNTEXT },
130: { "refmiscinfo", NODE_IGNTEXT },
131: { "refname", 0 },
132: { "refnamediv", NODE_IGNTEXT },
133: { "refpurpose", 0 },
1.20 kristaps 134: { "refsect1", NODE_IGNTEXT },
135: { "refsect2", NODE_IGNTEXT },
1.29 kristaps 136: { "refsect3", NODE_IGNTEXT },
137: { "refsection", NODE_IGNTEXT },
1.1 kristaps 138: { "refsynopsisdiv", NODE_IGNTEXT },
1.13 kristaps 139: { "replaceable", 0 },
1.25 kristaps 140: { "row", NODE_IGNTEXT },
1.19 kristaps 141: { "sbr", NODE_IGNTEXT },
1.22 kristaps 142: { "screen", NODE_IGNTEXT },
1.30 kristaps 143: { "sgmltag", 0 },
1.8 kristaps 144: { "structname", 0 },
1.1 kristaps 145: { "synopsis", 0 },
1.25 kristaps 146: { "table", NODE_IGNTEXT },
147: { "tbody", NODE_IGNTEXT },
1.13 kristaps 148: { "term", 0 },
1.1 kristaps 149: { NULL, 0 },
1.25 kristaps 150: { "tfoot", NODE_IGNTEXT },
151: { "tgroup", NODE_IGNTEXT },
152: { "thead", NODE_IGNTEXT },
1.29 kristaps 153: { "tip", NODE_IGNTEXT },
1.1 kristaps 154: { "title", 0 },
1.14 kristaps 155: { "ulink", 0 },
1.23 kristaps 156: { "userinput", 0 },
1.13 kristaps 157: { "variablelist", NODE_IGNTEXT },
158: { "varlistentry", NODE_IGNTEXT },
1.26 kristaps 159: { "varname", 0 },
1.29 kristaps 160: { "warning", NODE_IGNTEXT },
1.30 kristaps 161: { "wordasword", 0 },
1.1 kristaps 162: };
163:
1.10 kristaps 164: static void
165: pnode_print(struct parse *p, struct pnode *pn);
166:
1.8 kristaps 167: /*
168: * Process a stream of characters.
169: * We store text as nodes in and of themselves.
170: * If a text node is already open, append to it.
171: * If it's not open, open one under the current context.
172: */
1.1 kristaps 173: static void
174: xml_char(void *arg, const XML_Char *p, int sz)
175: {
176: struct parse *ps = arg;
177: struct pnode *dat;
1.4 kristaps 178: int i;
1.1 kristaps 179:
180: /* Stopped or no tree yet. */
181: if (ps->stop || NODE_ROOT == ps->node)
182: return;
183:
184: /* Not supposed to be collecting text. */
185: assert(NULL != ps->cur);
186: if (NODE_IGNTEXT & nodes[ps->node].flags)
187: return;
188:
189: /*
190: * Are we in the midst of processing text?
191: * If we're not processing text right now, then create a text
192: * node for doing so.
1.4 kristaps 193: * However, don't do so unless we have some non-whitespace to
1.10 kristaps 194: * process: strip out all leading whitespace to be sure.
1.1 kristaps 195: */
196: if (NODE_TEXT != ps->node) {
1.4 kristaps 197: for (i = 0; i < sz; i++)
198: if ( ! isspace((int)p[i]))
199: break;
200: if (i == sz)
201: return;
1.10 kristaps 202: p += i;
203: sz -= i;
1.1 kristaps 204: dat = calloc(1, sizeof(struct pnode));
205: if (NULL == dat) {
206: perror(NULL);
207: exit(EXIT_FAILURE);
208: }
209:
210: dat->node = ps->node = NODE_TEXT;
211: dat->parent = ps->cur;
212: TAILQ_INIT(&dat->childq);
1.12 kristaps 213: TAILQ_INIT(&dat->attrq);
1.1 kristaps 214: TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
215: ps->cur = dat;
216: assert(NULL != ps->root);
217: }
218:
219: /* Append to current buffer. */
220: assert(sz >= 0);
221: ps->cur->b = realloc(ps->cur->b,
222: ps->cur->bsz + (size_t)sz);
223: if (NULL == ps->cur->b) {
224: perror(NULL);
225: exit(EXIT_FAILURE);
226: }
227: memcpy(ps->cur->b + ps->cur->bsz, p, sz);
228: ps->cur->bsz += (size_t)sz;
229: }
230:
1.10 kristaps 231: static void
232: pnode_trim(struct pnode *pn)
233: {
234:
235: assert(NODE_TEXT == pn->node);
236: for ( ; pn->bsz > 0; pn->bsz--)
237: if ( ! isspace((int)pn->b[pn->bsz - 1]))
238: break;
239: }
240:
1.1 kristaps 241: /*
242: * Begin an element.
243: * First, look for the element.
244: * If we don't find it and we're not parsing, keep going.
1.8 kristaps 245: * If we don't find it and we're parsing, puke and exit.
1.1 kristaps 246: * If we find it but we're not parsing yet (i.e., it's not a refentry
247: * and thus out of context), keep going.
1.8 kristaps 248: * If we find it and we're at the root and already have a tree, puke and
249: * exit (FIXME: I don't think this is right?).
250: * If we find it but we're parsing a text node, close out the text node,
251: * return to its parent, and keep going.
1.1 kristaps 252: * Make sure that the element is in the right context.
253: * Lastly, put the node onto our parse tree and continue.
254: */
255: static void
256: xml_elem_start(void *arg, const XML_Char *name, const XML_Char **atts)
257: {
1.12 kristaps 258: struct parse *ps = arg;
259: enum nodeid node;
260: enum attrkey key;
261: enum attrval val;
262: struct pnode *dat;
263: struct pattr *pattr;
264: const XML_Char **att;
1.1 kristaps 265:
266: if (ps->stop)
267: return;
268:
269: /* Close out text node, if applicable... */
270: if (NODE_TEXT == ps->node) {
271: assert(NULL != ps->cur);
1.10 kristaps 272: pnode_trim(ps->cur);
1.1 kristaps 273: ps->cur = ps->cur->parent;
274: assert(NULL != ps->cur);
275: ps->node = ps->cur->node;
276: }
277:
278: for (node = 0; node < NODE__MAX; node++)
279: if (NULL == nodes[node].name)
280: continue;
281: else if (0 == strcmp(nodes[node].name, name))
282: break;
283:
284: if (NODE__MAX == node && NODE_ROOT == ps->node) {
285: return;
286: } else if (NODE__MAX == node) {
1.12 kristaps 287: fprintf(stderr, "%s:%zu:%zu: unknown node \"%s\"\n",
288: ps->fname, XML_GetCurrentLineNumber(ps->xml),
289: XML_GetCurrentColumnNumber(ps->xml), name);
1.1 kristaps 290: ps->stop = 1;
291: return;
292: } else if (NODE_ROOT == ps->node && NULL != ps->root) {
1.12 kristaps 293: fprintf(stderr, "%s:%zu:%zu: multiple refentries\n",
294: ps->fname, XML_GetCurrentLineNumber(ps->xml),
295: XML_GetCurrentColumnNumber(ps->xml));
1.1 kristaps 296: ps->stop = 1;
297: return;
298: } else if (NODE_ROOT == ps->node && NODE_REFENTRY != node) {
299: return;
300: } else if ( ! isparent(node, ps->node)) {
1.13 kristaps 301: fprintf(stderr, "%s:%zu:%zu: bad parent \"%s\" "
302: "of node \"%s\"\n",
1.12 kristaps 303: ps->fname, XML_GetCurrentLineNumber(ps->xml),
304: XML_GetCurrentColumnNumber(ps->xml),
305: NULL == nodes[ps->node].name ?
1.13 kristaps 306: "(none)" : nodes[ps->node].name,
307: NULL == nodes[node].name ?
308: "(none)" : nodes[node].name);
1.1 kristaps 309: ps->stop = 1;
310: return;
311: }
312:
313: if (NULL == (dat = calloc(1, sizeof(struct pnode)))) {
314: perror(NULL);
315: exit(EXIT_FAILURE);
316: }
317:
318: dat->node = ps->node = node;
319: dat->parent = ps->cur;
320: TAILQ_INIT(&dat->childq);
1.12 kristaps 321: TAILQ_INIT(&dat->attrq);
1.1 kristaps 322:
323: if (NULL != ps->cur)
324: TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
325:
326: ps->cur = dat;
327: if (NULL == ps->root)
328: ps->root = dat;
1.12 kristaps 329:
330: /*
331: * Process attributes.
332: */
333: for (att = atts; NULL != *att; att += 2) {
334: for (key = 0; key < ATTRKEY__MAX; key++)
335: if (0 == strcmp(*att, attrkeys[key]))
336: break;
337: if (ATTRKEY__MAX == key) {
1.31 ! kristaps 338: fprintf(stderr, "%s:%zu:%zu: warning: "
! 339: "unknown attribute \"%s\"\n",
! 340: ps->fname,
1.12 kristaps 341: XML_GetCurrentLineNumber(ps->xml),
342: XML_GetCurrentColumnNumber(ps->xml),
343: *att);
344: continue;
345: } else if ( ! isattrkey(node, key)) {
1.31 ! kristaps 346: fprintf(stderr, "%s:%zu:%zu: warning: "
! 347: "bad attribute \"%s\"\n",
! 348: ps->fname,
1.12 kristaps 349: XML_GetCurrentLineNumber(ps->xml),
350: XML_GetCurrentColumnNumber(ps->xml),
351: *att);
352: continue;
353: }
354: for (val = 0; val < ATTRVAL__MAX; val++)
355: if (0 == strcmp(*(att + 1), attrvals[val]))
356: break;
357: if (ATTRVAL__MAX != val && ! isattrval(key, val)) {
1.31 ! kristaps 358: fprintf(stderr, "%s:%zu:%zu: warning: "
! 359: "bad attribute value \"%s\"\n",
! 360: ps->fname,
1.12 kristaps 361: XML_GetCurrentLineNumber(ps->xml),
362: XML_GetCurrentColumnNumber(ps->xml),
363: *(att + 1));
364: continue;
365: }
366: pattr = calloc(1, sizeof(struct pattr));
367: pattr->key = key;
368: pattr->val = val;
369: if (ATTRVAL__MAX == val)
370: pattr->rawval = strdup(*(att + 1));
371: TAILQ_INSERT_TAIL(&dat->attrq, pattr, child);
372: }
373:
1.1 kristaps 374: }
375:
376: /*
377: * Roll up the parse tree.
1.8 kristaps 378: * If we're at a text node, roll that one up first.
1.1 kristaps 379: * If we hit the root, then assign ourselves as the NODE_ROOT.
380: */
381: static void
382: xml_elem_end(void *arg, const XML_Char *name)
383: {
384: struct parse *ps = arg;
385:
386: if (ps->stop || NODE_ROOT == ps->node)
387: return;
388:
389: /* Close out text node, if applicable... */
390: if (NODE_TEXT == ps->node) {
391: assert(NULL != ps->cur);
1.10 kristaps 392: pnode_trim(ps->cur);
1.1 kristaps 393: ps->cur = ps->cur->parent;
394: assert(NULL != ps->cur);
395: ps->node = ps->cur->node;
396: }
397:
398: if (NULL == (ps->cur = ps->cur->parent))
399: ps->node = NODE_ROOT;
400: else
401: ps->node = ps->cur->node;
402: }
403:
1.8 kristaps 404: /*
405: * Recursively free a node (NULL is ok).
406: */
1.1 kristaps 407: static void
408: pnode_free(struct pnode *pn)
409: {
410: struct pnode *pp;
1.12 kristaps 411: struct pattr *ap;
1.1 kristaps 412:
413: if (NULL == pn)
414: return;
415:
416: while (NULL != (pp = TAILQ_FIRST(&pn->childq))) {
417: TAILQ_REMOVE(&pn->childq, pp, child);
418: pnode_free(pp);
419: }
420:
1.12 kristaps 421: while (NULL != (ap = TAILQ_FIRST(&pn->attrq))) {
422: TAILQ_REMOVE(&pn->attrq, ap, child);
423: free(ap->rawval);
424: free(ap);
425: }
426:
1.1 kristaps 427: free(pn->b);
428: free(pn);
429: }
430:
1.8 kristaps 431: /*
432: * Unlink a node from its parent and pnode_free() it.
433: */
1.1 kristaps 434: static void
435: pnode_unlink(struct pnode *pn)
436: {
437:
438: if (NULL != pn->parent)
439: TAILQ_REMOVE(&pn->parent->childq, pn, child);
440: pnode_free(pn);
441: }
442:
1.8 kristaps 443: /*
444: * Unlink all children of a node and pnode_free() them.
445: */
1.1 kristaps 446: static void
1.4 kristaps 447: pnode_unlinksub(struct pnode *pn)
448: {
449:
450: while ( ! TAILQ_EMPTY(&pn->childq))
451: pnode_unlink(TAILQ_FIRST(&pn->childq));
452: }
453:
1.8 kristaps 454: /*
455: * Reset the lookaside buffer.
456: */
1.4 kristaps 457: static void
1.1 kristaps 458: bufclear(struct parse *p)
459: {
460:
461: p->b[p->bsz = 0] = '\0';
462: }
463:
1.8 kristaps 464: /*
465: * Append NODE_TEXT contents to the current buffer, reallocating its
466: * size if necessary.
467: * The buffer is ALWAYS nil-terminated.
468: */
1.1 kristaps 469: static void
470: bufappend(struct parse *p, struct pnode *pn)
471: {
472:
473: assert(NODE_TEXT == pn->node);
474: if (p->bsz + pn->bsz + 1 > p->mbsz) {
475: p->mbsz = p->bsz + pn->bsz + 1;
476: if (NULL == (p->b = realloc(p->b, p->mbsz))) {
477: perror(NULL);
478: exit(EXIT_FAILURE);
479: }
480: }
481: memcpy(p->b + p->bsz, pn->b, pn->bsz);
482: p->bsz += pn->bsz;
483: p->b[p->bsz] = '\0';
484: }
485:
1.8 kristaps 486: /*
487: * Recursively append all NODE_TEXT nodes to the buffer.
488: * This descends into non-text nodes, but doesn't do anything beyond
489: * them.
490: * In other words, this is a recursive text grok.
491: */
1.3 kristaps 492: static void
493: bufappend_r(struct parse *p, struct pnode *pn)
494: {
495: struct pnode *pp;
496:
497: if (NODE_TEXT == pn->node)
498: bufappend(p, pn);
499: TAILQ_FOREACH(pp, &pn->childq, child)
500: bufappend_r(p, pp);
501: }
502:
1.25 kristaps 503: /*
504: * Recursively search and return the first instance of "node".
505: */
506: static struct pnode *
507: pnode_findfirst(struct pnode *pn, enum nodeid node)
508: {
509: struct pnode *pp, *res;
510:
511: res = NULL;
512: TAILQ_FOREACH(pp, &pn->childq, child) {
513: res = pp->node == node ? pp :
514: pnode_findfirst(pp, node);
515: if (NULL != res)
516: break;
517: }
518:
519: return(res);
520: }
521:
1.12 kristaps 522: #define MACROLINE_NORM 0
523: #define MACROLINE_UPPER 1
1.1 kristaps 524: /*
1.8 kristaps 525: * Recursively print text presumably on a macro line.
1.1 kristaps 526: * Convert all whitespace to regular spaces.
527: */
528: static void
1.12 kristaps 529: pnode_printmacrolinetext(struct parse *p, struct pnode *pn, int fl)
1.1 kristaps 530: {
531: char *cp;
532:
1.13 kristaps 533: if (0 == p->newln)
534: putchar(' ');
535:
1.1 kristaps 536: bufclear(p);
1.3 kristaps 537: bufappend_r(p, pn);
1.1 kristaps 538:
539: /* Convert all space to spaces. */
540: for (cp = p->b; '\0' != *cp; cp++)
541: if (isspace((int)*cp))
542: *cp = ' ';
543:
544: for (cp = p->b; isspace((int)*cp); cp++)
1.4 kristaps 545: /* Spin past whitespace (XXX: necessary?) */ ;
1.1 kristaps 546: for ( ; '\0' != *cp; cp++) {
547: /* Escape us if we look like a macro. */
548: if ((cp == p->b || ' ' == *(cp - 1)) &&
549: isupper((int)*cp) &&
550: '\0' != *(cp + 1) &&
551: islower((int)*(cp + 1)) &&
552: ('\0' == *(cp + 2) ||
553: ' ' == *(cp + 2) ||
554: (islower((int)*(cp + 2)) &&
555: ('\0' == *(cp + 3) ||
556: ' ' == *(cp + 3)))))
557: fputs("\\&", stdout);
1.12 kristaps 558: if (MACROLINE_UPPER & fl)
559: putchar(toupper((int)*cp));
560: else
561: putchar((int)*cp);
1.1 kristaps 562: /* If we're a character escape, escape us. */
563: if ('\\' == *cp)
564: putchar('e');
565: }
566: }
567:
1.12 kristaps 568: static void
569: pnode_printmacrolinepart(struct parse *p, struct pnode *pn)
570: {
571:
572: pnode_printmacrolinetext(p, pn, 0);
573: }
574:
1.1 kristaps 575: /*
576: * Just pnode_printmacrolinepart() but with a newline.
577: * If no text, just the newline.
578: */
579: static void
580: pnode_printmacroline(struct parse *p, struct pnode *pn)
581: {
582:
1.13 kristaps 583: assert(0 == p->newln);
1.12 kristaps 584: pnode_printmacrolinetext(p, pn, 0);
1.1 kristaps 585: putchar('\n');
1.13 kristaps 586: p->newln = 1;
1.1 kristaps 587: }
588:
1.10 kristaps 589: static void
590: pnode_printmopen(struct parse *p)
591: {
592: if (p->newln) {
593: putchar('.');
594: p->newln = 0;
595: } else
596: putchar(' ');
597: }
598:
599: static void
600: pnode_printmclose(struct parse *p, int sv)
601: {
602:
603: if (sv && ! p->newln) {
604: putchar('\n');
605: p->newln = 1;
606: }
607: }
608:
1.8 kristaps 609: /*
1.10 kristaps 610: * If the SYNOPSIS macro has a superfluous title, kill it.
1.8 kristaps 611: */
1.1 kristaps 612: static void
1.6 kristaps 613: pnode_printrefsynopsisdiv(struct parse *p, struct pnode *pn)
614: {
615: struct pnode *pp;
616:
1.10 kristaps 617: TAILQ_FOREACH(pp, &pn->childq, child)
1.6 kristaps 618: if (NODE_TITLE == pp->node) {
619: pnode_unlink(pp);
1.10 kristaps 620: return;
1.6 kristaps 621: }
622: }
623:
1.8 kristaps 624: /*
625: * Start a hopefully-named `Sh' section.
626: */
1.6 kristaps 627: static void
1.1 kristaps 628: pnode_printrefsect(struct parse *p, struct pnode *pn)
629: {
630: struct pnode *pp;
631:
632: TAILQ_FOREACH(pp, &pn->childq, child)
633: if (NODE_TITLE == pp->node)
634: break;
635:
1.29 kristaps 636: switch (pn->node) {
637: case (NODE_REFSECT1):
1.20 kristaps 638: fputs(".Sh", stdout);
1.29 kristaps 639: break;
640: case (NODE_REFSECT2):
1.20 kristaps 641: fputs(".Ss", stdout);
1.29 kristaps 642: break;
643: case (NODE_REFSECT3):
644: puts(".Pp");
645: fputs(".Sy", stdout);
646: break;
647: case (NODE_NOTE):
648: /* FALLTHROUGH */
649: case (NODE_REFSECTION):
650: /* FALLTHROUGH */
651: case (NODE_TIP):
652: /* FALLTHROUGH */
653: case (NODE_CAUTION):
654: /* FALLTHROUGH */
655: case (NODE_WARNING):
656: puts(".Pp");
657: if (NULL == pp)
658: return;
659: fputs(".Em", stdout);
660: break;
661: default:
662: break;
663: }
1.20 kristaps 664:
1.13 kristaps 665: p->newln = 0;
1.4 kristaps 666:
1.5 kristaps 667: if (NULL != pp) {
1.20 kristaps 668: pnode_printmacrolinetext(p, pp,
669: NODE_REFSECT1 == pn->node ?
670: MACROLINE_UPPER : 0);
1.18 kristaps 671: pnode_printmclose(p, 1);
1.5 kristaps 672: pnode_unlink(pp);
1.13 kristaps 673: } else {
1.29 kristaps 674: puts(NODE_REFSECT1 == pn->node ?
675: "UNKNOWN" : "unknown");
1.13 kristaps 676: p->newln = 1;
677: }
1.1 kristaps 678: }
679:
1.8 kristaps 680: /*
681: * Start a reference, extracting the title and volume.
682: */
1.1 kristaps 683: static void
684: pnode_printciterefentry(struct parse *p, struct pnode *pn)
685: {
686: struct pnode *pp, *title, *manvol;
687:
688: title = manvol = NULL;
1.13 kristaps 689: assert(p->newln);
1.1 kristaps 690: TAILQ_FOREACH(pp, &pn->childq, child)
691: if (NODE_MANVOLNUM == pp->node)
692: manvol = pp;
693: else if (NODE_REFENTRYTITLE == pp->node)
694: title = pp;
695:
1.13 kristaps 696: fputs(".Xr", stdout);
697: p->newln = 0;
1.4 kristaps 698:
1.1 kristaps 699: if (NULL != title) {
700: pnode_printmacrolinepart(p, title);
701: } else
1.13 kristaps 702: fputs(" unknown ", stdout);
1.4 kristaps 703:
1.13 kristaps 704: if (NULL == manvol) {
705: puts(" 1");
706: p->newln = 1;
707: } else
1.1 kristaps 708: pnode_printmacroline(p, manvol);
709: }
710:
711: static void
712: pnode_printrefmeta(struct parse *p, struct pnode *pn)
713: {
714: struct pnode *pp, *title, *manvol;
715:
716: title = manvol = NULL;
1.13 kristaps 717: assert(p->newln);
1.1 kristaps 718: TAILQ_FOREACH(pp, &pn->childq, child)
719: if (NODE_MANVOLNUM == pp->node)
720: manvol = pp;
721: else if (NODE_REFENTRYTITLE == pp->node)
722: title = pp;
723:
1.2 kristaps 724: puts(".Dd $Mdocdate" "$");
1.13 kristaps 725: fputs(".Dt", stdout);
726: p->newln = 0;
1.1 kristaps 727:
1.13 kristaps 728: if (NULL != title)
1.12 kristaps 729: pnode_printmacrolinetext(p, title, MACROLINE_UPPER);
1.13 kristaps 730: else
731: fputs(" UNKNOWN ", stdout);
732:
733: if (NULL == manvol) {
734: puts(" 1");
735: p->newln = 1;
1.1 kristaps 736: } else
737: pnode_printmacroline(p, manvol);
738:
739: puts(".Os");
740: }
741:
1.3 kristaps 742: static void
743: pnode_printfuncdef(struct parse *p, struct pnode *pn)
744: {
745: struct pnode *pp, *ftype, *func;
746:
1.13 kristaps 747: assert(p->newln);
1.3 kristaps 748: ftype = func = NULL;
749: TAILQ_FOREACH(pp, &pn->childq, child)
750: if (NODE_TEXT == pp->node)
751: ftype = pp;
752: else if (NODE_FUNCTION == pp->node)
753: func = pp;
754:
755: if (NULL != ftype) {
1.13 kristaps 756: fputs(".Ft", stdout);
757: p->newln = 0;
1.3 kristaps 758: pnode_printmacroline(p, ftype);
759: }
760:
761: if (NULL != func) {
1.13 kristaps 762: fputs(".Fo", stdout);
763: p->newln = 0;
1.3 kristaps 764: pnode_printmacroline(p, func);
1.13 kristaps 765: } else {
1.3 kristaps 766: puts(".Fo UNKNOWN");
1.13 kristaps 767: p->newln = 1;
768: }
1.3 kristaps 769: }
770:
771: static void
772: pnode_printparamdef(struct parse *p, struct pnode *pn)
773: {
774: struct pnode *pp, *ptype, *param;
775:
1.13 kristaps 776: assert(p->newln);
1.3 kristaps 777: ptype = param = NULL;
778: TAILQ_FOREACH(pp, &pn->childq, child)
779: if (NODE_TEXT == pp->node)
780: ptype = pp;
781: else if (NODE_PARAMETER == pp->node)
782: param = pp;
783:
784: fputs(".Fa \"", stdout);
1.13 kristaps 785: p->newln = 0;
1.3 kristaps 786: if (NULL != ptype) {
787: pnode_printmacrolinepart(p, ptype);
788: putchar(' ');
789: }
790:
791: if (NULL != param)
792: pnode_printmacrolinepart(p, param);
793:
794: puts("\"");
1.13 kristaps 795: p->newln = 1;
1.3 kristaps 796: }
797:
798: static void
799: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
800: {
801: struct pnode *pp, *fdef;
802:
1.13 kristaps 803: assert(p->newln);
1.3 kristaps 804: TAILQ_FOREACH(fdef, &pn->childq, child)
805: if (NODE_FUNCDEF == fdef->node)
806: break;
807:
1.4 kristaps 808: if (NULL != fdef)
1.3 kristaps 809: pnode_printfuncdef(p, fdef);
1.4 kristaps 810: else
1.3 kristaps 811: puts(".Fo UNKNOWN");
812:
1.4 kristaps 813: TAILQ_FOREACH(pp, &pn->childq, child)
1.3 kristaps 814: if (NODE_PARAMDEF == pp->node)
815: pnode_printparamdef(p, pp);
816:
817: puts(".Fc");
1.13 kristaps 818: p->newln = 1;
1.3 kristaps 819: }
820:
1.10 kristaps 821: /*
822: * The <arg> element is more complicated than it should be because text
823: * nodes are treated like ".Ar foo", but non-text nodes need to be
824: * re-sent into the printer (i.e., without the preceding ".Ar").
1.12 kristaps 825: * This also handles the case of "repetition" (or in other words, the
826: * ellipsis following an argument) and optionality.
1.10 kristaps 827: */
1.4 kristaps 828: static void
1.10 kristaps 829: pnode_printarg(struct parse *p, struct pnode *pn)
1.4 kristaps 830: {
831: struct pnode *pp;
1.12 kristaps 832: struct pattr *ap;
833: int isop, isrep;
834:
835: isop = 1;
836: isrep = 0;
837: TAILQ_FOREACH(ap, &pn->attrq, child)
838: if (ATTRKEY_CHOICE == ap->key &&
839: (ATTRVAL_PLAIN == ap->val ||
840: ATTRVAL_REQ == ap->val))
841: isop = 0;
842: else if (ATTRKEY_REP == ap->key &&
843: (ATTRVAL_REPEAT == ap->val))
844: isrep = 1;
845:
846: if (isop) {
847: pnode_printmopen(p);
1.13 kristaps 848: fputs("Op", stdout);
1.12 kristaps 849: }
1.4 kristaps 850:
1.10 kristaps 851: TAILQ_FOREACH(pp, &pn->childq, child) {
852: if (NODE_TEXT == pp->node) {
853: pnode_printmopen(p);
1.13 kristaps 854: fputs("Ar", stdout);
1.10 kristaps 855: }
856: pnode_print(p, pp);
1.12 kristaps 857: if (NODE_TEXT == pp->node && isrep)
858: fputs("...", stdout);
1.10 kristaps 859: }
1.4 kristaps 860: }
861:
1.24 kristaps 862: static void
863: pnode_printgroup(struct parse *p, struct pnode *pn)
864: {
865: struct pnode *pp, *np;
866: struct pattr *ap;
867: int isop, sv;
868:
869: isop = 1;
870: TAILQ_FOREACH(ap, &pn->attrq, child)
871: if (ATTRKEY_CHOICE == ap->key &&
872: (ATTRVAL_PLAIN == ap->val ||
873: ATTRVAL_REQ == ap->val)) {
874: isop = 0;
875: break;
876: }
877:
878: /*
879: * Make sure we're on a macro line.
880: * This will prevent pnode_print() for putting us on a
881: * subsequent line.
882: */
883: sv = p->newln;
884: pnode_printmopen(p);
885: if (isop)
886: fputs("Op", stdout);
887: else if (sv)
888: fputs("No", stdout);
889:
890: /*
891: * Keep on printing text separated by the vertical bar as long
892: * as we're within the same origin node as the group.
893: * This is kind of a nightmare.
894: * Eh, DocBook...
895: * FIXME: if there's a "Fl", we don't cut off the leading "-"
896: * like we do in pnode_print().
897: */
898: TAILQ_FOREACH(pp, &pn->childq, child) {
899: pnode_print(p, pp);
900: np = TAILQ_NEXT(pp, child);
901: while (NULL != np) {
902: if (pp->node != np->node)
903: break;
904: fputs(" |", stdout);
905: pnode_printmacrolinepart(p, np);
906: pp = np;
907: np = TAILQ_NEXT(np, child);
908: }
909: }
910:
911: pnode_printmclose(p, sv);
912: }
913:
1.7 kristaps 914: static void
915: pnode_printprologue(struct parse *p, struct pnode *pn)
916: {
917: struct pnode *pp;
918:
1.9 kristaps 919: pp = NULL == p->root ? NULL :
920: pnode_findfirst(p->root, NODE_REFMETA);
921:
922: if (NULL != pp) {
1.7 kristaps 923: pnode_printrefmeta(p, pp);
924: pnode_unlink(pp);
925: } else {
926: puts(".\\\" Supplying bogus prologue...");
927: puts(".Dd $Mdocdate" "$");
928: puts(".Dt UNKNOWN 1");
929: puts(".Os");
930: }
931: }
932:
1.13 kristaps 933: static void
934: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
935: {
936: struct pnode *pp;
937:
938: assert(p->newln);
939: TAILQ_FOREACH(pp, &pn->childq, child)
940: if (NODE_TERM == pp->node) {
941: fputs(".It", stdout);
942: p->newln = 0;
943: pnode_print(p, pp);
944: pnode_unlink(pp);
1.16 kristaps 945: pnode_printmclose(p, 1);
1.13 kristaps 946: return;
947: }
948:
949: puts(".It");
950: p->newln = 1;
951: }
952:
953: static void
1.25 kristaps 954: pnode_printrow(struct parse *p, struct pnode *pn)
955: {
956: struct pnode *pp;
957:
958: puts(".Bl -dash -compact");
959:
960: TAILQ_FOREACH(pp, &pn->childq, child) {
961: assert(p->newln);
962: puts(".It");
963: pnode_print(p, pp);
964: pnode_printmclose(p, 1);
965: }
966: assert(p->newln);
967: puts(".El");
968: }
969:
970: static void
971: pnode_printtable(struct parse *p, struct pnode *pn)
1.16 kristaps 972: {
973: struct pnode *pp;
974:
975: assert(p->newln);
976: TAILQ_FOREACH(pp, &pn->childq, child)
977: if (NODE_TITLE == pp->node) {
978: puts(".Pp");
979: pnode_print(p, pp);
980: pnode_unlink(pp);
981: }
1.25 kristaps 982: assert(p->newln);
983: puts(".Bl -ohang");
984: while (NULL != (pp = pnode_findfirst(pn, NODE_ROW))) {
985: puts(".It Table Row");
986: pnode_printrow(p, pp);
987: pnode_printmclose(p, 1);
988: pnode_unlink(pp);
989: }
990: assert(p->newln);
991: puts(".El");
992: }
993:
994: static void
995: pnode_printlist(struct parse *p, struct pnode *pn)
996: {
997: struct pnode *pp;
1.16 kristaps 998:
999: assert(p->newln);
1.25 kristaps 1000: TAILQ_FOREACH(pp, &pn->childq, child)
1001: if (NODE_TITLE == pp->node) {
1002: puts(".Pp");
1003: pnode_print(p, pp);
1004: pnode_unlink(pp);
1005: }
1006: assert(p->newln);
1.21 kristaps 1007:
1008: if (NODE_ORDEREDLIST == pn->node)
1009: puts(".Bl -enum");
1010: else
1011: puts(".Bl -item");
1012:
1.16 kristaps 1013: TAILQ_FOREACH(pp, &pn->childq, child) {
1014: assert(p->newln);
1015: puts(".It");
1016: pnode_print(p, pp);
1017: pnode_printmclose(p, 1);
1018: }
1019: assert(p->newln);
1020: puts(".El");
1021: }
1022:
1023: static void
1.13 kristaps 1024: pnode_printvariablelist(struct parse *p, struct pnode *pn)
1025: {
1026: struct pnode *pp;
1027:
1028: assert(p->newln);
1029: TAILQ_FOREACH(pp, &pn->childq, child)
1030: if (NODE_TITLE == pp->node) {
1031: puts(".Pp");
1032: pnode_print(p, pp);
1033: pnode_unlink(pp);
1034: }
1035:
1036: assert(p->newln);
1037: puts(".Bl -tag -width Ds");
1038: TAILQ_FOREACH(pp, &pn->childq, child)
1039: if (NODE_VARLISTENTRY != pp->node) {
1040: assert(p->newln);
1041: fputs(".It", stdout);
1042: pnode_printmacroline(p, pp);
1043: } else {
1044: assert(p->newln);
1045: pnode_print(p, pp);
1046: }
1047: assert(p->newln);
1048: puts(".El");
1049: }
1050:
1.1 kristaps 1051: /*
1052: * Print a parsed node (or ignore it--whatever).
1053: * This is a recursive function.
1.23 kristaps 1054: * FIXME: if we're in a literal context (<screen> or <programlisting> or
1055: * whatever), don't print inline macros.
1.1 kristaps 1056: */
1057: static void
1058: pnode_print(struct parse *p, struct pnode *pn)
1059: {
1060: struct pnode *pp;
1061: char *cp;
1.10 kristaps 1062: int last, sv;
1.1 kristaps 1063:
1064: if (NULL == pn)
1065: return;
1066:
1.10 kristaps 1067: sv = p->newln;
1.1 kristaps 1068:
1069: switch (pn->node) {
1.27 kristaps 1070: case (NODE_APPLICATION):
1071: pnode_printmopen(p);
1072: fputs("Nm", stdout);
1073: break;
1.30 kristaps 1074: case (NODE_ANCHOR):
1075: /* Don't print anything! */
1076: return;
1.4 kristaps 1077: case (NODE_ARG):
1.10 kristaps 1078: pnode_printarg(p, pn);
1.4 kristaps 1079: pnode_unlinksub(pn);
1080: break;
1.1 kristaps 1081: case (NODE_CITEREFENTRY):
1.10 kristaps 1082: assert(p->newln);
1.1 kristaps 1083: pnode_printciterefentry(p, pn);
1.4 kristaps 1084: pnode_unlinksub(pn);
1.1 kristaps 1085: break;
1086: case (NODE_CODE):
1.10 kristaps 1087: pnode_printmopen(p);
1.13 kristaps 1088: fputs("Li", stdout);
1.4 kristaps 1089: break;
1090: case (NODE_COMMAND):
1.10 kristaps 1091: pnode_printmopen(p);
1.13 kristaps 1092: fputs("Nm", stdout);
1093: break;
1094: case (NODE_EMPHASIS):
1095: pnode_printmopen(p);
1096: fputs("Em", stdout);
1.1 kristaps 1097: break;
1.21 kristaps 1098: case (NODE_ENVAR):
1099: pnode_printmopen(p);
1100: fputs("Ev", stdout);
1101: break;
1.17 kristaps 1102: case (NODE_FILENAME):
1103: pnode_printmopen(p);
1104: fputs("Pa", stdout);
1105: break;
1.3 kristaps 1106: case (NODE_FUNCTION):
1.10 kristaps 1107: pnode_printmopen(p);
1.13 kristaps 1108: fputs("Fn", stdout);
1.3 kristaps 1109: break;
1110: case (NODE_FUNCPROTOTYPE):
1.10 kristaps 1111: assert(p->newln);
1.3 kristaps 1112: pnode_printfuncprototype(p, pn);
1.4 kristaps 1113: pnode_unlinksub(pn);
1.3 kristaps 1114: break;
1.1 kristaps 1115: case (NODE_FUNCSYNOPSISINFO):
1.10 kristaps 1116: pnode_printmopen(p);
1.13 kristaps 1117: fputs("Fd", stdout);
1.16 kristaps 1118: break;
1119: case (NODE_ITEMIZEDLIST):
1120: assert(p->newln);
1.25 kristaps 1121: pnode_printlist(p, pn);
1122: pnode_unlinksub(pn);
1.24 kristaps 1123: break;
1124: case (NODE_GROUP):
1125: pnode_printgroup(p, pn);
1126: pnode_unlinksub(pn);
1.10 kristaps 1127: break;
1.19 kristaps 1128: case (NODE_LITERAL):
1129: pnode_printmopen(p);
1130: fputs("Li", stdout);
1131: break;
1.10 kristaps 1132: case (NODE_OPTION):
1133: pnode_printmopen(p);
1.13 kristaps 1134: fputs("Fl", stdout);
1.1 kristaps 1135: break;
1.25 kristaps 1136: case (NODE_ORDEREDLIST):
1137: assert(p->newln);
1138: pnode_printlist(p, pn);
1139: pnode_unlinksub(pn);
1140: break;
1.1 kristaps 1141: case (NODE_PARA):
1.10 kristaps 1142: assert(p->newln);
1.13 kristaps 1143: if (NULL != pn->parent &&
1144: NODE_LISTITEM == pn->parent->node)
1145: break;
1.1 kristaps 1146: puts(".Pp");
1.3 kristaps 1147: break;
1148: case (NODE_PARAMETER):
1.10 kristaps 1149: /* Suppress non-text children... */
1150: pnode_printmopen(p);
1151: fputs("Fa \"", stdout);
1.3 kristaps 1152: pnode_printmacrolinepart(p, pn);
1153: puts("\"");
1.4 kristaps 1154: pnode_unlinksub(pn);
1.1 kristaps 1155: break;
1.28 kristaps 1156: case (NODE_QUOTE):
1157: pnode_printmopen(p);
1158: fputs("Qo", stdout);
1159: break;
1.1 kristaps 1160: case (NODE_PROGRAMLISTING):
1.22 kristaps 1161: /* FALLTHROUGH */
1162: case (NODE_SCREEN):
1.10 kristaps 1163: assert(p->newln);
1.1 kristaps 1164: puts(".Bd -literal");
1.15 kristaps 1165: break;
1166: case (NODE_REFENTRYINFO):
1167: /* Suppress. */
1168: pnode_unlinksub(pn);
1.1 kristaps 1169: break;
1170: case (NODE_REFMETA):
1.7 kristaps 1171: abort();
1.1 kristaps 1172: break;
1173: case (NODE_REFNAME):
1.10 kristaps 1174: /* Suppress non-text children... */
1175: pnode_printmopen(p);
1.13 kristaps 1176: fputs("Nm", stdout);
1177: p->newln = 0;
1.10 kristaps 1178: pnode_printmacrolinepart(p, pn);
1.4 kristaps 1179: pnode_unlinksub(pn);
1.10 kristaps 1180: break;
1.1 kristaps 1181: case (NODE_REFNAMEDIV):
1.10 kristaps 1182: assert(p->newln);
1.1 kristaps 1183: puts(".Sh NAME");
1184: break;
1185: case (NODE_REFPURPOSE):
1.10 kristaps 1186: assert(p->newln);
1.13 kristaps 1187: pnode_printmopen(p);
1188: fputs("Nd", stdout);
1.10 kristaps 1189: break;
1.1 kristaps 1190: case (NODE_REFSYNOPSISDIV):
1.10 kristaps 1191: assert(p->newln);
1.6 kristaps 1192: pnode_printrefsynopsisdiv(p, pn);
1.10 kristaps 1193: puts(".Sh SYNOPSIS");
1.1 kristaps 1194: break;
1195: case (NODE_REFSECT1):
1.20 kristaps 1196: /* FALLTHROUGH */
1197: case (NODE_REFSECT2):
1.29 kristaps 1198: /* FALLTHROUGH */
1199: case (NODE_REFSECT3):
1200: /* FALLTHROUGH */
1201: case (NODE_REFSECTION):
1202: /* FALLTHROUGH */
1203: case (NODE_NOTE):
1204: /* FALLTHROUGH */
1205: case (NODE_TIP):
1206: /* FALLTHROUGH */
1207: case (NODE_CAUTION):
1208: /* FALLTHROUGH */
1209: case (NODE_WARNING):
1.10 kristaps 1210: assert(p->newln);
1.1 kristaps 1211: pnode_printrefsect(p, pn);
1212: break;
1.13 kristaps 1213: case (NODE_REPLACEABLE):
1214: pnode_printmopen(p);
1215: fputs("Ar", stdout);
1216: break;
1.19 kristaps 1217: case (NODE_SBR):
1218: assert(p->newln);
1219: puts(".br");
1220: break;
1.30 kristaps 1221: case (NODE_SGMLTAG):
1222: pnode_printmopen(p);
1223: fputs("Li", stdout);
1224: break;
1.8 kristaps 1225: case (NODE_STRUCTNAME):
1.10 kristaps 1226: pnode_printmopen(p);
1.13 kristaps 1227: fputs("Vt", stdout);
1.25 kristaps 1228: break;
1229: case (NODE_TABLE):
1230: assert(p->newln);
1231: pnode_printtable(p, pn);
1232: pnode_unlinksub(pn);
1.10 kristaps 1233: break;
1.1 kristaps 1234: case (NODE_TEXT):
1.13 kristaps 1235: if (0 == p->newln)
1236: putchar(' ');
1.1 kristaps 1237: bufclear(p);
1238: bufappend(p, pn);
1239: /*
1240: * Output all characters, squeezing out whitespace
1241: * between newlines.
1242: * XXX: all whitespace, including tabs (?).
1243: * Remember to escape control characters and escapes.
1244: */
1.10 kristaps 1245: assert(p->bsz);
1.20 kristaps 1246: cp = p->b;
1247: /*
1248: * There's often a superfluous "-" in its <option> tags
1249: * before the actual flags themselves.
1250: * "Fl" does this for us, so remove it.
1251: */
1252: if (NULL != pn->parent &&
1253: NODE_OPTION == pn->parent->node &&
1254: '-' == *cp)
1255: cp++;
1256: for (last = '\n'; '\0' != *cp; ) {
1.1 kristaps 1257: if ('\n' == last) {
1258: /* Consume all whitespace. */
1259: if (isspace((int)*cp)) {
1260: while (isspace((int)*cp))
1261: cp++;
1262: continue;
1263: } else if ('\'' == *cp || '.' == *cp)
1264: fputs("\\&", stdout);
1265: }
1266: putchar(last = *cp++);
1267: /* If we're a character escape, escape us. */
1268: if ('\\' == last)
1269: putchar('e');
1270: }
1.10 kristaps 1271: p->newln = 0;
1.1 kristaps 1272: break;
1.26 kristaps 1273: case (NODE_USERINPUT):
1274: pnode_printmopen(p);
1275: fputs("Li", stdout);
1276: break;
1.13 kristaps 1277: case (NODE_VARIABLELIST):
1278: assert(p->newln);
1279: pnode_printvariablelist(p, pn);
1280: pnode_unlinksub(pn);
1281: break;
1282: case (NODE_VARLISTENTRY):
1283: assert(p->newln);
1284: pnode_printvarlistentry(p, pn);
1285: break;
1.26 kristaps 1286: case (NODE_VARNAME):
1.23 kristaps 1287: pnode_printmopen(p);
1.26 kristaps 1288: fputs("Va", stdout);
1.23 kristaps 1289: break;
1.1 kristaps 1290: default:
1291: break;
1292: }
1293:
1294: TAILQ_FOREACH(pp, &pn->childq, child)
1295: pnode_print(p, pp);
1296:
1297: switch (pn->node) {
1.27 kristaps 1298: case (NODE_APPLICATION):
1.10 kristaps 1299: case (NODE_ARG):
1300: case (NODE_CODE):
1301: case (NODE_COMMAND):
1.13 kristaps 1302: case (NODE_EMPHASIS):
1.21 kristaps 1303: case (NODE_ENVAR):
1.17 kristaps 1304: case (NODE_FILENAME):
1.10 kristaps 1305: case (NODE_FUNCTION):
1306: case (NODE_FUNCSYNOPSISINFO):
1.19 kristaps 1307: case (NODE_LITERAL):
1.10 kristaps 1308: case (NODE_OPTION):
1309: case (NODE_PARAMETER):
1.13 kristaps 1310: case (NODE_REPLACEABLE):
1311: case (NODE_REFPURPOSE):
1.30 kristaps 1312: case (NODE_SGMLTAG):
1.10 kristaps 1313: case (NODE_STRUCTNAME):
1314: case (NODE_TEXT):
1.23 kristaps 1315: case (NODE_USERINPUT):
1.26 kristaps 1316: case (NODE_VARNAME):
1.28 kristaps 1317: pnode_printmclose(p, sv);
1318: break;
1319: case (NODE_QUOTE):
1320: pnode_printmclose(p, sv);
1321: sv = p->newln;
1322: pnode_printmopen(p);
1323: fputs("Qc", stdout);
1.10 kristaps 1324: pnode_printmclose(p, sv);
1325: break;
1.12 kristaps 1326: case (NODE_REFNAME):
1327: /*
1328: * If we're in the NAME macro and we have multiple
1329: * <refname> macros in sequence, then print out a
1330: * trailing comma before the newline.
1331: */
1332: if (NULL != pn->parent &&
1333: NODE_REFNAMEDIV == pn->parent->node &&
1334: NULL != TAILQ_NEXT(pn, child) &&
1335: NODE_REFNAME == TAILQ_NEXT(pn, child)->node)
1336: fputs(" ,", stdout);
1337: pnode_printmclose(p, sv);
1338: break;
1.1 kristaps 1339: case (NODE_PROGRAMLISTING):
1.22 kristaps 1340: /* FALLTHROUGH */
1341: case (NODE_SCREEN):
1.10 kristaps 1342: assert(p->newln);
1.1 kristaps 1343: puts(".Ed");
1.10 kristaps 1344: p->newln = 1;
1.1 kristaps 1345: break;
1346: default:
1347: break;
1348: }
1349: }
1350:
1351: /*
1352: * Loop around the read buffer until we've drained it of all data.
1353: * Invoke the parser context with each buffer fill.
1354: */
1355: static int
1356: readfile(XML_Parser xp, int fd,
1357: char *b, size_t bsz, const char *fn)
1358: {
1359: struct parse p;
1360: int rc;
1361: ssize_t ssz;
1362:
1363: memset(&p, 0, sizeof(struct parse));
1364:
1365: p.b = malloc(p.bsz = p.mbsz = 1024);
1.12 kristaps 1366: p.fname = fn;
1367: p.xml = xp;
1.1 kristaps 1368:
1369: XML_SetCharacterDataHandler(xp, xml_char);
1370: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
1371: XML_SetUserData(xp, &p);
1372:
1373: while ((ssz = read(fd, b, bsz)) >= 0) {
1374: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
1.30 kristaps 1375: fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
1376: XML_GetCurrentLineNumber(xp),
1377: XML_GetCurrentColumnNumber(xp),
1.1 kristaps 1378: XML_ErrorString
1379: (XML_GetErrorCode(xp)));
1380: else if ( ! p.stop && ssz > 0)
1381: continue;
1382: /*
1383: * Exit when we've read all or errors have occured
1384: * during the parse sequence.
1385: */
1.10 kristaps 1386: p.newln = 1;
1.7 kristaps 1387: pnode_printprologue(&p, p.root);
1.1 kristaps 1388: pnode_print(&p, p.root);
1389: pnode_free(p.root);
1390: free(p.b);
1391: return(0 != rc && ! p.stop);
1392: }
1393:
1394: /* Read error has occured. */
1395: perror(fn);
1396: pnode_free(p.root);
1397: free(p.b);
1398: return(0);
1399: }
1400:
1401: int
1402: main(int argc, char *argv[])
1403: {
1404: XML_Parser xp;
1405: const char *fname;
1406: char *buf;
1407: int fd, rc;
1408:
1409: fname = "-";
1410: xp = NULL;
1411: buf = NULL;
1412: rc = 0;
1413:
1414: if (-1 != getopt(argc, argv, ""))
1415: return(EXIT_FAILURE);
1416:
1417: argc -= optind;
1418: argv += optind;
1419:
1420: if (argc > 1)
1421: return(EXIT_FAILURE);
1422: else if (argc > 0)
1423: fname = argv[0];
1424:
1425: /* Read from stdin or a file. */
1426: fd = 0 == strcmp(fname, "-") ?
1427: STDIN_FILENO : open(fname, O_RDONLY, 0);
1428:
1429: /*
1430: * Open file for reading.
1431: * Allocate a read buffer.
1432: * Create the parser context.
1433: * Dive directly into the parse.
1434: */
1435: if (-1 == fd)
1436: perror(fname);
1437: else if (NULL == (buf = malloc(4096)))
1438: perror(NULL);
1439: else if (NULL == (xp = XML_ParserCreate(NULL)))
1440: perror(NULL);
1441: else if ( ! readfile(xp, fd, buf, 4096, fname))
1442: rc = 1;
1443:
1444: XML_ParserFree(xp);
1445: free(buf);
1446: if (STDIN_FILENO != fd)
1447: close(fd);
1448: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1449: }
CVSweb