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