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