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