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