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