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