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