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