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