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