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