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