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