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