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