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