Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.50
1.50 ! schwarze 1: /* $Id: docbook2mdoc.c,v 1.49 2019/03/22 15:54:42 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.2 kristaps 809: puts(".Dd $Mdocdate" "$");
1.13 kristaps 810: fputs(".Dt", stdout);
811: p->newln = 0;
1.1 kristaps 812:
1.13 kristaps 813: if (NULL != title)
1.12 kristaps 814: pnode_printmacrolinetext(p, title, MACROLINE_UPPER);
1.13 kristaps 815: else
816: fputs(" UNKNOWN ", stdout);
817:
818: if (NULL == manvol) {
819: puts(" 1");
820: p->newln = 1;
1.1 kristaps 821: } else
822: pnode_printmacroline(p, manvol);
823:
824: puts(".Os");
825: }
826:
1.3 kristaps 827: static void
828: pnode_printfuncdef(struct parse *p, struct pnode *pn)
829: {
830: struct pnode *pp, *ftype, *func;
831:
1.13 kristaps 832: assert(p->newln);
1.3 kristaps 833: ftype = func = NULL;
834: TAILQ_FOREACH(pp, &pn->childq, child)
835: if (NODE_TEXT == pp->node)
836: ftype = pp;
837: else if (NODE_FUNCTION == pp->node)
838: func = pp;
839:
840: if (NULL != ftype) {
1.13 kristaps 841: fputs(".Ft", stdout);
842: p->newln = 0;
1.3 kristaps 843: pnode_printmacroline(p, ftype);
844: }
845:
846: if (NULL != func) {
1.13 kristaps 847: fputs(".Fo", stdout);
848: p->newln = 0;
1.3 kristaps 849: pnode_printmacroline(p, func);
1.13 kristaps 850: } else {
1.3 kristaps 851: puts(".Fo UNKNOWN");
1.13 kristaps 852: p->newln = 1;
853: }
1.3 kristaps 854: }
855:
856: static void
857: pnode_printparamdef(struct parse *p, struct pnode *pn)
858: {
859: struct pnode *pp, *ptype, *param;
860:
1.13 kristaps 861: assert(p->newln);
1.3 kristaps 862: ptype = param = NULL;
863: TAILQ_FOREACH(pp, &pn->childq, child)
864: if (NODE_TEXT == pp->node)
865: ptype = pp;
866: else if (NODE_PARAMETER == pp->node)
867: param = pp;
868:
869: fputs(".Fa \"", stdout);
1.13 kristaps 870: p->newln = 0;
1.3 kristaps 871: if (NULL != ptype) {
1.32 kristaps 872: pnode_printmacrolinetext(p, ptype, MACROLINE_NOWS);
1.3 kristaps 873: putchar(' ');
874: }
875:
876: if (NULL != param)
877: pnode_printmacrolinepart(p, param);
878:
879: puts("\"");
1.13 kristaps 880: p->newln = 1;
1.3 kristaps 881: }
882:
1.40 kristaps 883: /*
1.41 kristaps 884: * The <mml:mfenced> node is a little peculiar.
885: * First, it can have arbitrary open and closing tokens, which default
886: * to parentheses.
887: * Second, >1 arguments are separated by commas.
888: */
889: static void
890: pnode_printmathfenced(struct parse *p, struct pnode *pn)
891: {
892: struct pnode *pp;
893: struct pattr *ap;
894:
895: TAILQ_FOREACH(ap, &pn->attrq, child)
896: if (ATTRKEY_OPEN == ap->key) {
897: printf("left %s ", ap->rawval);
898: break;
899: }
900: if (NULL == ap)
901: printf("left ( ");
902:
903: pp = TAILQ_FIRST(&pn->childq);
904: pnode_print(p, pp);
905:
906: while (NULL != (pp = TAILQ_NEXT(pp, child))) {
907: putchar(',');
908: pnode_print(p, pp);
909: }
910:
911: TAILQ_FOREACH(ap, &pn->attrq, child)
912: if (ATTRKEY_CLOSE == ap->key) {
913: printf("right %s ", ap->rawval);
914: break;
915: }
916: if (NULL == ap)
917: printf("right ) ");
918: }
919:
920: /*
1.40 kristaps 921: * These math nodes require special handling because they have infix
922: * syntax, instead of the usual prefix or prefix.
923: * So we need to break up the first and second child node with a
924: * particular eqn(7) word.
925: */
926: static void
927: pnode_printmath(struct parse *p, struct pnode *pn)
928: {
929: struct pnode *pp;
930:
931: pp = TAILQ_FIRST(&pn->childq);
932: pnode_print(p, pp);
933:
934: switch (pn->node) {
935: case (NODE_MML_MSUP):
1.42 kristaps 936: fputs(" sup ", stdout);
1.40 kristaps 937: break;
938: case (NODE_MML_MFRAC):
1.42 kristaps 939: fputs(" over ", stdout);
1.40 kristaps 940: break;
941: case (NODE_MML_MSUB):
1.42 kristaps 942: fputs(" sub ", stdout);
1.40 kristaps 943: break;
944: default:
945: break;
946: }
947:
948: pp = TAILQ_NEXT(pp, child);
949: pnode_print(p, pp);
950: }
951:
1.3 kristaps 952: static void
953: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
954: {
955: struct pnode *pp, *fdef;
956:
1.13 kristaps 957: assert(p->newln);
1.3 kristaps 958: TAILQ_FOREACH(fdef, &pn->childq, child)
1.44 schwarze 959: if (NODE_FUNCDEF == fdef->node)
1.3 kristaps 960: break;
961:
1.4 kristaps 962: if (NULL != fdef)
1.3 kristaps 963: pnode_printfuncdef(p, fdef);
1.4 kristaps 964: else
1.3 kristaps 965: puts(".Fo UNKNOWN");
966:
1.44 schwarze 967: TAILQ_FOREACH(pp, &pn->childq, child)
1.3 kristaps 968: if (NODE_PARAMDEF == pp->node)
969: pnode_printparamdef(p, pp);
970:
971: puts(".Fc");
1.13 kristaps 972: p->newln = 1;
1.3 kristaps 973: }
974:
1.44 schwarze 975: /*
1.10 kristaps 976: * The <arg> element is more complicated than it should be because text
977: * nodes are treated like ".Ar foo", but non-text nodes need to be
978: * re-sent into the printer (i.e., without the preceding ".Ar").
1.12 kristaps 979: * This also handles the case of "repetition" (or in other words, the
980: * ellipsis following an argument) and optionality.
1.10 kristaps 981: */
1.4 kristaps 982: static void
1.10 kristaps 983: pnode_printarg(struct parse *p, struct pnode *pn)
1.4 kristaps 984: {
985: struct pnode *pp;
1.12 kristaps 986: struct pattr *ap;
987: int isop, isrep;
988:
989: isop = 1;
990: isrep = 0;
1.44 schwarze 991: TAILQ_FOREACH(ap, &pn->attrq, child)
1.12 kristaps 992: if (ATTRKEY_CHOICE == ap->key &&
993: (ATTRVAL_PLAIN == ap->val ||
1.44 schwarze 994: ATTRVAL_REQ == ap->val))
1.12 kristaps 995: isop = 0;
996: else if (ATTRKEY_REP == ap->key &&
997: (ATTRVAL_REPEAT == ap->val))
998: isrep = 1;
999:
1000: if (isop) {
1001: pnode_printmopen(p);
1.13 kristaps 1002: fputs("Op", stdout);
1.12 kristaps 1003: }
1.4 kristaps 1004:
1.10 kristaps 1005: TAILQ_FOREACH(pp, &pn->childq, child) {
1006: if (NODE_TEXT == pp->node) {
1007: pnode_printmopen(p);
1.13 kristaps 1008: fputs("Ar", stdout);
1.44 schwarze 1009: }
1.10 kristaps 1010: pnode_print(p, pp);
1.44 schwarze 1011: if (NODE_TEXT == pp->node && isrep)
1.12 kristaps 1012: fputs("...", stdout);
1.10 kristaps 1013: }
1.4 kristaps 1014: }
1015:
1.24 kristaps 1016: static void
1017: pnode_printgroup(struct parse *p, struct pnode *pn)
1018: {
1019: struct pnode *pp, *np;
1020: struct pattr *ap;
1021: int isop, sv;
1022:
1023: isop = 1;
1.44 schwarze 1024: TAILQ_FOREACH(ap, &pn->attrq, child)
1.24 kristaps 1025: if (ATTRKEY_CHOICE == ap->key &&
1026: (ATTRVAL_PLAIN == ap->val ||
1027: ATTRVAL_REQ == ap->val)) {
1028: isop = 0;
1029: break;
1030: }
1031:
1.44 schwarze 1032: /*
1.24 kristaps 1033: * Make sure we're on a macro line.
1034: * This will prevent pnode_print() for putting us on a
1035: * subsequent line.
1036: */
1037: sv = p->newln;
1038: pnode_printmopen(p);
1.44 schwarze 1039: if (isop)
1.24 kristaps 1040: fputs("Op", stdout);
1041: else if (sv)
1042: fputs("No", stdout);
1043:
1044: /*
1045: * Keep on printing text separated by the vertical bar as long
1046: * as we're within the same origin node as the group.
1047: * This is kind of a nightmare.
1048: * Eh, DocBook...
1049: * FIXME: if there's a "Fl", we don't cut off the leading "-"
1050: * like we do in pnode_print().
1051: */
1052: TAILQ_FOREACH(pp, &pn->childq, child) {
1053: pnode_print(p, pp);
1054: np = TAILQ_NEXT(pp, child);
1055: while (NULL != np) {
1056: if (pp->node != np->node)
1057: break;
1058: fputs(" |", stdout);
1059: pnode_printmacrolinepart(p, np);
1060: pp = np;
1061: np = TAILQ_NEXT(np, child);
1062: }
1063: }
1064:
1065: pnode_printmclose(p, sv);
1066: }
1067:
1.7 kristaps 1068: static void
1069: pnode_printprologue(struct parse *p, struct pnode *pn)
1070: {
1071: struct pnode *pp;
1072:
1.9 kristaps 1073: pp = NULL == p->root ? NULL :
1074: pnode_findfirst(p->root, NODE_REFMETA);
1075:
1076: if (NULL != pp) {
1.7 kristaps 1077: pnode_printrefmeta(p, pp);
1078: pnode_unlink(pp);
1079: } else {
1080: puts(".\\\" Supplying bogus prologue...");
1081: puts(".Dd $Mdocdate" "$");
1082: puts(".Dt UNKNOWN 1");
1083: puts(".Os");
1084: }
1.43 kristaps 1085:
1086: if (PARSE_EQN & p->flags) {
1087: puts(".EQ");
1088: puts("delim $$");
1089: puts(".EN");
1090: }
1.7 kristaps 1091: }
1092:
1.42 kristaps 1093: /*
1094: * We can have multiple <term> elements within a <varlistentry>, which
1095: * we should comma-separate as list headers.
1096: */
1.13 kristaps 1097: static void
1098: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
1099: {
1100: struct pnode *pp;
1.42 kristaps 1101: int first = 1;
1.13 kristaps 1102:
1103: assert(p->newln);
1.42 kristaps 1104: fputs(".It", stdout);
1105: p->newln = 0;
1106:
1.13 kristaps 1107: TAILQ_FOREACH(pp, &pn->childq, child)
1108: if (NODE_TERM == pp->node) {
1.42 kristaps 1109: if ( ! first)
1110: putchar(',');
1.13 kristaps 1111: pnode_print(p, pp);
1112: pnode_unlink(pp);
1.42 kristaps 1113: first = 0;
1114: } else
1115: break;
1.13 kristaps 1116:
1.42 kristaps 1117: putchar('\n');
1.13 kristaps 1118: p->newln = 1;
1119: }
1120:
1121: static void
1.25 kristaps 1122: pnode_printrow(struct parse *p, struct pnode *pn)
1123: {
1124: struct pnode *pp;
1125:
1126: puts(".Bl -dash -compact");
1127:
1128: TAILQ_FOREACH(pp, &pn->childq, child) {
1129: assert(p->newln);
1130: puts(".It");
1131: pnode_print(p, pp);
1132: pnode_printmclose(p, 1);
1133: }
1134: assert(p->newln);
1135: puts(".El");
1136: }
1137:
1138: static void
1139: pnode_printtable(struct parse *p, struct pnode *pn)
1.16 kristaps 1140: {
1141: struct pnode *pp;
1142:
1143: assert(p->newln);
1144: TAILQ_FOREACH(pp, &pn->childq, child)
1145: if (NODE_TITLE == pp->node) {
1146: puts(".Pp");
1147: pnode_print(p, pp);
1148: pnode_unlink(pp);
1149: }
1.25 kristaps 1150: assert(p->newln);
1151: puts(".Bl -ohang");
1152: while (NULL != (pp = pnode_findfirst(pn, NODE_ROW))) {
1153: puts(".It Table Row");
1154: pnode_printrow(p, pp);
1155: pnode_printmclose(p, 1);
1156: pnode_unlink(pp);
1157: }
1158: assert(p->newln);
1159: puts(".El");
1160: }
1161:
1162: static void
1163: pnode_printlist(struct parse *p, struct pnode *pn)
1164: {
1165: struct pnode *pp;
1.16 kristaps 1166:
1167: assert(p->newln);
1.25 kristaps 1168: TAILQ_FOREACH(pp, &pn->childq, child)
1169: if (NODE_TITLE == pp->node) {
1170: puts(".Pp");
1171: pnode_print(p, pp);
1172: pnode_unlink(pp);
1173: }
1174: assert(p->newln);
1.21 kristaps 1175:
1176: if (NODE_ORDEREDLIST == pn->node)
1177: puts(".Bl -enum");
1178: else
1179: puts(".Bl -item");
1180:
1.16 kristaps 1181: TAILQ_FOREACH(pp, &pn->childq, child) {
1182: assert(p->newln);
1183: puts(".It");
1184: pnode_print(p, pp);
1185: pnode_printmclose(p, 1);
1186: }
1187: assert(p->newln);
1188: puts(".El");
1189: }
1190:
1191: static void
1.13 kristaps 1192: pnode_printvariablelist(struct parse *p, struct pnode *pn)
1193: {
1194: struct pnode *pp;
1195:
1196: assert(p->newln);
1197: TAILQ_FOREACH(pp, &pn->childq, child)
1198: if (NODE_TITLE == pp->node) {
1199: puts(".Pp");
1200: pnode_print(p, pp);
1201: pnode_unlink(pp);
1202: }
1203:
1204: assert(p->newln);
1205: puts(".Bl -tag -width Ds");
1206: TAILQ_FOREACH(pp, &pn->childq, child)
1207: if (NODE_VARLISTENTRY != pp->node) {
1208: assert(p->newln);
1209: fputs(".It", stdout);
1210: pnode_printmacroline(p, pp);
1211: } else {
1212: assert(p->newln);
1213: pnode_print(p, pp);
1214: }
1215: assert(p->newln);
1216: puts(".El");
1217: }
1218:
1.1 kristaps 1219: /*
1220: * Print a parsed node (or ignore it--whatever).
1221: * This is a recursive function.
1.23 kristaps 1222: * FIXME: if we're in a literal context (<screen> or <programlisting> or
1223: * whatever), don't print inline macros.
1.1 kristaps 1224: */
1225: static void
1226: pnode_print(struct parse *p, struct pnode *pn)
1227: {
1228: struct pnode *pp;
1229: char *cp;
1.10 kristaps 1230: int last, sv;
1.1 kristaps 1231:
1232: if (NULL == pn)
1233: return;
1234:
1.10 kristaps 1235: sv = p->newln;
1.1 kristaps 1236:
1.50 ! schwarze 1237: /* XXX fprintf(stderr, "NODE %s\n", nodes[pn->node].name); */
1.1 kristaps 1238: switch (pn->node) {
1.27 kristaps 1239: case (NODE_APPLICATION):
1240: pnode_printmopen(p);
1241: fputs("Nm", stdout);
1242: break;
1.30 kristaps 1243: case (NODE_ANCHOR):
1244: /* Don't print anything! */
1245: return;
1.4 kristaps 1246: case (NODE_ARG):
1.10 kristaps 1247: pnode_printarg(p, pn);
1.4 kristaps 1248: pnode_unlinksub(pn);
1249: break;
1.50 ! schwarze 1250: case (NODE_AUTHOR):
! 1251: pnode_printmopen(p);
! 1252: fputs("An", stdout);
! 1253: break;
! 1254: case (NODE_AUTHORGROUP):
! 1255: assert(p->newln);
! 1256: puts(".An -split");
! 1257: break;
! 1258: case (NODE_BOOKINFO):
! 1259: assert(p->newln);
! 1260: puts(".Sh NAME");
! 1261: break;
1.1 kristaps 1262: case (NODE_CITEREFENTRY):
1.34 kristaps 1263: pnode_printmopen(p);
1264: fputs("Xr", stdout);
1.1 kristaps 1265: pnode_printciterefentry(p, pn);
1.4 kristaps 1266: pnode_unlinksub(pn);
1.1 kristaps 1267: break;
1268: case (NODE_CODE):
1.10 kristaps 1269: pnode_printmopen(p);
1.13 kristaps 1270: fputs("Li", stdout);
1.4 kristaps 1271: break;
1272: case (NODE_COMMAND):
1.10 kristaps 1273: pnode_printmopen(p);
1.13 kristaps 1274: fputs("Nm", stdout);
1275: break;
1.33 kristaps 1276: case (NODE_CONSTANT):
1277: pnode_printmopen(p);
1278: fputs("Dv", stdout);
1279: break;
1.50 ! schwarze 1280: case (NODE_EDITOR):
! 1281: puts("editor: ");
! 1282: pnode_printmopen(p);
! 1283: fputs("An", stdout);
! 1284: break;
1.13 kristaps 1285: case (NODE_EMPHASIS):
1286: pnode_printmopen(p);
1287: fputs("Em", stdout);
1.1 kristaps 1288: break;
1.21 kristaps 1289: case (NODE_ENVAR):
1290: pnode_printmopen(p);
1291: fputs("Ev", stdout);
1292: break;
1.17 kristaps 1293: case (NODE_FILENAME):
1294: pnode_printmopen(p);
1295: fputs("Pa", stdout);
1296: break;
1.3 kristaps 1297: case (NODE_FUNCTION):
1.10 kristaps 1298: pnode_printmopen(p);
1.13 kristaps 1299: fputs("Fn", stdout);
1.3 kristaps 1300: break;
1301: case (NODE_FUNCPROTOTYPE):
1.10 kristaps 1302: assert(p->newln);
1.3 kristaps 1303: pnode_printfuncprototype(p, pn);
1.4 kristaps 1304: pnode_unlinksub(pn);
1.3 kristaps 1305: break;
1.1 kristaps 1306: case (NODE_FUNCSYNOPSISINFO):
1.10 kristaps 1307: pnode_printmopen(p);
1.13 kristaps 1308: fputs("Fd", stdout);
1.16 kristaps 1309: break;
1.43 kristaps 1310: case (NODE_INFORMALEQUATION):
1311: if ( ! p->newln)
1312: putchar('\n');
1313: puts(".EQ");
1314: p->newln = 0;
1315: break;
1316: case (NODE_INLINEEQUATION):
1317: fputc('$', stdout);
1318: p->newln = 0;
1319: break;
1.16 kristaps 1320: case (NODE_ITEMIZEDLIST):
1321: assert(p->newln);
1.25 kristaps 1322: pnode_printlist(p, pn);
1323: pnode_unlinksub(pn);
1.24 kristaps 1324: break;
1325: case (NODE_GROUP):
1326: pnode_printgroup(p, pn);
1327: pnode_unlinksub(pn);
1.10 kristaps 1328: break;
1.50 ! schwarze 1329: case (NODE_LEGALNOTICE):
! 1330: assert(p->newln);
! 1331: puts(".Sh LEGAL NOTICE");
! 1332: break;
1.19 kristaps 1333: case (NODE_LITERAL):
1334: pnode_printmopen(p);
1335: fputs("Li", stdout);
1336: break;
1.40 kristaps 1337: case (NODE_MML_MFENCED):
1.41 kristaps 1338: pnode_printmathfenced(p, pn);
1339: pnode_unlinksub(pn);
1.40 kristaps 1340: break;
1341: case (NODE_MML_MROW):
1342: case (NODE_MML_MI):
1343: case (NODE_MML_MN):
1344: case (NODE_MML_MO):
1.43 kristaps 1345: if (TAILQ_EMPTY(&pn->childq))
1346: break;
1347: fputs(" { ", stdout);
1.40 kristaps 1348: break;
1349: case (NODE_MML_MFRAC):
1350: case (NODE_MML_MSUB):
1351: case (NODE_MML_MSUP):
1352: pnode_printmath(p, pn);
1353: pnode_unlinksub(pn);
1354: break;
1.10 kristaps 1355: case (NODE_OPTION):
1356: pnode_printmopen(p);
1.13 kristaps 1357: fputs("Fl", stdout);
1.1 kristaps 1358: break;
1.25 kristaps 1359: case (NODE_ORDEREDLIST):
1360: assert(p->newln);
1361: pnode_printlist(p, pn);
1362: pnode_unlinksub(pn);
1363: break;
1.1 kristaps 1364: case (NODE_PARA):
1.10 kristaps 1365: assert(p->newln);
1.44 schwarze 1366: if (NULL != pn->parent &&
1367: NODE_LISTITEM == pn->parent->node)
1.13 kristaps 1368: break;
1.1 kristaps 1369: puts(".Pp");
1.3 kristaps 1370: break;
1371: case (NODE_PARAMETER):
1.10 kristaps 1372: /* Suppress non-text children... */
1373: pnode_printmopen(p);
1374: fputs("Fa \"", stdout);
1.32 kristaps 1375: pnode_printmacrolinetext(p, pn, MACROLINE_NOWS);
1376: fputs("\"", stdout);
1.4 kristaps 1377: pnode_unlinksub(pn);
1.1 kristaps 1378: break;
1.28 kristaps 1379: case (NODE_QUOTE):
1380: pnode_printmopen(p);
1381: fputs("Qo", stdout);
1382: break;
1.50 ! schwarze 1383: case (NODE_LITERALLAYOUT):
! 1384: /* FALLTHROUGH */
1.1 kristaps 1385: case (NODE_PROGRAMLISTING):
1.22 kristaps 1386: /* FALLTHROUGH */
1387: case (NODE_SCREEN):
1.10 kristaps 1388: assert(p->newln);
1.1 kristaps 1389: puts(".Bd -literal");
1.15 kristaps 1390: break;
1391: case (NODE_REFENTRYINFO):
1392: /* Suppress. */
1393: pnode_unlinksub(pn);
1.1 kristaps 1394: break;
1395: case (NODE_REFMETA):
1.7 kristaps 1396: abort();
1.1 kristaps 1397: break;
1398: case (NODE_REFNAME):
1.10 kristaps 1399: /* Suppress non-text children... */
1400: pnode_printmopen(p);
1.13 kristaps 1401: fputs("Nm", stdout);
1402: p->newln = 0;
1.10 kristaps 1403: pnode_printmacrolinepart(p, pn);
1.4 kristaps 1404: pnode_unlinksub(pn);
1.10 kristaps 1405: break;
1.1 kristaps 1406: case (NODE_REFNAMEDIV):
1.10 kristaps 1407: assert(p->newln);
1.1 kristaps 1408: puts(".Sh NAME");
1409: break;
1410: case (NODE_REFPURPOSE):
1.10 kristaps 1411: assert(p->newln);
1.13 kristaps 1412: pnode_printmopen(p);
1413: fputs("Nd", stdout);
1.10 kristaps 1414: break;
1.1 kristaps 1415: case (NODE_REFSYNOPSISDIV):
1.10 kristaps 1416: assert(p->newln);
1.6 kristaps 1417: pnode_printrefsynopsisdiv(p, pn);
1.10 kristaps 1418: puts(".Sh SYNOPSIS");
1.1 kristaps 1419: break;
1420: case (NODE_REFSECT1):
1.20 kristaps 1421: /* FALLTHROUGH */
1422: case (NODE_REFSECT2):
1.29 kristaps 1423: /* FALLTHROUGH */
1424: case (NODE_REFSECT3):
1425: /* FALLTHROUGH */
1426: case (NODE_REFSECTION):
1427: /* FALLTHROUGH */
1.50 ! schwarze 1428: case (NODE_CHAPTER):
! 1429: /* FALLTHROUGH */
! 1430: case (NODE_SECT1):
! 1431: /* FALLTHROUGH */
! 1432: case (NODE_SECT2):
! 1433: /* FALLTHROUGH */
1.29 kristaps 1434: case (NODE_NOTE):
1435: /* FALLTHROUGH */
1436: case (NODE_TIP):
1437: /* FALLTHROUGH */
1438: case (NODE_CAUTION):
1439: /* FALLTHROUGH */
1440: case (NODE_WARNING):
1.10 kristaps 1441: assert(p->newln);
1.1 kristaps 1442: pnode_printrefsect(p, pn);
1443: break;
1.13 kristaps 1444: case (NODE_REPLACEABLE):
1445: pnode_printmopen(p);
1446: fputs("Ar", stdout);
1447: break;
1.19 kristaps 1448: case (NODE_SBR):
1449: assert(p->newln);
1450: puts(".br");
1451: break;
1.30 kristaps 1452: case (NODE_SGMLTAG):
1453: pnode_printmopen(p);
1454: fputs("Li", stdout);
1455: break;
1.8 kristaps 1456: case (NODE_STRUCTNAME):
1.10 kristaps 1457: pnode_printmopen(p);
1.13 kristaps 1458: fputs("Vt", stdout);
1.25 kristaps 1459: break;
1460: case (NODE_TABLE):
1.35 kristaps 1461: /* FALLTHROUGH */
1462: case (NODE_INFORMALTABLE):
1.25 kristaps 1463: assert(p->newln);
1464: pnode_printtable(p, pn);
1465: pnode_unlinksub(pn);
1.10 kristaps 1466: break;
1.1 kristaps 1467: case (NODE_TEXT):
1.13 kristaps 1468: if (0 == p->newln)
1469: putchar(' ');
1.37 kristaps 1470:
1.1 kristaps 1471: bufclear(p);
1472: bufappend(p, pn);
1.37 kristaps 1473:
1474: if (0 == p->bsz) {
1475: assert(pn->real != pn->b);
1476: break;
1477: }
1478:
1.1 kristaps 1479: /*
1480: * Output all characters, squeezing out whitespace
1.44 schwarze 1481: * between newlines.
1.1 kristaps 1482: * XXX: all whitespace, including tabs (?).
1483: * Remember to escape control characters and escapes.
1484: */
1.10 kristaps 1485: assert(p->bsz);
1.20 kristaps 1486: cp = p->b;
1.37 kristaps 1487:
1.20 kristaps 1488: /*
1489: * There's often a superfluous "-" in its <option> tags
1490: * before the actual flags themselves.
1491: * "Fl" does this for us, so remove it.
1492: */
1493: if (NULL != pn->parent &&
1494: NODE_OPTION == pn->parent->node &&
1495: '-' == *cp)
1496: cp++;
1497: for (last = '\n'; '\0' != *cp; ) {
1.1 kristaps 1498: if ('\n' == last) {
1499: /* Consume all whitespace. */
1.46 schwarze 1500: if (isspace((unsigned char)*cp)) {
1501: while (isspace((unsigned char)*cp))
1.1 kristaps 1502: cp++;
1503: continue;
1504: } else if ('\'' == *cp || '.' == *cp)
1505: fputs("\\&", stdout);
1506: }
1507: putchar(last = *cp++);
1508: /* If we're a character escape, escape us. */
1509: if ('\\' == last)
1510: putchar('e');
1511: }
1.10 kristaps 1512: p->newln = 0;
1.1 kristaps 1513: break;
1.50 ! schwarze 1514: case (NODE_TITLE):
! 1515: if (pn->parent->node == NODE_BOOKINFO) {
! 1516: pnode_printmopen(p);
! 1517: fputs("Nd", stdout);
! 1518: }
! 1519: break;
1.39 kristaps 1520: case (NODE_TYPE):
1521: pnode_printmopen(p);
1522: fputs("Vt", stdout);
1523: break;
1.26 kristaps 1524: case (NODE_USERINPUT):
1525: pnode_printmopen(p);
1526: fputs("Li", stdout);
1527: break;
1.13 kristaps 1528: case (NODE_VARIABLELIST):
1529: assert(p->newln);
1530: pnode_printvariablelist(p, pn);
1531: pnode_unlinksub(pn);
1532: break;
1533: case (NODE_VARLISTENTRY):
1534: assert(p->newln);
1535: pnode_printvarlistentry(p, pn);
1536: break;
1.26 kristaps 1537: case (NODE_VARNAME):
1.23 kristaps 1538: pnode_printmopen(p);
1.26 kristaps 1539: fputs("Va", stdout);
1.23 kristaps 1540: break;
1.1 kristaps 1541: default:
1542: break;
1543: }
1544:
1545: TAILQ_FOREACH(pp, &pn->childq, child)
1546: pnode_print(p, pp);
1547:
1548: switch (pn->node) {
1.43 kristaps 1549: case (NODE_INFORMALEQUATION):
1.40 kristaps 1550: if ( ! p->newln)
1551: putchar('\n');
1552: puts(".EN");
1553: p->newln = 1;
1554: break;
1.43 kristaps 1555: case (NODE_INLINEEQUATION):
1556: fputs("$ ", stdout);
1557: p->newln = sv;
1558: break;
1.40 kristaps 1559: case (NODE_MML_MROW):
1560: case (NODE_MML_MI):
1561: case (NODE_MML_MN):
1562: case (NODE_MML_MO):
1.43 kristaps 1563: if (TAILQ_EMPTY(&pn->childq))
1564: break;
1565: fputs(" } ", stdout);
1.40 kristaps 1566: break;
1.27 kristaps 1567: case (NODE_APPLICATION):
1.10 kristaps 1568: case (NODE_ARG):
1.50 ! schwarze 1569: case (NODE_AUTHOR):
1.34 kristaps 1570: case (NODE_CITEREFENTRY):
1.10 kristaps 1571: case (NODE_CODE):
1572: case (NODE_COMMAND):
1.33 kristaps 1573: case (NODE_CONSTANT):
1.50 ! schwarze 1574: case (NODE_EDITOR):
1.13 kristaps 1575: case (NODE_EMPHASIS):
1.21 kristaps 1576: case (NODE_ENVAR):
1.17 kristaps 1577: case (NODE_FILENAME):
1.10 kristaps 1578: case (NODE_FUNCTION):
1579: case (NODE_FUNCSYNOPSISINFO):
1.19 kristaps 1580: case (NODE_LITERAL):
1.10 kristaps 1581: case (NODE_OPTION):
1582: case (NODE_PARAMETER):
1.13 kristaps 1583: case (NODE_REPLACEABLE):
1584: case (NODE_REFPURPOSE):
1.30 kristaps 1585: case (NODE_SGMLTAG):
1.10 kristaps 1586: case (NODE_STRUCTNAME):
1587: case (NODE_TEXT):
1.39 kristaps 1588: case (NODE_TYPE):
1.23 kristaps 1589: case (NODE_USERINPUT):
1.26 kristaps 1590: case (NODE_VARNAME):
1.37 kristaps 1591: pnode_printmclosepunct(p, pn, sv);
1.28 kristaps 1592: break;
1593: case (NODE_QUOTE):
1594: pnode_printmclose(p, sv);
1595: sv = p->newln;
1596: pnode_printmopen(p);
1597: fputs("Qc", stdout);
1.10 kristaps 1598: pnode_printmclose(p, sv);
1599: break;
1.12 kristaps 1600: case (NODE_REFNAME):
1601: /*
1602: * If we're in the NAME macro and we have multiple
1603: * <refname> macros in sequence, then print out a
1604: * trailing comma before the newline.
1605: */
1.44 schwarze 1606: if (NULL != pn->parent &&
1.12 kristaps 1607: NODE_REFNAMEDIV == pn->parent->node &&
1608: NULL != TAILQ_NEXT(pn, child) &&
1.44 schwarze 1609: NODE_REFNAME == TAILQ_NEXT(pn, child)->node)
1.12 kristaps 1610: fputs(" ,", stdout);
1611: pnode_printmclose(p, sv);
1612: break;
1.50 ! schwarze 1613: case (NODE_LITERALLAYOUT):
! 1614: /* FALLTHROUGH */
1.1 kristaps 1615: case (NODE_PROGRAMLISTING):
1.22 kristaps 1616: /* FALLTHROUGH */
1617: case (NODE_SCREEN):
1.10 kristaps 1618: assert(p->newln);
1.1 kristaps 1619: puts(".Ed");
1.10 kristaps 1620: p->newln = 1;
1.50 ! schwarze 1621: break;
! 1622: case (NODE_TITLE):
! 1623: if (pn->parent->node == NODE_BOOKINFO) {
! 1624: pnode_printmclose(p, 1);
! 1625: puts(".Sh AUTHORS");
! 1626: }
1.1 kristaps 1627: break;
1628: default:
1629: break;
1630: }
1631: }
1632:
1633: /*
1634: * Loop around the read buffer until we've drained it of all data.
1635: * Invoke the parser context with each buffer fill.
1636: */
1637: static int
1.44 schwarze 1638: readfile(XML_Parser xp, int fd,
1.1 kristaps 1639: char *b, size_t bsz, const char *fn)
1640: {
1641: struct parse p;
1642: int rc;
1643: ssize_t ssz;
1644:
1645: memset(&p, 0, sizeof(struct parse));
1646:
1647: p.b = malloc(p.bsz = p.mbsz = 1024);
1.12 kristaps 1648: p.fname = fn;
1649: p.xml = xp;
1.1 kristaps 1650:
1651: XML_SetCharacterDataHandler(xp, xml_char);
1652: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
1653: XML_SetUserData(xp, &p);
1654:
1655: while ((ssz = read(fd, b, bsz)) >= 0) {
1656: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
1.30 kristaps 1657: fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
1658: XML_GetCurrentLineNumber(xp),
1.44 schwarze 1659: XML_GetCurrentColumnNumber(xp),
1.1 kristaps 1660: XML_ErrorString
1661: (XML_GetErrorCode(xp)));
1662: else if ( ! p.stop && ssz > 0)
1663: continue;
1.44 schwarze 1664: /*
1.1 kristaps 1665: * Exit when we've read all or errors have occured
1666: * during the parse sequence.
1667: */
1.10 kristaps 1668: p.newln = 1;
1.7 kristaps 1669: pnode_printprologue(&p, p.root);
1.1 kristaps 1670: pnode_print(&p, p.root);
1671: pnode_free(p.root);
1672: free(p.b);
1673: return(0 != rc && ! p.stop);
1674: }
1675:
1676: /* Read error has occured. */
1677: perror(fn);
1678: pnode_free(p.root);
1679: free(p.b);
1680: return(0);
1681: }
1682:
1683: int
1684: main(int argc, char *argv[])
1685: {
1686: XML_Parser xp;
1687: const char *fname;
1688: char *buf;
1.38 kristaps 1689: int fd, rc, ch;
1690: const char *progname;
1691:
1692: progname = strrchr(argv[0], '/');
1693: if (progname == NULL)
1694: progname = argv[0];
1695: else
1696: ++progname;
1.1 kristaps 1697:
1698: fname = "-";
1699: xp = NULL;
1700: buf = NULL;
1701: rc = 0;
1702:
1.38 kristaps 1703: while (-1 != (ch = getopt(argc, argv, "W")))
1704: switch (ch) {
1705: case ('W'):
1706: warn = 1;
1707: break;
1708: default:
1709: goto usage;
1710: }
1.1 kristaps 1711:
1712: argc -= optind;
1713: argv += optind;
1714:
1.45 schwarze 1715: if (argc > 1) {
1716: fprintf(stderr, "%s: Too many arguments\n", argv[1]);
1717: goto usage;
1718: } else if (argc > 0)
1.1 kristaps 1719: fname = argv[0];
1720:
1721: /* Read from stdin or a file. */
1.44 schwarze 1722: fd = 0 == strcmp(fname, "-") ?
1.1 kristaps 1723: STDIN_FILENO : open(fname, O_RDONLY, 0);
1724:
1725: /*
1726: * Open file for reading.
1727: * Allocate a read buffer.
1728: * Create the parser context.
1729: * Dive directly into the parse.
1730: */
1731: if (-1 == fd)
1732: perror(fname);
1733: else if (NULL == (buf = malloc(4096)))
1734: perror(NULL);
1735: else if (NULL == (xp = XML_ParserCreate(NULL)))
1736: perror(NULL);
1737: else if ( ! readfile(xp, fd, buf, 4096, fname))
1738: rc = 1;
1739:
1740: XML_ParserFree(xp);
1741: free(buf);
1742: if (STDIN_FILENO != fd)
1743: close(fd);
1744: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1.38 kristaps 1745:
1746: usage:
1.45 schwarze 1747: fprintf(stderr, "usage: %s [-W] [input_filename]\n", progname);
1.38 kristaps 1748: return(EXIT_FAILURE);
1.1 kristaps 1749: }
CVSweb