Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.62
1.62 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.61 2019/03/22 17:28:59 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) {
1.62 ! schwarze 774: case NODE_CAUTION:
! 775: case NODE_NOTE:
! 776: case NODE_TIP:
! 777: case NODE_WARNING:
1.52 schwarze 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) {
1.62 ! schwarze 791: case NODE_PREFACE:
1.52 schwarze 792: title = "Preface";
793: break;
1.62 ! schwarze 794: case NODE_CAUTION:
1.52 schwarze 795: title = "Caution";
796: break;
1.62 ! schwarze 797: case NODE_NOTE:
1.52 schwarze 798: title = "Note";
799: break;
1.62 ! schwarze 800: case NODE_TIP:
1.52 schwarze 801: title = "Tip";
802: break;
1.62 ! schwarze 803: case NODE_WARNING:
1.52 schwarze 804: title = "Warning";
805: break;
806: default:
807: title = "Unknown";
808: break;
809: }
810: }
811:
812: switch (level) {
1.62 ! schwarze 813: case 1:
1.20 kristaps 814: fputs(".Sh", stdout);
1.29 kristaps 815: break;
1.62 ! 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) {
1.62 ! schwarze 984: case NODE_MML_MSUP:
1.42 kristaps 985: fputs(" sup ", stdout);
1.40 kristaps 986: break;
1.62 ! schwarze 987: case NODE_MML_MFRAC:
1.42 kristaps 988: fputs(" over ", stdout);
1.40 kristaps 989: break;
1.62 ! schwarze 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:
1286: switch (pn->node) {
1.62 ! schwarze 1287: case NODE_APPLICATION:
1.27 kristaps 1288: pnode_printmopen(p);
1289: fputs("Nm", stdout);
1290: break;
1.62 ! schwarze 1291: case NODE_ANCHOR:
1.30 kristaps 1292: /* Don't print anything! */
1293: return;
1.62 ! schwarze 1294: case NODE_ARG:
1.10 kristaps 1295: pnode_printarg(p, pn);
1.4 kristaps 1296: pnode_unlinksub(pn);
1297: break;
1.62 ! schwarze 1298: case NODE_AUTHOR:
1.50 schwarze 1299: pnode_printmopen(p);
1300: fputs("An", stdout);
1301: break;
1.62 ! schwarze 1302: case NODE_AUTHORGROUP:
1.50 schwarze 1303: assert(p->newln);
1304: puts(".An -split");
1305: break;
1.62 ! schwarze 1306: case NODE_BOOKINFO:
1.50 schwarze 1307: assert(p->newln);
1308: puts(".Sh NAME");
1309: break;
1.62 ! schwarze 1310: case NODE_CITEREFENTRY:
1.34 kristaps 1311: pnode_printmopen(p);
1312: fputs("Xr", stdout);
1.1 kristaps 1313: pnode_printciterefentry(p, pn);
1.4 kristaps 1314: pnode_unlinksub(pn);
1.1 kristaps 1315: break;
1.62 ! schwarze 1316: case NODE_CODE:
1.10 kristaps 1317: pnode_printmopen(p);
1.13 kristaps 1318: fputs("Li", stdout);
1.4 kristaps 1319: break;
1.62 ! schwarze 1320: case NODE_COMMAND:
1.10 kristaps 1321: pnode_printmopen(p);
1.13 kristaps 1322: fputs("Nm", stdout);
1323: break;
1.62 ! schwarze 1324: case NODE_CONSTANT:
1.33 kristaps 1325: pnode_printmopen(p);
1326: fputs("Dv", stdout);
1327: break;
1.62 ! schwarze 1328: case NODE_EDITOR:
1.50 schwarze 1329: puts("editor: ");
1330: pnode_printmopen(p);
1331: fputs("An", stdout);
1332: break;
1.62 ! schwarze 1333: case NODE_EMPHASIS:
! 1334: case NODE_FIRSTTERM:
1.13 kristaps 1335: pnode_printmopen(p);
1336: fputs("Em", stdout);
1.1 kristaps 1337: break;
1.62 ! schwarze 1338: case NODE_ENVAR:
1.21 kristaps 1339: pnode_printmopen(p);
1340: fputs("Ev", stdout);
1341: break;
1.62 ! schwarze 1342: case NODE_FILENAME:
1.17 kristaps 1343: pnode_printmopen(p);
1344: fputs("Pa", stdout);
1345: break;
1.62 ! schwarze 1346: case NODE_FUNCTION:
1.10 kristaps 1347: pnode_printmopen(p);
1.13 kristaps 1348: fputs("Fn", stdout);
1.3 kristaps 1349: break;
1.62 ! schwarze 1350: case NODE_FUNCPROTOTYPE:
1.10 kristaps 1351: assert(p->newln);
1.3 kristaps 1352: pnode_printfuncprototype(p, pn);
1.4 kristaps 1353: pnode_unlinksub(pn);
1.3 kristaps 1354: break;
1.62 ! schwarze 1355: case NODE_FUNCSYNOPSISINFO:
1.10 kristaps 1356: pnode_printmopen(p);
1.13 kristaps 1357: fputs("Fd", stdout);
1.16 kristaps 1358: break;
1.62 ! schwarze 1359: case NODE_INDEXTERM:
1.55 schwarze 1360: return;
1.62 ! schwarze 1361: case NODE_INFORMALEQUATION:
1.43 kristaps 1362: if ( ! p->newln)
1363: putchar('\n');
1364: puts(".EQ");
1365: p->newln = 0;
1366: break;
1.62 ! schwarze 1367: case NODE_INLINEEQUATION:
1.43 kristaps 1368: fputc('$', stdout);
1369: p->newln = 0;
1370: break;
1.62 ! schwarze 1371: case NODE_ITEMIZEDLIST:
1.16 kristaps 1372: assert(p->newln);
1.25 kristaps 1373: pnode_printlist(p, pn);
1374: pnode_unlinksub(pn);
1.24 kristaps 1375: break;
1.62 ! schwarze 1376: case NODE_GROUP:
1.24 kristaps 1377: pnode_printgroup(p, pn);
1378: pnode_unlinksub(pn);
1.10 kristaps 1379: break;
1.62 ! schwarze 1380: case NODE_LEGALNOTICE:
1.50 schwarze 1381: assert(p->newln);
1382: puts(".Sh LEGAL NOTICE");
1383: break;
1.62 ! schwarze 1384: case NODE_LINK:
1.59 schwarze 1385: ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
1386: if (NULL == ccp)
1.58 schwarze 1387: break;
1388: pnode_printmopen(p);
1.59 schwarze 1389: printf("Sx %s\n", ccp);
1.58 schwarze 1390: p->newln = 1;
1391: return;
1.62 ! schwarze 1392: case NODE_LITERAL:
1.19 kristaps 1393: pnode_printmopen(p);
1394: fputs("Li", stdout);
1395: break;
1.62 ! schwarze 1396: case NODE_LITERALLAYOUT:
1.60 schwarze 1397: assert(p->newln);
1398: puts(".Bd -literal");
1399: break;
1.62 ! schwarze 1400: case NODE_MML_MFENCED:
1.41 kristaps 1401: pnode_printmathfenced(p, pn);
1402: pnode_unlinksub(pn);
1.40 kristaps 1403: break;
1.62 ! schwarze 1404: case NODE_MML_MROW:
! 1405: case NODE_MML_MI:
! 1406: case NODE_MML_MN:
! 1407: case NODE_MML_MO:
1.43 kristaps 1408: if (TAILQ_EMPTY(&pn->childq))
1409: break;
1410: fputs(" { ", stdout);
1.40 kristaps 1411: break;
1.62 ! schwarze 1412: case NODE_MML_MFRAC:
! 1413: case NODE_MML_MSUB:
! 1414: case NODE_MML_MSUP:
1.40 kristaps 1415: pnode_printmath(p, pn);
1416: pnode_unlinksub(pn);
1417: break;
1.62 ! schwarze 1418: case NODE_OPTION:
1.10 kristaps 1419: pnode_printmopen(p);
1.13 kristaps 1420: fputs("Fl", stdout);
1.1 kristaps 1421: break;
1.62 ! schwarze 1422: case NODE_ORDEREDLIST:
1.25 kristaps 1423: assert(p->newln);
1424: pnode_printlist(p, pn);
1425: pnode_unlinksub(pn);
1426: break;
1.62 ! schwarze 1427: case NODE_PARA:
1.54 schwarze 1428: pnode_printpara(p, pn);
1.3 kristaps 1429: break;
1.62 ! schwarze 1430: case NODE_PARAMETER:
1.10 kristaps 1431: /* Suppress non-text children... */
1432: pnode_printmopen(p);
1433: fputs("Fa \"", stdout);
1.32 kristaps 1434: pnode_printmacrolinetext(p, pn, MACROLINE_NOWS);
1435: fputs("\"", stdout);
1.4 kristaps 1436: pnode_unlinksub(pn);
1.1 kristaps 1437: break;
1.62 ! schwarze 1438: case NODE_QUOTE:
1.28 kristaps 1439: pnode_printmopen(p);
1440: fputs("Qo", stdout);
1441: break;
1.62 ! schwarze 1442: case NODE_PROGRAMLISTING:
! 1443: case NODE_SCREEN:
1.10 kristaps 1444: assert(p->newln);
1.60 schwarze 1445: printf(".Bd %s\n", ATTRVAL_MONOSPACED ==
1446: pnode_getattr(pn, ATTRKEY_CLASS) ?
1447: "-literal" : "-unfilled");
1.15 kristaps 1448: break;
1.62 ! schwarze 1449: case NODE_REFENTRYINFO:
1.15 kristaps 1450: /* Suppress. */
1451: pnode_unlinksub(pn);
1.1 kristaps 1452: break;
1.62 ! schwarze 1453: case NODE_REFMETA:
1.7 kristaps 1454: abort();
1.1 kristaps 1455: break;
1.62 ! schwarze 1456: case NODE_REFNAME:
1.10 kristaps 1457: /* Suppress non-text children... */
1458: pnode_printmopen(p);
1.13 kristaps 1459: fputs("Nm", stdout);
1460: p->newln = 0;
1.10 kristaps 1461: pnode_printmacrolinepart(p, pn);
1.4 kristaps 1462: pnode_unlinksub(pn);
1.10 kristaps 1463: break;
1.62 ! schwarze 1464: case NODE_REFNAMEDIV:
1.10 kristaps 1465: assert(p->newln);
1.1 kristaps 1466: puts(".Sh NAME");
1467: break;
1.62 ! schwarze 1468: case NODE_REFPURPOSE:
1.10 kristaps 1469: assert(p->newln);
1.13 kristaps 1470: pnode_printmopen(p);
1471: fputs("Nd", stdout);
1.10 kristaps 1472: break;
1.62 ! schwarze 1473: case NODE_REFSYNOPSISDIV:
1.10 kristaps 1474: assert(p->newln);
1.6 kristaps 1475: pnode_printrefsynopsisdiv(p, pn);
1.10 kristaps 1476: puts(".Sh SYNOPSIS");
1.1 kristaps 1477: break;
1.62 ! schwarze 1478: case NODE_PREFACE:
! 1479: case NODE_SECTION:
! 1480: case NODE_NOTE:
! 1481: case NODE_TIP:
! 1482: case NODE_CAUTION:
! 1483: case NODE_WARNING:
1.10 kristaps 1484: assert(p->newln);
1.1 kristaps 1485: pnode_printrefsect(p, pn);
1486: break;
1.62 ! schwarze 1487: case NODE_REPLACEABLE:
1.13 kristaps 1488: pnode_printmopen(p);
1489: fputs("Ar", stdout);
1490: break;
1.62 ! schwarze 1491: case NODE_SBR:
1.19 kristaps 1492: assert(p->newln);
1493: puts(".br");
1494: break;
1.62 ! schwarze 1495: case NODE_SGMLTAG:
1.30 kristaps 1496: pnode_printmopen(p);
1497: fputs("Li", stdout);
1498: break;
1.62 ! schwarze 1499: case NODE_STRUCTNAME:
1.10 kristaps 1500: pnode_printmopen(p);
1.13 kristaps 1501: fputs("Vt", stdout);
1.25 kristaps 1502: break;
1.62 ! schwarze 1503: case NODE_TABLE:
! 1504: case NODE_INFORMALTABLE:
1.25 kristaps 1505: assert(p->newln);
1506: pnode_printtable(p, pn);
1507: pnode_unlinksub(pn);
1.10 kristaps 1508: break;
1.62 ! schwarze 1509: case NODE_TEXT:
1.13 kristaps 1510: if (0 == p->newln)
1511: putchar(' ');
1.37 kristaps 1512:
1.1 kristaps 1513: bufclear(p);
1514: bufappend(p, pn);
1.37 kristaps 1515:
1516: if (0 == p->bsz) {
1517: assert(pn->real != pn->b);
1518: break;
1519: }
1520:
1.1 kristaps 1521: /*
1522: * Output all characters, squeezing out whitespace
1.44 schwarze 1523: * between newlines.
1.1 kristaps 1524: * XXX: all whitespace, including tabs (?).
1525: * Remember to escape control characters and escapes.
1526: */
1.10 kristaps 1527: assert(p->bsz);
1.20 kristaps 1528: cp = p->b;
1.37 kristaps 1529:
1.20 kristaps 1530: /*
1531: * There's often a superfluous "-" in its <option> tags
1532: * before the actual flags themselves.
1533: * "Fl" does this for us, so remove it.
1534: */
1535: if (NULL != pn->parent &&
1536: NODE_OPTION == pn->parent->node &&
1537: '-' == *cp)
1538: cp++;
1539: for (last = '\n'; '\0' != *cp; ) {
1.1 kristaps 1540: if ('\n' == last) {
1541: /* Consume all whitespace. */
1.46 schwarze 1542: if (isspace((unsigned char)*cp)) {
1543: while (isspace((unsigned char)*cp))
1.1 kristaps 1544: cp++;
1545: continue;
1546: } else if ('\'' == *cp || '.' == *cp)
1547: fputs("\\&", stdout);
1548: }
1549: putchar(last = *cp++);
1550: /* If we're a character escape, escape us. */
1551: if ('\\' == last)
1552: putchar('e');
1553: }
1.10 kristaps 1554: p->newln = 0;
1.1 kristaps 1555: break;
1.62 ! schwarze 1556: case NODE_TITLE:
1.50 schwarze 1557: if (pn->parent->node == NODE_BOOKINFO) {
1558: pnode_printmopen(p);
1559: fputs("Nd", stdout);
1560: }
1561: break;
1.62 ! schwarze 1562: case NODE_TYPE:
1.39 kristaps 1563: pnode_printmopen(p);
1564: fputs("Vt", stdout);
1565: break;
1.62 ! schwarze 1566: case NODE_USERINPUT:
1.26 kristaps 1567: pnode_printmopen(p);
1568: fputs("Li", stdout);
1569: break;
1.62 ! schwarze 1570: case NODE_VARIABLELIST:
1.13 kristaps 1571: assert(p->newln);
1572: pnode_printvariablelist(p, pn);
1573: pnode_unlinksub(pn);
1574: break;
1.62 ! schwarze 1575: case NODE_VARLISTENTRY:
1.13 kristaps 1576: assert(p->newln);
1577: pnode_printvarlistentry(p, pn);
1578: break;
1.62 ! schwarze 1579: case NODE_VARNAME:
1.23 kristaps 1580: pnode_printmopen(p);
1.26 kristaps 1581: fputs("Va", stdout);
1.23 kristaps 1582: break;
1.1 kristaps 1583: default:
1584: break;
1585: }
1586:
1587: TAILQ_FOREACH(pp, &pn->childq, child)
1588: pnode_print(p, pp);
1589:
1590: switch (pn->node) {
1.62 ! schwarze 1591: case NODE_INFORMALEQUATION:
1.40 kristaps 1592: if ( ! p->newln)
1593: putchar('\n');
1594: puts(".EN");
1595: p->newln = 1;
1596: break;
1.62 ! schwarze 1597: case NODE_INLINEEQUATION:
1.43 kristaps 1598: fputs("$ ", stdout);
1599: p->newln = sv;
1600: break;
1.62 ! schwarze 1601: case NODE_MML_MROW:
! 1602: case NODE_MML_MI:
! 1603: case NODE_MML_MN:
! 1604: case NODE_MML_MO:
1.43 kristaps 1605: if (TAILQ_EMPTY(&pn->childq))
1606: break;
1607: fputs(" } ", stdout);
1.40 kristaps 1608: break;
1.62 ! schwarze 1609: case NODE_APPLICATION:
! 1610: case NODE_ARG:
! 1611: case NODE_AUTHOR:
! 1612: case NODE_CITEREFENTRY:
! 1613: case NODE_CODE:
! 1614: case NODE_COMMAND:
! 1615: case NODE_CONSTANT:
! 1616: case NODE_EDITOR:
! 1617: case NODE_EMPHASIS:
! 1618: case NODE_ENVAR:
! 1619: case NODE_FILENAME:
! 1620: case NODE_FIRSTTERM:
! 1621: case NODE_FUNCTION:
! 1622: case NODE_FUNCSYNOPSISINFO:
! 1623: case NODE_LITERAL:
! 1624: case NODE_OPTION:
! 1625: case NODE_PARAMETER:
! 1626: case NODE_REPLACEABLE:
! 1627: case NODE_REFPURPOSE:
! 1628: case NODE_SGMLTAG:
! 1629: case NODE_STRUCTNAME:
! 1630: case NODE_TEXT:
! 1631: case NODE_TYPE:
! 1632: case NODE_USERINPUT:
! 1633: case NODE_VARNAME:
1.37 kristaps 1634: pnode_printmclosepunct(p, pn, sv);
1.28 kristaps 1635: break;
1.62 ! schwarze 1636: case NODE_QUOTE:
1.28 kristaps 1637: pnode_printmclose(p, sv);
1638: sv = p->newln;
1639: pnode_printmopen(p);
1640: fputs("Qc", stdout);
1.10 kristaps 1641: pnode_printmclose(p, sv);
1642: break;
1.62 ! schwarze 1643: case NODE_REFNAME:
1.12 kristaps 1644: /*
1645: * If we're in the NAME macro and we have multiple
1646: * <refname> macros in sequence, then print out a
1647: * trailing comma before the newline.
1648: */
1.44 schwarze 1649: if (NULL != pn->parent &&
1.12 kristaps 1650: NODE_REFNAMEDIV == pn->parent->node &&
1651: NULL != TAILQ_NEXT(pn, child) &&
1.44 schwarze 1652: NODE_REFNAME == TAILQ_NEXT(pn, child)->node)
1.12 kristaps 1653: fputs(" ,", stdout);
1654: pnode_printmclose(p, sv);
1.52 schwarze 1655: break;
1.62 ! schwarze 1656: case NODE_PREFACE:
! 1657: case NODE_SECTION:
! 1658: case NODE_NOTE:
! 1659: case NODE_TIP:
! 1660: case NODE_CAUTION:
! 1661: case NODE_WARNING:
1.52 schwarze 1662: p->level--;
1.12 kristaps 1663: break;
1.62 ! schwarze 1664: case NODE_LITERALLAYOUT:
! 1665: case NODE_PROGRAMLISTING:
! 1666: case NODE_SCREEN:
1.10 kristaps 1667: assert(p->newln);
1.1 kristaps 1668: puts(".Ed");
1.10 kristaps 1669: p->newln = 1;
1.50 schwarze 1670: break;
1.62 ! schwarze 1671: case NODE_TITLE:
1.50 schwarze 1672: if (pn->parent->node == NODE_BOOKINFO) {
1673: pnode_printmclose(p, 1);
1674: puts(".Sh AUTHORS");
1675: }
1.1 kristaps 1676: break;
1677: default:
1678: break;
1679: }
1680: }
1681:
1682: /*
1683: * Loop around the read buffer until we've drained it of all data.
1684: * Invoke the parser context with each buffer fill.
1685: */
1686: static int
1.44 schwarze 1687: readfile(XML_Parser xp, int fd,
1.1 kristaps 1688: char *b, size_t bsz, const char *fn)
1689: {
1690: struct parse p;
1691: int rc;
1692: ssize_t ssz;
1693:
1694: memset(&p, 0, sizeof(struct parse));
1695:
1696: p.b = malloc(p.bsz = p.mbsz = 1024);
1.12 kristaps 1697: p.fname = fn;
1698: p.xml = xp;
1.1 kristaps 1699:
1700: XML_SetCharacterDataHandler(xp, xml_char);
1701: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
1702: XML_SetUserData(xp, &p);
1703:
1704: while ((ssz = read(fd, b, bsz)) >= 0) {
1705: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
1.30 kristaps 1706: fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
1707: XML_GetCurrentLineNumber(xp),
1.44 schwarze 1708: XML_GetCurrentColumnNumber(xp),
1.1 kristaps 1709: XML_ErrorString
1710: (XML_GetErrorCode(xp)));
1711: else if ( ! p.stop && ssz > 0)
1712: continue;
1.44 schwarze 1713: /*
1.1 kristaps 1714: * Exit when we've read all or errors have occured
1715: * during the parse sequence.
1716: */
1.10 kristaps 1717: p.newln = 1;
1.7 kristaps 1718: pnode_printprologue(&p, p.root);
1.1 kristaps 1719: pnode_print(&p, p.root);
1720: pnode_free(p.root);
1721: free(p.b);
1722: return(0 != rc && ! p.stop);
1723: }
1724:
1725: /* Read error has occured. */
1726: perror(fn);
1727: pnode_free(p.root);
1728: free(p.b);
1729: return(0);
1730: }
1731:
1732: int
1733: main(int argc, char *argv[])
1734: {
1735: XML_Parser xp;
1736: const char *fname;
1737: char *buf;
1.38 kristaps 1738: int fd, rc, ch;
1739: const char *progname;
1740:
1741: progname = strrchr(argv[0], '/');
1742: if (progname == NULL)
1743: progname = argv[0];
1744: else
1745: ++progname;
1.1 kristaps 1746:
1747: fname = "-";
1748: xp = NULL;
1749: buf = NULL;
1750: rc = 0;
1751:
1.38 kristaps 1752: while (-1 != (ch = getopt(argc, argv, "W")))
1753: switch (ch) {
1.62 ! schwarze 1754: case 'W':
1.38 kristaps 1755: warn = 1;
1756: break;
1757: default:
1758: goto usage;
1759: }
1.1 kristaps 1760:
1761: argc -= optind;
1762: argv += optind;
1763:
1.45 schwarze 1764: if (argc > 1) {
1765: fprintf(stderr, "%s: Too many arguments\n", argv[1]);
1766: goto usage;
1767: } else if (argc > 0)
1.1 kristaps 1768: fname = argv[0];
1769:
1770: /* Read from stdin or a file. */
1.44 schwarze 1771: fd = 0 == strcmp(fname, "-") ?
1.1 kristaps 1772: STDIN_FILENO : open(fname, O_RDONLY, 0);
1773:
1774: /*
1775: * Open file for reading.
1776: * Allocate a read buffer.
1777: * Create the parser context.
1778: * Dive directly into the parse.
1779: */
1780: if (-1 == fd)
1781: perror(fname);
1782: else if (NULL == (buf = malloc(4096)))
1783: perror(NULL);
1784: else if (NULL == (xp = XML_ParserCreate(NULL)))
1785: perror(NULL);
1786: else if ( ! readfile(xp, fd, buf, 4096, fname))
1787: rc = 1;
1788:
1789: XML_ParserFree(xp);
1790: free(buf);
1791: if (STDIN_FILENO != fd)
1792: close(fd);
1793: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1.38 kristaps 1794:
1795: usage:
1.45 schwarze 1796: fprintf(stderr, "usage: %s [-W] [input_filename]\n", progname);
1.38 kristaps 1797: return(EXIT_FAILURE);
1.1 kristaps 1798: }
CVSweb