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