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