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