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