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