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