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