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