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