Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.30
1.30 ! kristaps 1: /* $Id: docbook2mdoc.c,v 1.29 2014/04/02 10:59:07 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) {
338: fprintf(stderr, "%s:%zu:%zu: unknown "
339: "attribute \"%s\"\n", ps->fname,
340: XML_GetCurrentLineNumber(ps->xml),
341: XML_GetCurrentColumnNumber(ps->xml),
342: *att);
343: continue;
344: } else if ( ! isattrkey(node, key)) {
345: fprintf(stderr, "%s:%zu:%zu: bad "
346: "attribute \"%s\"\n", ps->fname,
347: XML_GetCurrentLineNumber(ps->xml),
348: XML_GetCurrentColumnNumber(ps->xml),
349: *att);
350: continue;
351: }
352: for (val = 0; val < ATTRVAL__MAX; val++)
353: if (0 == strcmp(*(att + 1), attrvals[val]))
354: break;
355: if (ATTRVAL__MAX != val && ! isattrval(key, val)) {
356: fprintf(stderr, "%s:%zu:%zu: bad "
357: "value \"%s\"\n", ps->fname,
358: XML_GetCurrentLineNumber(ps->xml),
359: XML_GetCurrentColumnNumber(ps->xml),
360: *(att + 1));
361: continue;
362: }
363: pattr = calloc(1, sizeof(struct pattr));
364: pattr->key = key;
365: pattr->val = val;
366: if (ATTRVAL__MAX == val)
367: pattr->rawval = strdup(*(att + 1));
368: TAILQ_INSERT_TAIL(&dat->attrq, pattr, child);
369: }
370:
1.1 kristaps 371: }
372:
373: /*
374: * Roll up the parse tree.
1.8 kristaps 375: * If we're at a text node, roll that one up first.
1.1 kristaps 376: * If we hit the root, then assign ourselves as the NODE_ROOT.
377: */
378: static void
379: xml_elem_end(void *arg, const XML_Char *name)
380: {
381: struct parse *ps = arg;
382:
383: if (ps->stop || NODE_ROOT == ps->node)
384: return;
385:
386: /* Close out text node, if applicable... */
387: if (NODE_TEXT == ps->node) {
388: assert(NULL != ps->cur);
1.10 kristaps 389: pnode_trim(ps->cur);
1.1 kristaps 390: ps->cur = ps->cur->parent;
391: assert(NULL != ps->cur);
392: ps->node = ps->cur->node;
393: }
394:
395: if (NULL == (ps->cur = ps->cur->parent))
396: ps->node = NODE_ROOT;
397: else
398: ps->node = ps->cur->node;
399: }
400:
1.8 kristaps 401: /*
402: * Recursively free a node (NULL is ok).
403: */
1.1 kristaps 404: static void
405: pnode_free(struct pnode *pn)
406: {
407: struct pnode *pp;
1.12 kristaps 408: struct pattr *ap;
1.1 kristaps 409:
410: if (NULL == pn)
411: return;
412:
413: while (NULL != (pp = TAILQ_FIRST(&pn->childq))) {
414: TAILQ_REMOVE(&pn->childq, pp, child);
415: pnode_free(pp);
416: }
417:
1.12 kristaps 418: while (NULL != (ap = TAILQ_FIRST(&pn->attrq))) {
419: TAILQ_REMOVE(&pn->attrq, ap, child);
420: free(ap->rawval);
421: free(ap);
422: }
423:
1.1 kristaps 424: free(pn->b);
425: free(pn);
426: }
427:
1.8 kristaps 428: /*
429: * Unlink a node from its parent and pnode_free() it.
430: */
1.1 kristaps 431: static void
432: pnode_unlink(struct pnode *pn)
433: {
434:
435: if (NULL != pn->parent)
436: TAILQ_REMOVE(&pn->parent->childq, pn, child);
437: pnode_free(pn);
438: }
439:
1.8 kristaps 440: /*
441: * Unlink all children of a node and pnode_free() them.
442: */
1.1 kristaps 443: static void
1.4 kristaps 444: pnode_unlinksub(struct pnode *pn)
445: {
446:
447: while ( ! TAILQ_EMPTY(&pn->childq))
448: pnode_unlink(TAILQ_FIRST(&pn->childq));
449: }
450:
1.8 kristaps 451: /*
452: * Reset the lookaside buffer.
453: */
1.4 kristaps 454: static void
1.1 kristaps 455: bufclear(struct parse *p)
456: {
457:
458: p->b[p->bsz = 0] = '\0';
459: }
460:
1.8 kristaps 461: /*
462: * Append NODE_TEXT contents to the current buffer, reallocating its
463: * size if necessary.
464: * The buffer is ALWAYS nil-terminated.
465: */
1.1 kristaps 466: static void
467: bufappend(struct parse *p, struct pnode *pn)
468: {
469:
470: assert(NODE_TEXT == pn->node);
471: if (p->bsz + pn->bsz + 1 > p->mbsz) {
472: p->mbsz = p->bsz + pn->bsz + 1;
473: if (NULL == (p->b = realloc(p->b, p->mbsz))) {
474: perror(NULL);
475: exit(EXIT_FAILURE);
476: }
477: }
478: memcpy(p->b + p->bsz, pn->b, pn->bsz);
479: p->bsz += pn->bsz;
480: p->b[p->bsz] = '\0';
481: }
482:
1.8 kristaps 483: /*
484: * Recursively append all NODE_TEXT nodes to the buffer.
485: * This descends into non-text nodes, but doesn't do anything beyond
486: * them.
487: * In other words, this is a recursive text grok.
488: */
1.3 kristaps 489: static void
490: bufappend_r(struct parse *p, struct pnode *pn)
491: {
492: struct pnode *pp;
493:
494: if (NODE_TEXT == pn->node)
495: bufappend(p, pn);
496: TAILQ_FOREACH(pp, &pn->childq, child)
497: bufappend_r(p, pp);
498: }
499:
1.25 kristaps 500: /*
501: * Recursively search and return the first instance of "node".
502: */
503: static struct pnode *
504: pnode_findfirst(struct pnode *pn, enum nodeid node)
505: {
506: struct pnode *pp, *res;
507:
508: res = NULL;
509: TAILQ_FOREACH(pp, &pn->childq, child) {
510: res = pp->node == node ? pp :
511: pnode_findfirst(pp, node);
512: if (NULL != res)
513: break;
514: }
515:
516: return(res);
517: }
518:
1.12 kristaps 519: #define MACROLINE_NORM 0
520: #define MACROLINE_UPPER 1
1.1 kristaps 521: /*
1.8 kristaps 522: * Recursively print text presumably on a macro line.
1.1 kristaps 523: * Convert all whitespace to regular spaces.
524: */
525: static void
1.12 kristaps 526: pnode_printmacrolinetext(struct parse *p, struct pnode *pn, int fl)
1.1 kristaps 527: {
528: char *cp;
529:
1.13 kristaps 530: if (0 == p->newln)
531: putchar(' ');
532:
1.1 kristaps 533: bufclear(p);
1.3 kristaps 534: bufappend_r(p, pn);
1.1 kristaps 535:
536: /* Convert all space to spaces. */
537: for (cp = p->b; '\0' != *cp; cp++)
538: if (isspace((int)*cp))
539: *cp = ' ';
540:
541: for (cp = p->b; isspace((int)*cp); cp++)
1.4 kristaps 542: /* Spin past whitespace (XXX: necessary?) */ ;
1.1 kristaps 543: for ( ; '\0' != *cp; cp++) {
544: /* Escape us if we look like a macro. */
545: if ((cp == p->b || ' ' == *(cp - 1)) &&
546: isupper((int)*cp) &&
547: '\0' != *(cp + 1) &&
548: islower((int)*(cp + 1)) &&
549: ('\0' == *(cp + 2) ||
550: ' ' == *(cp + 2) ||
551: (islower((int)*(cp + 2)) &&
552: ('\0' == *(cp + 3) ||
553: ' ' == *(cp + 3)))))
554: fputs("\\&", stdout);
1.12 kristaps 555: if (MACROLINE_UPPER & fl)
556: putchar(toupper((int)*cp));
557: else
558: putchar((int)*cp);
1.1 kristaps 559: /* If we're a character escape, escape us. */
560: if ('\\' == *cp)
561: putchar('e');
562: }
563: }
564:
1.12 kristaps 565: static void
566: pnode_printmacrolinepart(struct parse *p, struct pnode *pn)
567: {
568:
569: pnode_printmacrolinetext(p, pn, 0);
570: }
571:
1.1 kristaps 572: /*
573: * Just pnode_printmacrolinepart() but with a newline.
574: * If no text, just the newline.
575: */
576: static void
577: pnode_printmacroline(struct parse *p, struct pnode *pn)
578: {
579:
1.13 kristaps 580: assert(0 == p->newln);
1.12 kristaps 581: pnode_printmacrolinetext(p, pn, 0);
1.1 kristaps 582: putchar('\n');
1.13 kristaps 583: p->newln = 1;
1.1 kristaps 584: }
585:
1.10 kristaps 586: static void
587: pnode_printmopen(struct parse *p)
588: {
589: if (p->newln) {
590: putchar('.');
591: p->newln = 0;
592: } else
593: putchar(' ');
594: }
595:
596: static void
597: pnode_printmclose(struct parse *p, int sv)
598: {
599:
600: if (sv && ! p->newln) {
601: putchar('\n');
602: p->newln = 1;
603: }
604: }
605:
1.8 kristaps 606: /*
1.10 kristaps 607: * If the SYNOPSIS macro has a superfluous title, kill it.
1.8 kristaps 608: */
1.1 kristaps 609: static void
1.6 kristaps 610: pnode_printrefsynopsisdiv(struct parse *p, struct pnode *pn)
611: {
612: struct pnode *pp;
613:
1.10 kristaps 614: TAILQ_FOREACH(pp, &pn->childq, child)
1.6 kristaps 615: if (NODE_TITLE == pp->node) {
616: pnode_unlink(pp);
1.10 kristaps 617: return;
1.6 kristaps 618: }
619: }
620:
1.8 kristaps 621: /*
622: * Start a hopefully-named `Sh' section.
623: */
1.6 kristaps 624: static void
1.1 kristaps 625: pnode_printrefsect(struct parse *p, struct pnode *pn)
626: {
627: struct pnode *pp;
628:
629: TAILQ_FOREACH(pp, &pn->childq, child)
630: if (NODE_TITLE == pp->node)
631: break;
632:
1.29 kristaps 633: switch (pn->node) {
634: case (NODE_REFSECT1):
1.20 kristaps 635: fputs(".Sh", stdout);
1.29 kristaps 636: break;
637: case (NODE_REFSECT2):
1.20 kristaps 638: fputs(".Ss", stdout);
1.29 kristaps 639: break;
640: case (NODE_REFSECT3):
641: puts(".Pp");
642: fputs(".Sy", stdout);
643: break;
644: case (NODE_NOTE):
645: /* FALLTHROUGH */
646: case (NODE_REFSECTION):
647: /* FALLTHROUGH */
648: case (NODE_TIP):
649: /* FALLTHROUGH */
650: case (NODE_CAUTION):
651: /* FALLTHROUGH */
652: case (NODE_WARNING):
653: puts(".Pp");
654: if (NULL == pp)
655: return;
656: fputs(".Em", stdout);
657: break;
658: default:
659: break;
660: }
1.20 kristaps 661:
1.13 kristaps 662: p->newln = 0;
1.4 kristaps 663:
1.5 kristaps 664: if (NULL != pp) {
1.20 kristaps 665: pnode_printmacrolinetext(p, pp,
666: NODE_REFSECT1 == pn->node ?
667: MACROLINE_UPPER : 0);
1.18 kristaps 668: pnode_printmclose(p, 1);
1.5 kristaps 669: pnode_unlink(pp);
1.13 kristaps 670: } else {
1.29 kristaps 671: puts(NODE_REFSECT1 == pn->node ?
672: "UNKNOWN" : "unknown");
1.13 kristaps 673: p->newln = 1;
674: }
1.1 kristaps 675: }
676:
1.8 kristaps 677: /*
678: * Start a reference, extracting the title and volume.
679: */
1.1 kristaps 680: static void
681: pnode_printciterefentry(struct parse *p, struct pnode *pn)
682: {
683: struct pnode *pp, *title, *manvol;
684:
685: title = manvol = NULL;
1.13 kristaps 686: assert(p->newln);
1.1 kristaps 687: TAILQ_FOREACH(pp, &pn->childq, child)
688: if (NODE_MANVOLNUM == pp->node)
689: manvol = pp;
690: else if (NODE_REFENTRYTITLE == pp->node)
691: title = pp;
692:
1.13 kristaps 693: fputs(".Xr", stdout);
694: p->newln = 0;
1.4 kristaps 695:
1.1 kristaps 696: if (NULL != title) {
697: pnode_printmacrolinepart(p, title);
698: } else
1.13 kristaps 699: fputs(" unknown ", stdout);
1.4 kristaps 700:
1.13 kristaps 701: if (NULL == manvol) {
702: puts(" 1");
703: p->newln = 1;
704: } else
1.1 kristaps 705: pnode_printmacroline(p, manvol);
706: }
707:
708: static void
709: pnode_printrefmeta(struct parse *p, struct pnode *pn)
710: {
711: struct pnode *pp, *title, *manvol;
712:
713: title = manvol = NULL;
1.13 kristaps 714: assert(p->newln);
1.1 kristaps 715: TAILQ_FOREACH(pp, &pn->childq, child)
716: if (NODE_MANVOLNUM == pp->node)
717: manvol = pp;
718: else if (NODE_REFENTRYTITLE == pp->node)
719: title = pp;
720:
1.2 kristaps 721: puts(".Dd $Mdocdate" "$");
1.13 kristaps 722: fputs(".Dt", stdout);
723: p->newln = 0;
1.1 kristaps 724:
1.13 kristaps 725: if (NULL != title)
1.12 kristaps 726: pnode_printmacrolinetext(p, title, MACROLINE_UPPER);
1.13 kristaps 727: else
728: fputs(" UNKNOWN ", stdout);
729:
730: if (NULL == manvol) {
731: puts(" 1");
732: p->newln = 1;
1.1 kristaps 733: } else
734: pnode_printmacroline(p, manvol);
735:
736: puts(".Os");
737: }
738:
1.3 kristaps 739: static void
740: pnode_printfuncdef(struct parse *p, struct pnode *pn)
741: {
742: struct pnode *pp, *ftype, *func;
743:
1.13 kristaps 744: assert(p->newln);
1.3 kristaps 745: ftype = func = NULL;
746: TAILQ_FOREACH(pp, &pn->childq, child)
747: if (NODE_TEXT == pp->node)
748: ftype = pp;
749: else if (NODE_FUNCTION == pp->node)
750: func = pp;
751:
752: if (NULL != ftype) {
1.13 kristaps 753: fputs(".Ft", stdout);
754: p->newln = 0;
1.3 kristaps 755: pnode_printmacroline(p, ftype);
756: }
757:
758: if (NULL != func) {
1.13 kristaps 759: fputs(".Fo", stdout);
760: p->newln = 0;
1.3 kristaps 761: pnode_printmacroline(p, func);
1.13 kristaps 762: } else {
1.3 kristaps 763: puts(".Fo UNKNOWN");
1.13 kristaps 764: p->newln = 1;
765: }
1.3 kristaps 766: }
767:
768: static void
769: pnode_printparamdef(struct parse *p, struct pnode *pn)
770: {
771: struct pnode *pp, *ptype, *param;
772:
1.13 kristaps 773: assert(p->newln);
1.3 kristaps 774: ptype = param = NULL;
775: TAILQ_FOREACH(pp, &pn->childq, child)
776: if (NODE_TEXT == pp->node)
777: ptype = pp;
778: else if (NODE_PARAMETER == pp->node)
779: param = pp;
780:
781: fputs(".Fa \"", stdout);
1.13 kristaps 782: p->newln = 0;
1.3 kristaps 783: if (NULL != ptype) {
784: pnode_printmacrolinepart(p, ptype);
785: putchar(' ');
786: }
787:
788: if (NULL != param)
789: pnode_printmacrolinepart(p, param);
790:
791: puts("\"");
1.13 kristaps 792: p->newln = 1;
1.3 kristaps 793: }
794:
795: static void
796: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
797: {
798: struct pnode *pp, *fdef;
799:
1.13 kristaps 800: assert(p->newln);
1.3 kristaps 801: TAILQ_FOREACH(fdef, &pn->childq, child)
802: if (NODE_FUNCDEF == fdef->node)
803: break;
804:
1.4 kristaps 805: if (NULL != fdef)
1.3 kristaps 806: pnode_printfuncdef(p, fdef);
1.4 kristaps 807: else
1.3 kristaps 808: puts(".Fo UNKNOWN");
809:
1.4 kristaps 810: TAILQ_FOREACH(pp, &pn->childq, child)
1.3 kristaps 811: if (NODE_PARAMDEF == pp->node)
812: pnode_printparamdef(p, pp);
813:
814: puts(".Fc");
1.13 kristaps 815: p->newln = 1;
1.3 kristaps 816: }
817:
1.10 kristaps 818: /*
819: * The <arg> element is more complicated than it should be because text
820: * nodes are treated like ".Ar foo", but non-text nodes need to be
821: * re-sent into the printer (i.e., without the preceding ".Ar").
1.12 kristaps 822: * This also handles the case of "repetition" (or in other words, the
823: * ellipsis following an argument) and optionality.
1.10 kristaps 824: */
1.4 kristaps 825: static void
1.10 kristaps 826: pnode_printarg(struct parse *p, struct pnode *pn)
1.4 kristaps 827: {
828: struct pnode *pp;
1.12 kristaps 829: struct pattr *ap;
830: int isop, isrep;
831:
832: isop = 1;
833: isrep = 0;
834: TAILQ_FOREACH(ap, &pn->attrq, child)
835: if (ATTRKEY_CHOICE == ap->key &&
836: (ATTRVAL_PLAIN == ap->val ||
837: ATTRVAL_REQ == ap->val))
838: isop = 0;
839: else if (ATTRKEY_REP == ap->key &&
840: (ATTRVAL_REPEAT == ap->val))
841: isrep = 1;
842:
843: if (isop) {
844: pnode_printmopen(p);
1.13 kristaps 845: fputs("Op", stdout);
1.12 kristaps 846: }
1.4 kristaps 847:
1.10 kristaps 848: TAILQ_FOREACH(pp, &pn->childq, child) {
849: if (NODE_TEXT == pp->node) {
850: pnode_printmopen(p);
1.13 kristaps 851: fputs("Ar", stdout);
1.10 kristaps 852: }
853: pnode_print(p, pp);
1.12 kristaps 854: if (NODE_TEXT == pp->node && isrep)
855: fputs("...", stdout);
1.10 kristaps 856: }
1.4 kristaps 857: }
858:
1.24 kristaps 859: static void
860: pnode_printgroup(struct parse *p, struct pnode *pn)
861: {
862: struct pnode *pp, *np;
863: struct pattr *ap;
864: int isop, sv;
865:
866: isop = 1;
867: TAILQ_FOREACH(ap, &pn->attrq, child)
868: if (ATTRKEY_CHOICE == ap->key &&
869: (ATTRVAL_PLAIN == ap->val ||
870: ATTRVAL_REQ == ap->val)) {
871: isop = 0;
872: break;
873: }
874:
875: /*
876: * Make sure we're on a macro line.
877: * This will prevent pnode_print() for putting us on a
878: * subsequent line.
879: */
880: sv = p->newln;
881: pnode_printmopen(p);
882: if (isop)
883: fputs("Op", stdout);
884: else if (sv)
885: fputs("No", stdout);
886:
887: /*
888: * Keep on printing text separated by the vertical bar as long
889: * as we're within the same origin node as the group.
890: * This is kind of a nightmare.
891: * Eh, DocBook...
892: * FIXME: if there's a "Fl", we don't cut off the leading "-"
893: * like we do in pnode_print().
894: */
895: TAILQ_FOREACH(pp, &pn->childq, child) {
896: pnode_print(p, pp);
897: np = TAILQ_NEXT(pp, child);
898: while (NULL != np) {
899: if (pp->node != np->node)
900: break;
901: fputs(" |", stdout);
902: pnode_printmacrolinepart(p, np);
903: pp = np;
904: np = TAILQ_NEXT(np, child);
905: }
906: }
907:
908: pnode_printmclose(p, sv);
909: }
910:
1.7 kristaps 911: static void
912: pnode_printprologue(struct parse *p, struct pnode *pn)
913: {
914: struct pnode *pp;
915:
1.9 kristaps 916: pp = NULL == p->root ? NULL :
917: pnode_findfirst(p->root, NODE_REFMETA);
918:
919: if (NULL != pp) {
1.7 kristaps 920: pnode_printrefmeta(p, pp);
921: pnode_unlink(pp);
922: } else {
923: puts(".\\\" Supplying bogus prologue...");
924: puts(".Dd $Mdocdate" "$");
925: puts(".Dt UNKNOWN 1");
926: puts(".Os");
927: }
928: }
929:
1.13 kristaps 930: static void
931: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
932: {
933: struct pnode *pp;
934:
935: assert(p->newln);
936: TAILQ_FOREACH(pp, &pn->childq, child)
937: if (NODE_TERM == pp->node) {
938: fputs(".It", stdout);
939: p->newln = 0;
940: pnode_print(p, pp);
941: pnode_unlink(pp);
1.16 kristaps 942: pnode_printmclose(p, 1);
1.13 kristaps 943: return;
944: }
945:
946: puts(".It");
947: p->newln = 1;
948: }
949:
950: static void
1.25 kristaps 951: pnode_printrow(struct parse *p, struct pnode *pn)
952: {
953: struct pnode *pp;
954:
955: puts(".Bl -dash -compact");
956:
957: TAILQ_FOREACH(pp, &pn->childq, child) {
958: assert(p->newln);
959: puts(".It");
960: pnode_print(p, pp);
961: pnode_printmclose(p, 1);
962: }
963: assert(p->newln);
964: puts(".El");
965: }
966:
967: static void
968: pnode_printtable(struct parse *p, struct pnode *pn)
1.16 kristaps 969: {
970: struct pnode *pp;
971:
972: assert(p->newln);
973: TAILQ_FOREACH(pp, &pn->childq, child)
974: if (NODE_TITLE == pp->node) {
975: puts(".Pp");
976: pnode_print(p, pp);
977: pnode_unlink(pp);
978: }
1.25 kristaps 979: assert(p->newln);
980: puts(".Bl -ohang");
981: while (NULL != (pp = pnode_findfirst(pn, NODE_ROW))) {
982: puts(".It Table Row");
983: pnode_printrow(p, pp);
984: pnode_printmclose(p, 1);
985: pnode_unlink(pp);
986: }
987: assert(p->newln);
988: puts(".El");
989: }
990:
991: static void
992: pnode_printlist(struct parse *p, struct pnode *pn)
993: {
994: struct pnode *pp;
1.16 kristaps 995:
996: assert(p->newln);
1.25 kristaps 997: TAILQ_FOREACH(pp, &pn->childq, child)
998: if (NODE_TITLE == pp->node) {
999: puts(".Pp");
1000: pnode_print(p, pp);
1001: pnode_unlink(pp);
1002: }
1003: assert(p->newln);
1.21 kristaps 1004:
1005: if (NODE_ORDEREDLIST == pn->node)
1006: puts(".Bl -enum");
1007: else
1008: puts(".Bl -item");
1009:
1.16 kristaps 1010: TAILQ_FOREACH(pp, &pn->childq, child) {
1011: assert(p->newln);
1012: puts(".It");
1013: pnode_print(p, pp);
1014: pnode_printmclose(p, 1);
1015: }
1016: assert(p->newln);
1017: puts(".El");
1018: }
1019:
1020: static void
1.13 kristaps 1021: pnode_printvariablelist(struct parse *p, struct pnode *pn)
1022: {
1023: struct pnode *pp;
1024:
1025: assert(p->newln);
1026: TAILQ_FOREACH(pp, &pn->childq, child)
1027: if (NODE_TITLE == pp->node) {
1028: puts(".Pp");
1029: pnode_print(p, pp);
1030: pnode_unlink(pp);
1031: }
1032:
1033: assert(p->newln);
1034: puts(".Bl -tag -width Ds");
1035: TAILQ_FOREACH(pp, &pn->childq, child)
1036: if (NODE_VARLISTENTRY != pp->node) {
1037: assert(p->newln);
1038: fputs(".It", stdout);
1039: pnode_printmacroline(p, pp);
1040: } else {
1041: assert(p->newln);
1042: pnode_print(p, pp);
1043: }
1044: assert(p->newln);
1045: puts(".El");
1046: }
1047:
1.1 kristaps 1048: /*
1049: * Print a parsed node (or ignore it--whatever).
1050: * This is a recursive function.
1.23 kristaps 1051: * FIXME: if we're in a literal context (<screen> or <programlisting> or
1052: * whatever), don't print inline macros.
1.1 kristaps 1053: */
1054: static void
1055: pnode_print(struct parse *p, struct pnode *pn)
1056: {
1057: struct pnode *pp;
1058: char *cp;
1.10 kristaps 1059: int last, sv;
1.1 kristaps 1060:
1061: if (NULL == pn)
1062: return;
1063:
1.10 kristaps 1064: sv = p->newln;
1.1 kristaps 1065:
1066: switch (pn->node) {
1.27 kristaps 1067: case (NODE_APPLICATION):
1068: pnode_printmopen(p);
1069: fputs("Nm", stdout);
1070: break;
1.30 ! kristaps 1071: case (NODE_ANCHOR):
! 1072: /* Don't print anything! */
! 1073: return;
1.4 kristaps 1074: case (NODE_ARG):
1.10 kristaps 1075: pnode_printarg(p, pn);
1.4 kristaps 1076: pnode_unlinksub(pn);
1077: break;
1.1 kristaps 1078: case (NODE_CITEREFENTRY):
1.10 kristaps 1079: assert(p->newln);
1.1 kristaps 1080: pnode_printciterefentry(p, pn);
1.4 kristaps 1081: pnode_unlinksub(pn);
1.1 kristaps 1082: break;
1083: case (NODE_CODE):
1.10 kristaps 1084: pnode_printmopen(p);
1.13 kristaps 1085: fputs("Li", stdout);
1.4 kristaps 1086: break;
1087: case (NODE_COMMAND):
1.10 kristaps 1088: pnode_printmopen(p);
1.13 kristaps 1089: fputs("Nm", stdout);
1090: break;
1091: case (NODE_EMPHASIS):
1092: pnode_printmopen(p);
1093: fputs("Em", stdout);
1.1 kristaps 1094: break;
1.21 kristaps 1095: case (NODE_ENVAR):
1096: pnode_printmopen(p);
1097: fputs("Ev", stdout);
1098: break;
1.17 kristaps 1099: case (NODE_FILENAME):
1100: pnode_printmopen(p);
1101: fputs("Pa", stdout);
1102: break;
1.3 kristaps 1103: case (NODE_FUNCTION):
1.10 kristaps 1104: pnode_printmopen(p);
1.13 kristaps 1105: fputs("Fn", stdout);
1.3 kristaps 1106: break;
1107: case (NODE_FUNCPROTOTYPE):
1.10 kristaps 1108: assert(p->newln);
1.3 kristaps 1109: pnode_printfuncprototype(p, pn);
1.4 kristaps 1110: pnode_unlinksub(pn);
1.3 kristaps 1111: break;
1.1 kristaps 1112: case (NODE_FUNCSYNOPSISINFO):
1.10 kristaps 1113: pnode_printmopen(p);
1.13 kristaps 1114: fputs("Fd", stdout);
1.16 kristaps 1115: break;
1116: case (NODE_ITEMIZEDLIST):
1117: assert(p->newln);
1.25 kristaps 1118: pnode_printlist(p, pn);
1119: pnode_unlinksub(pn);
1.24 kristaps 1120: break;
1121: case (NODE_GROUP):
1122: pnode_printgroup(p, pn);
1123: pnode_unlinksub(pn);
1.10 kristaps 1124: break;
1.19 kristaps 1125: case (NODE_LITERAL):
1126: pnode_printmopen(p);
1127: fputs("Li", stdout);
1128: break;
1.10 kristaps 1129: case (NODE_OPTION):
1130: pnode_printmopen(p);
1.13 kristaps 1131: fputs("Fl", stdout);
1.1 kristaps 1132: break;
1.25 kristaps 1133: case (NODE_ORDEREDLIST):
1134: assert(p->newln);
1135: pnode_printlist(p, pn);
1136: pnode_unlinksub(pn);
1137: break;
1.1 kristaps 1138: case (NODE_PARA):
1.10 kristaps 1139: assert(p->newln);
1.13 kristaps 1140: if (NULL != pn->parent &&
1141: NODE_LISTITEM == pn->parent->node)
1142: break;
1.1 kristaps 1143: puts(".Pp");
1.3 kristaps 1144: break;
1145: case (NODE_PARAMETER):
1.10 kristaps 1146: /* Suppress non-text children... */
1147: pnode_printmopen(p);
1148: fputs("Fa \"", stdout);
1.3 kristaps 1149: pnode_printmacrolinepart(p, pn);
1150: puts("\"");
1.4 kristaps 1151: pnode_unlinksub(pn);
1.1 kristaps 1152: break;
1.28 kristaps 1153: case (NODE_QUOTE):
1154: pnode_printmopen(p);
1155: fputs("Qo", stdout);
1156: break;
1.1 kristaps 1157: case (NODE_PROGRAMLISTING):
1.22 kristaps 1158: /* FALLTHROUGH */
1159: case (NODE_SCREEN):
1.10 kristaps 1160: assert(p->newln);
1.1 kristaps 1161: puts(".Bd -literal");
1.15 kristaps 1162: break;
1163: case (NODE_REFENTRYINFO):
1164: /* Suppress. */
1165: pnode_unlinksub(pn);
1.1 kristaps 1166: break;
1167: case (NODE_REFMETA):
1.7 kristaps 1168: abort();
1.1 kristaps 1169: break;
1170: case (NODE_REFNAME):
1.10 kristaps 1171: /* Suppress non-text children... */
1172: pnode_printmopen(p);
1.13 kristaps 1173: fputs("Nm", stdout);
1174: p->newln = 0;
1.10 kristaps 1175: pnode_printmacrolinepart(p, pn);
1.4 kristaps 1176: pnode_unlinksub(pn);
1.10 kristaps 1177: break;
1.1 kristaps 1178: case (NODE_REFNAMEDIV):
1.10 kristaps 1179: assert(p->newln);
1.1 kristaps 1180: puts(".Sh NAME");
1181: break;
1182: case (NODE_REFPURPOSE):
1.10 kristaps 1183: assert(p->newln);
1.13 kristaps 1184: pnode_printmopen(p);
1185: fputs("Nd", stdout);
1.10 kristaps 1186: break;
1.1 kristaps 1187: case (NODE_REFSYNOPSISDIV):
1.10 kristaps 1188: assert(p->newln);
1.6 kristaps 1189: pnode_printrefsynopsisdiv(p, pn);
1.10 kristaps 1190: puts(".Sh SYNOPSIS");
1.1 kristaps 1191: break;
1192: case (NODE_REFSECT1):
1.20 kristaps 1193: /* FALLTHROUGH */
1194: case (NODE_REFSECT2):
1.29 kristaps 1195: /* FALLTHROUGH */
1196: case (NODE_REFSECT3):
1197: /* FALLTHROUGH */
1198: case (NODE_REFSECTION):
1199: /* FALLTHROUGH */
1200: case (NODE_NOTE):
1201: /* FALLTHROUGH */
1202: case (NODE_TIP):
1203: /* FALLTHROUGH */
1204: case (NODE_CAUTION):
1205: /* FALLTHROUGH */
1206: case (NODE_WARNING):
1.10 kristaps 1207: assert(p->newln);
1.1 kristaps 1208: pnode_printrefsect(p, pn);
1209: break;
1.13 kristaps 1210: case (NODE_REPLACEABLE):
1211: pnode_printmopen(p);
1212: fputs("Ar", stdout);
1213: break;
1.19 kristaps 1214: case (NODE_SBR):
1215: assert(p->newln);
1216: puts(".br");
1217: break;
1.30 ! kristaps 1218: case (NODE_SGMLTAG):
! 1219: pnode_printmopen(p);
! 1220: fputs("Li", stdout);
! 1221: break;
1.8 kristaps 1222: case (NODE_STRUCTNAME):
1.10 kristaps 1223: pnode_printmopen(p);
1.13 kristaps 1224: fputs("Vt", stdout);
1.25 kristaps 1225: break;
1226: case (NODE_TABLE):
1227: assert(p->newln);
1228: pnode_printtable(p, pn);
1229: pnode_unlinksub(pn);
1.10 kristaps 1230: break;
1.1 kristaps 1231: case (NODE_TEXT):
1.13 kristaps 1232: if (0 == p->newln)
1233: putchar(' ');
1.1 kristaps 1234: bufclear(p);
1235: bufappend(p, pn);
1236: /*
1237: * Output all characters, squeezing out whitespace
1238: * between newlines.
1239: * XXX: all whitespace, including tabs (?).
1240: * Remember to escape control characters and escapes.
1241: */
1.10 kristaps 1242: assert(p->bsz);
1.20 kristaps 1243: cp = p->b;
1244: /*
1245: * There's often a superfluous "-" in its <option> tags
1246: * before the actual flags themselves.
1247: * "Fl" does this for us, so remove it.
1248: */
1249: if (NULL != pn->parent &&
1250: NODE_OPTION == pn->parent->node &&
1251: '-' == *cp)
1252: cp++;
1253: for (last = '\n'; '\0' != *cp; ) {
1.1 kristaps 1254: if ('\n' == last) {
1255: /* Consume all whitespace. */
1256: if (isspace((int)*cp)) {
1257: while (isspace((int)*cp))
1258: cp++;
1259: continue;
1260: } else if ('\'' == *cp || '.' == *cp)
1261: fputs("\\&", stdout);
1262: }
1263: putchar(last = *cp++);
1264: /* If we're a character escape, escape us. */
1265: if ('\\' == last)
1266: putchar('e');
1267: }
1.10 kristaps 1268: p->newln = 0;
1.1 kristaps 1269: break;
1.26 kristaps 1270: case (NODE_USERINPUT):
1271: pnode_printmopen(p);
1272: fputs("Li", stdout);
1273: break;
1.13 kristaps 1274: case (NODE_VARIABLELIST):
1275: assert(p->newln);
1276: pnode_printvariablelist(p, pn);
1277: pnode_unlinksub(pn);
1278: break;
1279: case (NODE_VARLISTENTRY):
1280: assert(p->newln);
1281: pnode_printvarlistentry(p, pn);
1282: break;
1.26 kristaps 1283: case (NODE_VARNAME):
1.23 kristaps 1284: pnode_printmopen(p);
1.26 kristaps 1285: fputs("Va", stdout);
1.23 kristaps 1286: break;
1.1 kristaps 1287: default:
1288: break;
1289: }
1290:
1291: TAILQ_FOREACH(pp, &pn->childq, child)
1292: pnode_print(p, pp);
1293:
1294: switch (pn->node) {
1.27 kristaps 1295: case (NODE_APPLICATION):
1.10 kristaps 1296: case (NODE_ARG):
1297: case (NODE_CODE):
1298: case (NODE_COMMAND):
1.13 kristaps 1299: case (NODE_EMPHASIS):
1.21 kristaps 1300: case (NODE_ENVAR):
1.17 kristaps 1301: case (NODE_FILENAME):
1.10 kristaps 1302: case (NODE_FUNCTION):
1303: case (NODE_FUNCSYNOPSISINFO):
1.19 kristaps 1304: case (NODE_LITERAL):
1.10 kristaps 1305: case (NODE_OPTION):
1306: case (NODE_PARAMETER):
1.13 kristaps 1307: case (NODE_REPLACEABLE):
1308: case (NODE_REFPURPOSE):
1.30 ! kristaps 1309: case (NODE_SGMLTAG):
1.10 kristaps 1310: case (NODE_STRUCTNAME):
1311: case (NODE_TEXT):
1.23 kristaps 1312: case (NODE_USERINPUT):
1.26 kristaps 1313: case (NODE_VARNAME):
1.28 kristaps 1314: pnode_printmclose(p, sv);
1315: break;
1316: case (NODE_QUOTE):
1317: pnode_printmclose(p, sv);
1318: sv = p->newln;
1319: pnode_printmopen(p);
1320: fputs("Qc", stdout);
1.10 kristaps 1321: pnode_printmclose(p, sv);
1322: break;
1.12 kristaps 1323: case (NODE_REFNAME):
1324: /*
1325: * If we're in the NAME macro and we have multiple
1326: * <refname> macros in sequence, then print out a
1327: * trailing comma before the newline.
1328: */
1329: if (NULL != pn->parent &&
1330: NODE_REFNAMEDIV == pn->parent->node &&
1331: NULL != TAILQ_NEXT(pn, child) &&
1332: NODE_REFNAME == TAILQ_NEXT(pn, child)->node)
1333: fputs(" ,", stdout);
1334: pnode_printmclose(p, sv);
1335: break;
1.1 kristaps 1336: case (NODE_PROGRAMLISTING):
1.22 kristaps 1337: /* FALLTHROUGH */
1338: case (NODE_SCREEN):
1.10 kristaps 1339: assert(p->newln);
1.1 kristaps 1340: puts(".Ed");
1.10 kristaps 1341: p->newln = 1;
1.1 kristaps 1342: break;
1343: default:
1344: break;
1345: }
1346: }
1347:
1348: /*
1349: * Loop around the read buffer until we've drained it of all data.
1350: * Invoke the parser context with each buffer fill.
1351: */
1352: static int
1353: readfile(XML_Parser xp, int fd,
1354: char *b, size_t bsz, const char *fn)
1355: {
1356: struct parse p;
1357: int rc;
1358: ssize_t ssz;
1359:
1360: memset(&p, 0, sizeof(struct parse));
1361:
1362: p.b = malloc(p.bsz = p.mbsz = 1024);
1.12 kristaps 1363: p.fname = fn;
1364: p.xml = xp;
1.1 kristaps 1365:
1366: XML_SetCharacterDataHandler(xp, xml_char);
1367: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
1368: XML_SetUserData(xp, &p);
1369:
1370: while ((ssz = read(fd, b, bsz)) >= 0) {
1371: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
1.30 ! kristaps 1372: fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
! 1373: XML_GetCurrentLineNumber(xp),
! 1374: XML_GetCurrentColumnNumber(xp),
1.1 kristaps 1375: XML_ErrorString
1376: (XML_GetErrorCode(xp)));
1377: else if ( ! p.stop && ssz > 0)
1378: continue;
1379: /*
1380: * Exit when we've read all or errors have occured
1381: * during the parse sequence.
1382: */
1.10 kristaps 1383: p.newln = 1;
1.7 kristaps 1384: pnode_printprologue(&p, p.root);
1.1 kristaps 1385: pnode_print(&p, p.root);
1386: pnode_free(p.root);
1387: free(p.b);
1388: return(0 != rc && ! p.stop);
1389: }
1390:
1391: /* Read error has occured. */
1392: perror(fn);
1393: pnode_free(p.root);
1394: free(p.b);
1395: return(0);
1396: }
1397:
1398: int
1399: main(int argc, char *argv[])
1400: {
1401: XML_Parser xp;
1402: const char *fname;
1403: char *buf;
1404: int fd, rc;
1405:
1406: fname = "-";
1407: xp = NULL;
1408: buf = NULL;
1409: rc = 0;
1410:
1411: if (-1 != getopt(argc, argv, ""))
1412: return(EXIT_FAILURE);
1413:
1414: argc -= optind;
1415: argv += optind;
1416:
1417: if (argc > 1)
1418: return(EXIT_FAILURE);
1419: else if (argc > 0)
1420: fname = argv[0];
1421:
1422: /* Read from stdin or a file. */
1423: fd = 0 == strcmp(fname, "-") ?
1424: STDIN_FILENO : open(fname, O_RDONLY, 0);
1425:
1426: /*
1427: * Open file for reading.
1428: * Allocate a read buffer.
1429: * Create the parser context.
1430: * Dive directly into the parse.
1431: */
1432: if (-1 == fd)
1433: perror(fname);
1434: else if (NULL == (buf = malloc(4096)))
1435: perror(NULL);
1436: else if (NULL == (xp = XML_ParserCreate(NULL)))
1437: perror(NULL);
1438: else if ( ! readfile(xp, fd, buf, 4096, fname))
1439: rc = 1;
1440:
1441: XML_ParserFree(xp);
1442: free(buf);
1443: if (STDIN_FILENO != fd)
1444: close(fd);
1445: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1446: }
CVSweb