Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.41
1.41 ! kristaps 1: /* $Id: docbook2mdoc.c,v 1.40 2014/10/12 15:08:45 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):
920: printf(" sup ");
921: break;
922: case (NODE_MML_MFRAC):
923: printf(" over ");
924: break;
925: case (NODE_MML_MSUB):
926: printf(" sub ");
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.13 kristaps 1071: static void
1072: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
1073: {
1074: struct pnode *pp;
1075:
1076: assert(p->newln);
1077: TAILQ_FOREACH(pp, &pn->childq, child)
1078: if (NODE_TERM == pp->node) {
1.33 kristaps 1079: assert(p->newln);
1.13 kristaps 1080: fputs(".It", stdout);
1081: p->newln = 0;
1082: pnode_print(p, pp);
1083: pnode_unlink(pp);
1.16 kristaps 1084: pnode_printmclose(p, 1);
1.13 kristaps 1085: return;
1086: }
1087:
1088: puts(".It");
1089: p->newln = 1;
1090: }
1091:
1092: static void
1.25 kristaps 1093: pnode_printrow(struct parse *p, struct pnode *pn)
1094: {
1095: struct pnode *pp;
1096:
1097: puts(".Bl -dash -compact");
1098:
1099: TAILQ_FOREACH(pp, &pn->childq, child) {
1100: assert(p->newln);
1101: puts(".It");
1102: pnode_print(p, pp);
1103: pnode_printmclose(p, 1);
1104: }
1105: assert(p->newln);
1106: puts(".El");
1107: }
1108:
1109: static void
1110: pnode_printtable(struct parse *p, struct pnode *pn)
1.16 kristaps 1111: {
1112: struct pnode *pp;
1113:
1114: assert(p->newln);
1115: TAILQ_FOREACH(pp, &pn->childq, child)
1116: if (NODE_TITLE == pp->node) {
1117: puts(".Pp");
1118: pnode_print(p, pp);
1119: pnode_unlink(pp);
1120: }
1.25 kristaps 1121: assert(p->newln);
1122: puts(".Bl -ohang");
1123: while (NULL != (pp = pnode_findfirst(pn, NODE_ROW))) {
1124: puts(".It Table Row");
1125: pnode_printrow(p, pp);
1126: pnode_printmclose(p, 1);
1127: pnode_unlink(pp);
1128: }
1129: assert(p->newln);
1130: puts(".El");
1131: }
1132:
1133: static void
1134: pnode_printlist(struct parse *p, struct pnode *pn)
1135: {
1136: struct pnode *pp;
1.16 kristaps 1137:
1138: assert(p->newln);
1.25 kristaps 1139: TAILQ_FOREACH(pp, &pn->childq, child)
1140: if (NODE_TITLE == pp->node) {
1141: puts(".Pp");
1142: pnode_print(p, pp);
1143: pnode_unlink(pp);
1144: }
1145: assert(p->newln);
1.21 kristaps 1146:
1147: if (NODE_ORDEREDLIST == pn->node)
1148: puts(".Bl -enum");
1149: else
1150: puts(".Bl -item");
1151:
1.16 kristaps 1152: TAILQ_FOREACH(pp, &pn->childq, child) {
1153: assert(p->newln);
1154: puts(".It");
1155: pnode_print(p, pp);
1156: pnode_printmclose(p, 1);
1157: }
1158: assert(p->newln);
1159: puts(".El");
1160: }
1161:
1162: static void
1.13 kristaps 1163: pnode_printvariablelist(struct parse *p, struct pnode *pn)
1164: {
1165: struct pnode *pp;
1166:
1167: assert(p->newln);
1168: TAILQ_FOREACH(pp, &pn->childq, child)
1169: if (NODE_TITLE == pp->node) {
1170: puts(".Pp");
1171: pnode_print(p, pp);
1172: pnode_unlink(pp);
1173: }
1174:
1175: assert(p->newln);
1176: puts(".Bl -tag -width Ds");
1177: TAILQ_FOREACH(pp, &pn->childq, child)
1178: if (NODE_VARLISTENTRY != pp->node) {
1179: assert(p->newln);
1180: fputs(".It", stdout);
1181: pnode_printmacroline(p, pp);
1182: } else {
1183: assert(p->newln);
1184: pnode_print(p, pp);
1185: }
1186: assert(p->newln);
1187: puts(".El");
1188: }
1189:
1.1 kristaps 1190: /*
1191: * Print a parsed node (or ignore it--whatever).
1192: * This is a recursive function.
1.23 kristaps 1193: * FIXME: if we're in a literal context (<screen> or <programlisting> or
1194: * whatever), don't print inline macros.
1.1 kristaps 1195: */
1196: static void
1197: pnode_print(struct parse *p, struct pnode *pn)
1198: {
1199: struct pnode *pp;
1200: char *cp;
1.10 kristaps 1201: int last, sv;
1.1 kristaps 1202:
1203: if (NULL == pn)
1204: return;
1205:
1.10 kristaps 1206: sv = p->newln;
1.1 kristaps 1207:
1208: switch (pn->node) {
1.27 kristaps 1209: case (NODE_APPLICATION):
1210: pnode_printmopen(p);
1211: fputs("Nm", stdout);
1212: break;
1.30 kristaps 1213: case (NODE_ANCHOR):
1214: /* Don't print anything! */
1215: return;
1.4 kristaps 1216: case (NODE_ARG):
1.10 kristaps 1217: pnode_printarg(p, pn);
1.4 kristaps 1218: pnode_unlinksub(pn);
1219: break;
1.1 kristaps 1220: case (NODE_CITEREFENTRY):
1.34 kristaps 1221: pnode_printmopen(p);
1222: fputs("Xr", stdout);
1.1 kristaps 1223: pnode_printciterefentry(p, pn);
1.4 kristaps 1224: pnode_unlinksub(pn);
1.1 kristaps 1225: break;
1226: case (NODE_CODE):
1.10 kristaps 1227: pnode_printmopen(p);
1.13 kristaps 1228: fputs("Li", stdout);
1.4 kristaps 1229: break;
1230: case (NODE_COMMAND):
1.10 kristaps 1231: pnode_printmopen(p);
1.13 kristaps 1232: fputs("Nm", stdout);
1233: break;
1.33 kristaps 1234: case (NODE_CONSTANT):
1235: pnode_printmopen(p);
1236: fputs("Dv", stdout);
1237: break;
1.13 kristaps 1238: case (NODE_EMPHASIS):
1239: pnode_printmopen(p);
1240: fputs("Em", stdout);
1.1 kristaps 1241: break;
1.21 kristaps 1242: case (NODE_ENVAR):
1243: pnode_printmopen(p);
1244: fputs("Ev", stdout);
1245: break;
1.17 kristaps 1246: case (NODE_FILENAME):
1247: pnode_printmopen(p);
1248: fputs("Pa", stdout);
1249: break;
1.3 kristaps 1250: case (NODE_FUNCTION):
1.10 kristaps 1251: pnode_printmopen(p);
1.13 kristaps 1252: fputs("Fn", stdout);
1.3 kristaps 1253: break;
1254: case (NODE_FUNCPROTOTYPE):
1.10 kristaps 1255: assert(p->newln);
1.3 kristaps 1256: pnode_printfuncprototype(p, pn);
1.4 kristaps 1257: pnode_unlinksub(pn);
1.3 kristaps 1258: break;
1.1 kristaps 1259: case (NODE_FUNCSYNOPSISINFO):
1.10 kristaps 1260: pnode_printmopen(p);
1.13 kristaps 1261: fputs("Fd", stdout);
1.16 kristaps 1262: break;
1263: case (NODE_ITEMIZEDLIST):
1264: assert(p->newln);
1.25 kristaps 1265: pnode_printlist(p, pn);
1266: pnode_unlinksub(pn);
1.24 kristaps 1267: break;
1268: case (NODE_GROUP):
1269: pnode_printgroup(p, pn);
1270: pnode_unlinksub(pn);
1.10 kristaps 1271: break;
1.19 kristaps 1272: case (NODE_LITERAL):
1273: pnode_printmopen(p);
1274: fputs("Li", stdout);
1275: break;
1.40 kristaps 1276: case (NODE_MML_MATH):
1277: if ( ! p->newln)
1278: putchar('\n');
1279: puts(".EQ");
1280: p->newln = 0;
1281: break;
1282: case (NODE_MML_MFENCED):
1.41 ! kristaps 1283: pnode_printmathfenced(p, pn);
! 1284: pnode_unlinksub(pn);
1.40 kristaps 1285: break;
1286: case (NODE_MML_MROW):
1287: case (NODE_MML_MI):
1288: case (NODE_MML_MN):
1289: case (NODE_MML_MO):
1290: putchar('{');
1291: break;
1292: case (NODE_MML_MFRAC):
1293: case (NODE_MML_MSUB):
1294: case (NODE_MML_MSUP):
1295: pnode_printmath(p, pn);
1296: pnode_unlinksub(pn);
1297: break;
1.10 kristaps 1298: case (NODE_OPTION):
1299: pnode_printmopen(p);
1.13 kristaps 1300: fputs("Fl", stdout);
1.1 kristaps 1301: break;
1.25 kristaps 1302: case (NODE_ORDEREDLIST):
1303: assert(p->newln);
1304: pnode_printlist(p, pn);
1305: pnode_unlinksub(pn);
1306: break;
1.1 kristaps 1307: case (NODE_PARA):
1.10 kristaps 1308: assert(p->newln);
1.13 kristaps 1309: if (NULL != pn->parent &&
1310: NODE_LISTITEM == pn->parent->node)
1311: break;
1.1 kristaps 1312: puts(".Pp");
1.3 kristaps 1313: break;
1314: case (NODE_PARAMETER):
1.10 kristaps 1315: /* Suppress non-text children... */
1316: pnode_printmopen(p);
1317: fputs("Fa \"", stdout);
1.32 kristaps 1318: pnode_printmacrolinetext(p, pn, MACROLINE_NOWS);
1319: fputs("\"", stdout);
1.4 kristaps 1320: pnode_unlinksub(pn);
1.1 kristaps 1321: break;
1.28 kristaps 1322: case (NODE_QUOTE):
1323: pnode_printmopen(p);
1324: fputs("Qo", stdout);
1325: break;
1.1 kristaps 1326: case (NODE_PROGRAMLISTING):
1.22 kristaps 1327: /* FALLTHROUGH */
1328: case (NODE_SCREEN):
1.10 kristaps 1329: assert(p->newln);
1.1 kristaps 1330: puts(".Bd -literal");
1.15 kristaps 1331: break;
1332: case (NODE_REFENTRYINFO):
1333: /* Suppress. */
1334: pnode_unlinksub(pn);
1.1 kristaps 1335: break;
1336: case (NODE_REFMETA):
1.7 kristaps 1337: abort();
1.1 kristaps 1338: break;
1339: case (NODE_REFNAME):
1.10 kristaps 1340: /* Suppress non-text children... */
1341: pnode_printmopen(p);
1.13 kristaps 1342: fputs("Nm", stdout);
1343: p->newln = 0;
1.10 kristaps 1344: pnode_printmacrolinepart(p, pn);
1.4 kristaps 1345: pnode_unlinksub(pn);
1.10 kristaps 1346: break;
1.1 kristaps 1347: case (NODE_REFNAMEDIV):
1.10 kristaps 1348: assert(p->newln);
1.1 kristaps 1349: puts(".Sh NAME");
1350: break;
1351: case (NODE_REFPURPOSE):
1.10 kristaps 1352: assert(p->newln);
1.13 kristaps 1353: pnode_printmopen(p);
1354: fputs("Nd", stdout);
1.10 kristaps 1355: break;
1.1 kristaps 1356: case (NODE_REFSYNOPSISDIV):
1.10 kristaps 1357: assert(p->newln);
1.6 kristaps 1358: pnode_printrefsynopsisdiv(p, pn);
1.10 kristaps 1359: puts(".Sh SYNOPSIS");
1.1 kristaps 1360: break;
1361: case (NODE_REFSECT1):
1.20 kristaps 1362: /* FALLTHROUGH */
1363: case (NODE_REFSECT2):
1.29 kristaps 1364: /* FALLTHROUGH */
1365: case (NODE_REFSECT3):
1366: /* FALLTHROUGH */
1367: case (NODE_REFSECTION):
1368: /* FALLTHROUGH */
1369: case (NODE_NOTE):
1370: /* FALLTHROUGH */
1371: case (NODE_TIP):
1372: /* FALLTHROUGH */
1373: case (NODE_CAUTION):
1374: /* FALLTHROUGH */
1375: case (NODE_WARNING):
1.10 kristaps 1376: assert(p->newln);
1.1 kristaps 1377: pnode_printrefsect(p, pn);
1378: break;
1.13 kristaps 1379: case (NODE_REPLACEABLE):
1380: pnode_printmopen(p);
1381: fputs("Ar", stdout);
1382: break;
1.19 kristaps 1383: case (NODE_SBR):
1384: assert(p->newln);
1385: puts(".br");
1386: break;
1.30 kristaps 1387: case (NODE_SGMLTAG):
1388: pnode_printmopen(p);
1389: fputs("Li", stdout);
1390: break;
1.8 kristaps 1391: case (NODE_STRUCTNAME):
1.10 kristaps 1392: pnode_printmopen(p);
1.13 kristaps 1393: fputs("Vt", stdout);
1.25 kristaps 1394: break;
1395: case (NODE_TABLE):
1.35 kristaps 1396: /* FALLTHROUGH */
1397: case (NODE_INFORMALTABLE):
1.25 kristaps 1398: assert(p->newln);
1399: pnode_printtable(p, pn);
1400: pnode_unlinksub(pn);
1.10 kristaps 1401: break;
1.1 kristaps 1402: case (NODE_TEXT):
1.13 kristaps 1403: if (0 == p->newln)
1404: putchar(' ');
1.37 kristaps 1405:
1.1 kristaps 1406: bufclear(p);
1407: bufappend(p, pn);
1.37 kristaps 1408:
1409: if (0 == p->bsz) {
1410: assert(pn->real != pn->b);
1411: break;
1412: }
1413:
1.1 kristaps 1414: /*
1415: * Output all characters, squeezing out whitespace
1416: * between newlines.
1417: * XXX: all whitespace, including tabs (?).
1418: * Remember to escape control characters and escapes.
1419: */
1.10 kristaps 1420: assert(p->bsz);
1.20 kristaps 1421: cp = p->b;
1.37 kristaps 1422:
1.20 kristaps 1423: /*
1424: * There's often a superfluous "-" in its <option> tags
1425: * before the actual flags themselves.
1426: * "Fl" does this for us, so remove it.
1427: */
1428: if (NULL != pn->parent &&
1429: NODE_OPTION == pn->parent->node &&
1430: '-' == *cp)
1431: cp++;
1432: for (last = '\n'; '\0' != *cp; ) {
1.1 kristaps 1433: if ('\n' == last) {
1434: /* Consume all whitespace. */
1435: if (isspace((int)*cp)) {
1436: while (isspace((int)*cp))
1437: cp++;
1438: continue;
1439: } else if ('\'' == *cp || '.' == *cp)
1440: fputs("\\&", stdout);
1441: }
1442: putchar(last = *cp++);
1443: /* If we're a character escape, escape us. */
1444: if ('\\' == last)
1445: putchar('e');
1446: }
1.10 kristaps 1447: p->newln = 0;
1.1 kristaps 1448: break;
1.39 kristaps 1449: case (NODE_TYPE):
1450: pnode_printmopen(p);
1451: fputs("Vt", stdout);
1452: break;
1.26 kristaps 1453: case (NODE_USERINPUT):
1454: pnode_printmopen(p);
1455: fputs("Li", stdout);
1456: break;
1.13 kristaps 1457: case (NODE_VARIABLELIST):
1458: assert(p->newln);
1459: pnode_printvariablelist(p, pn);
1460: pnode_unlinksub(pn);
1461: break;
1462: case (NODE_VARLISTENTRY):
1463: assert(p->newln);
1464: pnode_printvarlistentry(p, pn);
1465: break;
1.26 kristaps 1466: case (NODE_VARNAME):
1.23 kristaps 1467: pnode_printmopen(p);
1.26 kristaps 1468: fputs("Va", stdout);
1.23 kristaps 1469: break;
1.1 kristaps 1470: default:
1471: break;
1472: }
1473:
1474: TAILQ_FOREACH(pp, &pn->childq, child)
1475: pnode_print(p, pp);
1476:
1477: switch (pn->node) {
1.40 kristaps 1478: case (NODE_MML_MATH):
1479: if ( ! p->newln)
1480: putchar('\n');
1481: puts(".EN");
1482: p->newln = 1;
1483: break;
1484: case (NODE_MML_MROW):
1485: case (NODE_MML_MI):
1486: case (NODE_MML_MN):
1487: case (NODE_MML_MO):
1488: putchar('}');
1489: break;
1.27 kristaps 1490: case (NODE_APPLICATION):
1.10 kristaps 1491: case (NODE_ARG):
1.34 kristaps 1492: case (NODE_CITEREFENTRY):
1.10 kristaps 1493: case (NODE_CODE):
1494: case (NODE_COMMAND):
1.33 kristaps 1495: case (NODE_CONSTANT):
1.13 kristaps 1496: case (NODE_EMPHASIS):
1.21 kristaps 1497: case (NODE_ENVAR):
1.17 kristaps 1498: case (NODE_FILENAME):
1.10 kristaps 1499: case (NODE_FUNCTION):
1500: case (NODE_FUNCSYNOPSISINFO):
1.19 kristaps 1501: case (NODE_LITERAL):
1.10 kristaps 1502: case (NODE_OPTION):
1503: case (NODE_PARAMETER):
1.13 kristaps 1504: case (NODE_REPLACEABLE):
1505: case (NODE_REFPURPOSE):
1.30 kristaps 1506: case (NODE_SGMLTAG):
1.10 kristaps 1507: case (NODE_STRUCTNAME):
1508: case (NODE_TEXT):
1.39 kristaps 1509: case (NODE_TYPE):
1.23 kristaps 1510: case (NODE_USERINPUT):
1.26 kristaps 1511: case (NODE_VARNAME):
1.37 kristaps 1512: pnode_printmclosepunct(p, pn, sv);
1.28 kristaps 1513: break;
1514: case (NODE_QUOTE):
1515: pnode_printmclose(p, sv);
1516: sv = p->newln;
1517: pnode_printmopen(p);
1518: fputs("Qc", stdout);
1.10 kristaps 1519: pnode_printmclose(p, sv);
1520: break;
1.12 kristaps 1521: case (NODE_REFNAME):
1522: /*
1523: * If we're in the NAME macro and we have multiple
1524: * <refname> macros in sequence, then print out a
1525: * trailing comma before the newline.
1526: */
1527: if (NULL != pn->parent &&
1528: NODE_REFNAMEDIV == pn->parent->node &&
1529: NULL != TAILQ_NEXT(pn, child) &&
1530: NODE_REFNAME == TAILQ_NEXT(pn, child)->node)
1531: fputs(" ,", stdout);
1532: pnode_printmclose(p, sv);
1533: break;
1.1 kristaps 1534: case (NODE_PROGRAMLISTING):
1.22 kristaps 1535: /* FALLTHROUGH */
1536: case (NODE_SCREEN):
1.10 kristaps 1537: assert(p->newln);
1.1 kristaps 1538: puts(".Ed");
1.10 kristaps 1539: p->newln = 1;
1.1 kristaps 1540: break;
1541: default:
1542: break;
1543: }
1544: }
1545:
1546: /*
1547: * Loop around the read buffer until we've drained it of all data.
1548: * Invoke the parser context with each buffer fill.
1549: */
1550: static int
1551: readfile(XML_Parser xp, int fd,
1552: char *b, size_t bsz, const char *fn)
1553: {
1554: struct parse p;
1555: int rc;
1556: ssize_t ssz;
1557:
1558: memset(&p, 0, sizeof(struct parse));
1559:
1560: p.b = malloc(p.bsz = p.mbsz = 1024);
1.12 kristaps 1561: p.fname = fn;
1562: p.xml = xp;
1.1 kristaps 1563:
1564: XML_SetCharacterDataHandler(xp, xml_char);
1565: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
1566: XML_SetUserData(xp, &p);
1567:
1568: while ((ssz = read(fd, b, bsz)) >= 0) {
1569: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
1.30 kristaps 1570: fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
1571: XML_GetCurrentLineNumber(xp),
1572: XML_GetCurrentColumnNumber(xp),
1.1 kristaps 1573: XML_ErrorString
1574: (XML_GetErrorCode(xp)));
1575: else if ( ! p.stop && ssz > 0)
1576: continue;
1577: /*
1578: * Exit when we've read all or errors have occured
1579: * during the parse sequence.
1580: */
1.10 kristaps 1581: p.newln = 1;
1.7 kristaps 1582: pnode_printprologue(&p, p.root);
1.1 kristaps 1583: pnode_print(&p, p.root);
1584: pnode_free(p.root);
1585: free(p.b);
1586: return(0 != rc && ! p.stop);
1587: }
1588:
1589: /* Read error has occured. */
1590: perror(fn);
1591: pnode_free(p.root);
1592: free(p.b);
1593: return(0);
1594: }
1595:
1596: int
1597: main(int argc, char *argv[])
1598: {
1599: XML_Parser xp;
1600: const char *fname;
1601: char *buf;
1.38 kristaps 1602: int fd, rc, ch;
1603: const char *progname;
1604:
1605: progname = strrchr(argv[0], '/');
1606: if (progname == NULL)
1607: progname = argv[0];
1608: else
1609: ++progname;
1.1 kristaps 1610:
1611: fname = "-";
1612: xp = NULL;
1613: buf = NULL;
1614: rc = 0;
1615:
1.38 kristaps 1616: while (-1 != (ch = getopt(argc, argv, "W")))
1617: switch (ch) {
1618: case ('W'):
1619: warn = 1;
1620: break;
1621: default:
1622: goto usage;
1623: }
1.1 kristaps 1624:
1625: argc -= optind;
1626: argv += optind;
1627:
1628: if (argc > 1)
1629: return(EXIT_FAILURE);
1630: else if (argc > 0)
1631: fname = argv[0];
1632:
1633: /* Read from stdin or a file. */
1634: fd = 0 == strcmp(fname, "-") ?
1635: STDIN_FILENO : open(fname, O_RDONLY, 0);
1636:
1637: /*
1638: * Open file for reading.
1639: * Allocate a read buffer.
1640: * Create the parser context.
1641: * Dive directly into the parse.
1642: */
1643: if (-1 == fd)
1644: perror(fname);
1645: else if (NULL == (buf = malloc(4096)))
1646: perror(NULL);
1647: else if (NULL == (xp = XML_ParserCreate(NULL)))
1648: perror(NULL);
1649: else if ( ! readfile(xp, fd, buf, 4096, fname))
1650: rc = 1;
1651:
1652: XML_ParserFree(xp);
1653: free(buf);
1654: if (STDIN_FILENO != fd)
1655: close(fd);
1656: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1.38 kristaps 1657:
1658: usage:
1659: fprintf(stderr, "usage: %s [-W]\n", progname);
1660: return(EXIT_FAILURE);
1.1 kristaps 1661: }
CVSweb