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