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