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