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