Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.3
1.3 ! kristaps 1: /* $Id: docbook2mdoc.c,v 1.2 2014/03/28 02:06:29 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/queue.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
21: #include <expat.h>
22: #include <fcntl.h>
23: #include <getopt.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27:
28: /*
29: * All recognised node types.
30: */
31: enum nodeid {
32: NODE_ROOT = 0, /* Must comes first. */
33: /* Alpha-ordered hereafter. */
34: NODE_CITEREFENTRY,
35: NODE_CODE,
1.3 ! kristaps 36: NODE_FUNCDEF,
! 37: NODE_FUNCPROTOTYPE,
1.1 kristaps 38: NODE_FUNCSYNOPSIS,
39: NODE_FUNCSYNOPSISINFO,
1.3 ! kristaps 40: NODE_FUNCTION,
1.1 kristaps 41: NODE_MANVOLNUM,
42: NODE_PARA,
1.3 ! kristaps 43: NODE_PARAMDEF,
! 44: NODE_PARAMETER,
1.1 kristaps 45: NODE_PROGRAMLISTING,
46: NODE_REFCLASS,
47: NODE_REFDESCRIPTOR,
48: NODE_REFENTRY,
49: NODE_REFENTRYTITLE,
50: NODE_REFMETA,
51: NODE_REFMISCINFO,
52: NODE_REFNAME,
53: NODE_REFNAMEDIV,
54: NODE_REFPURPOSE,
55: NODE_REFSECT1,
56: NODE_REFSYNOPSISDIV,
57: NODE_SYNOPSIS,
58: NODE_TEXT,
59: NODE_TITLE,
60: NODE__MAX
61: };
62:
63: /*
64: * Global parse state.
65: * Keep this as simple and small as possible.
66: */
67: struct parse {
68: enum nodeid node; /* current (NODE_ROOT if pre-tree) */
69: int stop; /* should we stop now? */
70: struct pnode *root; /* root of parse tree */
71: struct pnode *cur; /* current node in tree */
72: char *b;
73: size_t bsz;
74: size_t mbsz;
75: };
76:
77: struct node {
78: const char *name;
79: unsigned int flags;
80: #define NODE_IGNTEXT 1 /* ignore all contained text */
81: };
82:
83: TAILQ_HEAD(pnodeq, pnode);
84:
85: struct pnode {
86: enum nodeid node; /* node type */
87: char *b; /* binary data buffer */
88: size_t bsz; /* data buffer size */
89: struct pnode *parent; /* parent (or NULL if top) */
90: struct pnodeq childq; /* queue of children */
91: TAILQ_ENTRY(pnode) child;
92: };
93:
94: static const struct node nodes[NODE__MAX] = {
95: { NULL, 0 },
96: { "citerefentry", NODE_IGNTEXT },
97: { "code", 0 },
1.3 ! kristaps 98: { "funcdef", 0 },
! 99: { "funcprototype", NODE_IGNTEXT },
1.1 kristaps 100: { "funcsynopsis", NODE_IGNTEXT },
101: { "funcsynopsisinfo", 0 },
1.3 ! kristaps 102: { "function", 0 },
1.1 kristaps 103: { "manvolnum", 0 },
104: { "para", 0 },
1.3 ! kristaps 105: { "paramdef", 0 },
! 106: { "parameter", 0 },
1.1 kristaps 107: { "programlisting", 0 },
108: { "refclass", NODE_IGNTEXT },
109: { "refdescriptor", NODE_IGNTEXT },
110: { "refentry", NODE_IGNTEXT },
111: { "refentrytitle", 0 },
112: { "refmeta", NODE_IGNTEXT },
113: { "refmiscinfo", NODE_IGNTEXT },
114: { "refname", 0 },
115: { "refnamediv", NODE_IGNTEXT },
116: { "refpurpose", 0 },
117: { "refsect1", 0 },
118: { "refsynopsisdiv", NODE_IGNTEXT },
119: { "synopsis", 0 },
120: { NULL, 0 },
121: { "title", 0 },
122: };
123:
124: /*
125: * Look up whether "parent" is a valid parent for "node".
126: */
127: static int
128: isparent(enum nodeid node, enum nodeid parent)
129: {
130:
131: switch (node) {
132: case (NODE_ROOT):
133: return(0);
134: case (NODE_CITEREFENTRY):
135: switch (parent) {
136: case (NODE_FUNCSYNOPSISINFO):
137: case (NODE_PARA):
138: case (NODE_PROGRAMLISTING):
139: case (NODE_REFDESCRIPTOR):
140: case (NODE_REFENTRYTITLE):
141: case (NODE_REFNAME):
142: case (NODE_REFPURPOSE):
143: case (NODE_SYNOPSIS):
144: case (NODE_TITLE):
145: return(1);
146: default:
147: break;
148: }
149: return(0);
150: case (NODE_CODE):
151: switch (parent) {
152: case (NODE_FUNCSYNOPSISINFO):
153: case (NODE_PARA):
154: case (NODE_PROGRAMLISTING):
155: case (NODE_REFDESCRIPTOR):
156: case (NODE_REFENTRYTITLE):
157: case (NODE_REFNAME):
158: case (NODE_REFPURPOSE):
159: case (NODE_SYNOPSIS):
160: case (NODE_TITLE):
161: return(1);
162: default:
163: break;
164: }
165: return(0);
1.3 ! kristaps 166: case (NODE_FUNCDEF):
! 167: return(NODE_FUNCPROTOTYPE == parent);
! 168: case (NODE_FUNCPROTOTYPE):
! 169: return(NODE_FUNCSYNOPSIS == parent);
! 170: case (NODE_FUNCSYNOPSIS):
! 171: switch (parent) {
! 172: case (NODE_PARA):
! 173: case (NODE_REFSECT1):
! 174: case (NODE_REFSYNOPSISDIV):
! 175: return(1);
! 176: default:
! 177: break;
! 178: }
! 179: return(0);
! 180: case (NODE_FUNCSYNOPSISINFO):
! 181: return(NODE_FUNCSYNOPSIS == parent);
! 182: case (NODE_FUNCTION):
! 183: switch (parent) {
! 184: case (NODE_CODE):
! 185: case (NODE_FUNCDEF):
! 186: case (NODE_FUNCSYNOPSISINFO):
! 187: case (NODE_PARA):
! 188: case (NODE_REFDESCRIPTOR):
! 189: case (NODE_REFENTRYTITLE):
! 190: case (NODE_REFNAME):
! 191: case (NODE_REFPURPOSE):
! 192: case (NODE_SYNOPSIS):
! 193: case (NODE_TITLE):
! 194: return(1);
! 195: default:
! 196: break;
! 197: }
! 198: return(0);
1.1 kristaps 199: case (NODE_MANVOLNUM):
200: switch (parent) {
201: case (NODE_CITEREFENTRY):
202: case (NODE_REFMETA):
203: return(1);
204: default:
205: break;
206: }
207: return(0);
1.3 ! kristaps 208: case (NODE_PARA):
1.1 kristaps 209: switch (parent) {
210: case (NODE_REFSECT1):
211: case (NODE_REFSYNOPSISDIV):
212: return(1);
213: default:
214: break;
215: }
216: return(0);
1.3 ! kristaps 217: case (NODE_PARAMDEF):
! 218: return(NODE_FUNCPROTOTYPE == parent);
! 219: case (NODE_PARAMETER):
1.1 kristaps 220: switch (parent) {
1.3 ! kristaps 221: case (NODE_CODE):
! 222: case (NODE_FUNCSYNOPSISINFO):
! 223: case (NODE_PARA):
! 224: case (NODE_PARAMDEF):
! 225: case (NODE_REFDESCRIPTOR):
! 226: case (NODE_REFENTRYTITLE):
! 227: case (NODE_REFNAME):
! 228: case (NODE_REFPURPOSE):
! 229: case (NODE_SYNOPSIS):
! 230: case (NODE_TITLE):
1.1 kristaps 231: return(1);
232: default:
233: break;
234: }
235: return(0);
236: case (NODE_PROGRAMLISTING):
237: switch (parent) {
238: case (NODE_PARA):
239: case (NODE_REFSECT1):
240: case (NODE_REFSYNOPSISDIV):
241: return(1);
242: default:
243: break;
244: }
245: return(0);
246: case (NODE_REFCLASS):
247: return(parent == NODE_REFNAMEDIV);
248: case (NODE_REFDESCRIPTOR):
249: return(parent == NODE_REFNAMEDIV);
250: case (NODE_REFENTRY):
251: return(parent == NODE_ROOT);
252: case (NODE_REFENTRYTITLE):
253: switch (parent) {
254: case (NODE_CITEREFENTRY):
255: case (NODE_REFMETA):
256: return(1);
257: default:
258: break;
259: }
260: case (NODE_REFMETA):
261: return(parent == NODE_REFENTRY);
262: case (NODE_REFMISCINFO):
263: return(parent == NODE_REFMETA);
264: case (NODE_REFNAME):
265: return(parent == NODE_REFNAMEDIV);
266: case (NODE_REFNAMEDIV):
267: return(parent == NODE_REFENTRY);
268: case (NODE_REFPURPOSE):
269: return(parent == NODE_REFNAMEDIV);
270: case (NODE_REFSECT1):
271: return(parent == NODE_REFENTRY);
272: case (NODE_REFSYNOPSISDIV):
273: return(parent == NODE_REFENTRY);
274: case (NODE_SYNOPSIS):
275: switch (parent) {
276: case (NODE_REFSYNOPSISDIV):
277: case (NODE_REFSECT1):
278: return(1);
279: default:
280: break;
281: }
282: return(0);
283: case (NODE_TITLE):
284: switch (parent) {
285: case (NODE_REFSECT1):
286: case (NODE_REFSYNOPSISDIV):
287: return(1);
288: default:
289: break;
290: }
291: return(0);
292: case (NODE_TEXT):
293: return(1);
294: case (NODE__MAX):
295: break;
296: }
297:
298: abort();
299: return(0);
300: }
301:
302: static void
303: xml_char(void *arg, const XML_Char *p, int sz)
304: {
305: struct parse *ps = arg;
306: struct pnode *dat;
307:
308: /* Stopped or no tree yet. */
309: if (ps->stop || NODE_ROOT == ps->node)
310: return;
311:
312: /* Not supposed to be collecting text. */
313: assert(NULL != ps->cur);
314: if (NODE_IGNTEXT & nodes[ps->node].flags)
315: return;
316:
317: /*
318: * Are we in the midst of processing text?
319: * If we're not processing text right now, then create a text
320: * node for doing so.
321: */
322: if (NODE_TEXT != ps->node) {
323: dat = calloc(1, sizeof(struct pnode));
324: if (NULL == dat) {
325: perror(NULL);
326: exit(EXIT_FAILURE);
327: }
328:
329: dat->node = ps->node = NODE_TEXT;
330: dat->parent = ps->cur;
331: TAILQ_INIT(&dat->childq);
332: TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
333: ps->cur = dat;
334: assert(NULL != ps->root);
335:
336: }
337:
338: /* Append to current buffer. */
339: assert(sz >= 0);
340: ps->cur->b = realloc(ps->cur->b,
341: ps->cur->bsz + (size_t)sz);
342: if (NULL == ps->cur->b) {
343: perror(NULL);
344: exit(EXIT_FAILURE);
345: }
346: memcpy(ps->cur->b + ps->cur->bsz, p, sz);
347: ps->cur->bsz += (size_t)sz;
348: }
349:
350: /*
351: * Begin an element.
352: * First, look for the element.
353: * If we don't find it and we're not parsing, keep going.
354: * If we don't find it (and we're parsing), puke and exit.
355: * If we find it but we're not parsing yet (i.e., it's not a refentry
356: * and thus out of context), keep going.
357: * If we're at the root and already have a tree, puke and exit.
358: * Make sure that the element is in the right context.
359: * Lastly, put the node onto our parse tree and continue.
360: */
361: static void
362: xml_elem_start(void *arg, const XML_Char *name, const XML_Char **atts)
363: {
364: struct parse *ps = arg;
365: enum nodeid node;
366: struct pnode *dat;
367:
368: if (ps->stop)
369: return;
370:
371: /* Close out text node, if applicable... */
372: if (NODE_TEXT == ps->node) {
373: assert(NULL != ps->cur);
374: ps->cur = ps->cur->parent;
375: assert(NULL != ps->cur);
376: ps->node = ps->cur->node;
377: }
378:
379: for (node = 0; node < NODE__MAX; node++)
380: if (NULL == nodes[node].name)
381: continue;
382: else if (0 == strcmp(nodes[node].name, name))
383: break;
384:
385: if (NODE__MAX == node && NODE_ROOT == ps->node) {
386: fprintf(stderr, "%s: ignoring node\n", name);
387: return;
388: } else if (NODE__MAX == node) {
389: fprintf(stderr, "%s: unknown node\n", name);
390: ps->stop = 1;
391: return;
392: } else if (NODE_ROOT == ps->node && NULL != ps->root) {
393: fprintf(stderr, "%s: reentering?\n", name);
394: ps->stop = 1;
395: return;
396: } else if (NODE_ROOT == ps->node && NODE_REFENTRY != node) {
397: fprintf(stderr, "%s: known node w/o context\n", name);
398: return;
399: } else if ( ! isparent(node, ps->node)) {
400: fprintf(stderr, "%s: bad parent\n", name);
401: ps->stop = 1;
402: return;
403: }
404:
405: if (NULL == (dat = calloc(1, sizeof(struct pnode)))) {
406: perror(NULL);
407: exit(EXIT_FAILURE);
408: }
409:
410: dat->node = ps->node = node;
411: dat->parent = ps->cur;
412: TAILQ_INIT(&dat->childq);
413:
414: if (NULL != ps->cur)
415: TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
416:
417: ps->cur = dat;
418: if (NULL == ps->root)
419: ps->root = dat;
420: }
421:
422: /*
423: * Roll up the parse tree.
424: * Does nothing else special.
425: * If we hit the root, then assign ourselves as the NODE_ROOT.
426: */
427: static void
428: xml_elem_end(void *arg, const XML_Char *name)
429: {
430: struct parse *ps = arg;
431:
432: if (ps->stop || NODE_ROOT == ps->node)
433: return;
434:
435: /* Close out text node, if applicable... */
436: if (NODE_TEXT == ps->node) {
437: assert(NULL != ps->cur);
438: ps->cur = ps->cur->parent;
439: assert(NULL != ps->cur);
440: ps->node = ps->cur->node;
441: }
442:
443: if (NULL == (ps->cur = ps->cur->parent))
444: ps->node = NODE_ROOT;
445: else
446: ps->node = ps->cur->node;
447: }
448:
449: static void
450: pnode_free(struct pnode *pn)
451: {
452: struct pnode *pp;
453:
454: if (NULL == pn)
455: return;
456:
457: while (NULL != (pp = TAILQ_FIRST(&pn->childq))) {
458: TAILQ_REMOVE(&pn->childq, pp, child);
459: pnode_free(pp);
460: }
461:
462: free(pn->b);
463: free(pn);
464: }
465:
466: static void
467: pnode_unlink(struct pnode *pn)
468: {
469:
470: if (NULL != pn->parent)
471: TAILQ_REMOVE(&pn->parent->childq, pn, child);
472: pnode_free(pn);
473: }
474:
475: static void
476: bufclear(struct parse *p)
477: {
478:
479: p->b[p->bsz = 0] = '\0';
480: }
481:
482: static void
483: bufappend(struct parse *p, struct pnode *pn)
484: {
485:
486: assert(NODE_TEXT == pn->node);
487: if (p->bsz + pn->bsz + 1 > p->mbsz) {
488: p->mbsz = p->bsz + pn->bsz + 1;
489: if (NULL == (p->b = realloc(p->b, p->mbsz))) {
490: perror(NULL);
491: exit(EXIT_FAILURE);
492: }
493: }
494: memcpy(p->b + p->bsz, pn->b, pn->bsz);
495: p->bsz += pn->bsz;
496: p->b[p->bsz] = '\0';
497: }
498:
1.3 ! kristaps 499: static void
! 500: bufappend_r(struct parse *p, struct pnode *pn)
! 501: {
! 502: struct pnode *pp;
! 503:
! 504: if (NODE_TEXT == pn->node)
! 505: bufappend(p, pn);
! 506: TAILQ_FOREACH(pp, &pn->childq, child)
! 507: bufappend_r(p, pp);
! 508: }
! 509:
1.1 kristaps 510: /*
511: * Print text presumably on a macro line.
512: * Ignore any child macros.
513: * Convert all whitespace to regular spaces.
514: */
515: static void
516: pnode_printmacrolinepart(struct parse *p, struct pnode *pn)
517: {
518: char *cp;
519:
520: bufclear(p);
1.3 ! kristaps 521: bufappend_r(p, pn);
1.1 kristaps 522:
523: /* Convert all space to spaces. */
524: for (cp = p->b; '\0' != *cp; cp++)
525: if (isspace((int)*cp))
526: *cp = ' ';
527:
528: for (cp = p->b; isspace((int)*cp); cp++)
529: /* Spin. */ ;
530:
531: for ( ; '\0' != *cp; cp++) {
532: /* Escape us if we look like a macro. */
533: if ((cp == p->b || ' ' == *(cp - 1)) &&
534: isupper((int)*cp) &&
535: '\0' != *(cp + 1) &&
536: islower((int)*(cp + 1)) &&
537: ('\0' == *(cp + 2) ||
538: ' ' == *(cp + 2) ||
539: (islower((int)*(cp + 2)) &&
540: ('\0' == *(cp + 3) ||
541: ' ' == *(cp + 3)))))
542: fputs("\\&", stdout);
543: putchar(*cp);
544: /* If we're a character escape, escape us. */
545: if ('\\' == *cp)
546: putchar('e');
547: }
548: }
549:
550: /*
551: * Just pnode_printmacrolinepart() but with a newline.
552: * If no text, just the newline.
553: */
554: static void
555: pnode_printmacroline(struct parse *p, struct pnode *pn)
556: {
557:
558: pnode_printmacrolinepart(p, pn);
559: putchar('\n');
560: }
561:
562: static void
563: pnode_printrefsect(struct parse *p, struct pnode *pn)
564: {
565: struct pnode *pp;
566:
567: TAILQ_FOREACH(pp, &pn->childq, child)
568: if (NODE_TITLE == pp->node)
569: break;
570:
571: if (NULL != pp) {
572: fputs(".Sh ", stdout);
573: pnode_printmacroline(p, pp);
574: pnode_unlink(pp);
575: } else
576: puts(".Sh UNKNOWN");
577: }
578:
579: static void
580: pnode_printciterefentry(struct parse *p, struct pnode *pn)
581: {
582: struct pnode *pp, *title, *manvol;
583:
584: title = manvol = NULL;
585: TAILQ_FOREACH(pp, &pn->childq, child)
586: if (NODE_MANVOLNUM == pp->node)
587: manvol = pp;
588: else if (NODE_REFENTRYTITLE == pp->node)
589: title = pp;
590:
591: fputs(".Xr ", stdout);
592: if (NULL != title) {
593: pnode_printmacrolinepart(p, title);
594: pnode_unlink(title);
595: } else
596: fputs("unknown", stdout);
597: putchar(' ');
598: if (NULL != manvol) {
599: pnode_printmacroline(p, manvol);
600: pnode_unlink(manvol);
601: } else
602: puts("1");
603: }
604:
605: static void
606: pnode_printrefmeta(struct parse *p, struct pnode *pn)
607: {
608: struct pnode *pp, *title, *manvol;
609:
610: title = manvol = NULL;
611: TAILQ_FOREACH(pp, &pn->childq, child)
612: if (NODE_MANVOLNUM == pp->node)
613: manvol = pp;
614: else if (NODE_REFENTRYTITLE == pp->node)
615: title = pp;
616:
1.2 kristaps 617: puts(".Dd $Mdocdate" "$");
1.1 kristaps 618: fputs(".Dt ", stdout);
619:
620: if (NULL != title) {
621: pnode_printmacrolinepart(p, title);
622: pnode_unlink(title);
623: } else
624: fputs("UNKNOWN", stdout);
625: putchar(' ');
626: if (NULL != manvol) {
627: pnode_printmacroline(p, manvol);
628: pnode_unlink(manvol);
629: } else
630: puts("1");
631:
632: puts(".Os");
633: }
634:
1.3 ! kristaps 635: static void
! 636: pnode_printfuncdef(struct parse *p, struct pnode *pn)
! 637: {
! 638: struct pnode *pp, *ftype, *func;
! 639:
! 640: ftype = func = NULL;
! 641: TAILQ_FOREACH(pp, &pn->childq, child)
! 642: if (NODE_TEXT == pp->node)
! 643: ftype = pp;
! 644: else if (NODE_FUNCTION == pp->node)
! 645: func = pp;
! 646:
! 647: if (NULL != ftype) {
! 648: fputs(".Ft ", stdout);
! 649: pnode_printmacroline(p, ftype);
! 650: }
! 651:
! 652: if (NULL != func) {
! 653: fputs(".Fo ", stdout);
! 654: pnode_printmacroline(p, func);
! 655: } else
! 656: puts(".Fo UNKNOWN");
! 657: }
! 658:
! 659: static void
! 660: pnode_printparamdef(struct parse *p, struct pnode *pn)
! 661: {
! 662: struct pnode *pp, *ptype, *param;
! 663:
! 664: ptype = param = NULL;
! 665: TAILQ_FOREACH(pp, &pn->childq, child)
! 666: if (NODE_TEXT == pp->node)
! 667: ptype = pp;
! 668: else if (NODE_PARAMETER == pp->node)
! 669: param = pp;
! 670:
! 671: fputs(".Fa \"", stdout);
! 672: if (NULL != ptype) {
! 673: pnode_printmacrolinepart(p, ptype);
! 674: putchar(' ');
! 675: }
! 676:
! 677: if (NULL != param)
! 678: pnode_printmacrolinepart(p, param);
! 679: else
! 680: fputs("UNKNOWN", stdout);
! 681:
! 682: puts("\"");
! 683: }
! 684:
! 685: static void
! 686: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
! 687: {
! 688: struct pnode *pp, *fdef;
! 689:
! 690: TAILQ_FOREACH(fdef, &pn->childq, child)
! 691: if (NODE_FUNCDEF == fdef->node)
! 692: break;
! 693:
! 694: if (NULL != fdef) {
! 695: pnode_printfuncdef(p, fdef);
! 696: pnode_unlink(fdef);
! 697: } else
! 698: puts(".Fo UNKNOWN");
! 699:
! 700: TAILQ_FOREACH(pp, &pn->childq, child) {
! 701: if (NODE_PARAMDEF == pp->node)
! 702: pnode_printparamdef(p, pp);
! 703: pnode_unlink(pp);
! 704: }
! 705:
! 706: puts(".Fc");
! 707: }
! 708:
1.1 kristaps 709: /*
710: * Print a parsed node (or ignore it--whatever).
711: * This is a recursive function.
712: * FIXME: macro line continuation?
713: */
714: static void
715: pnode_print(struct parse *p, struct pnode *pn)
716: {
717: struct pnode *pp;
718: char *cp;
719: int last;
720:
721: if (NULL == pn)
722: return;
723:
724: if (NODE_TEXT != pn->node && NODE_ROOT != pn->node)
725: printf(".\\\" %s\n", nodes[pn->node].name);
726:
727: switch (pn->node) {
728: case (NODE_CITEREFENTRY):
729: pnode_printciterefentry(p, pn);
730: break;
731: case (NODE_CODE):
732: fputs(".Li ", stdout);
733: pnode_printmacroline(p, pn);
734: break;
1.3 ! kristaps 735: case (NODE_FUNCTION):
! 736: fputs(".Fn ", stdout);
! 737: pnode_printmacroline(p, pn);
! 738: break;
! 739: case (NODE_FUNCPROTOTYPE):
! 740: pnode_printfuncprototype(p, pn);
! 741: break;
1.1 kristaps 742: case (NODE_FUNCSYNOPSISINFO):
743: fputs(".Fd ", stdout);
744: pnode_printmacroline(p, pn);
745: break;
746: case (NODE_PARA):
747: /* FIXME: not always. */
748: puts(".Pp");
1.3 ! kristaps 749: break;
! 750: case (NODE_PARAMETER):
! 751: fputs(".Fa \"", stdout);
! 752: pnode_printmacrolinepart(p, pn);
! 753: puts("\"");
1.1 kristaps 754: break;
755: case (NODE_PROGRAMLISTING):
756: puts(".Bd -literal");
757: break;
758: case (NODE_REFMETA):
759: pnode_printrefmeta(p, pn);
760: break;
761: case (NODE_REFNAME):
762: fputs(".Nm ", stdout);
763: pnode_printmacroline(p, pn);
764: return;
765: case (NODE_REFNAMEDIV):
766: puts(".Sh NAME");
767: break;
768: case (NODE_REFPURPOSE):
769: fputs(".Nd ", stdout);
770: pnode_printmacroline(p, pn);
771: return;
772: case (NODE_REFSYNOPSISDIV):
773: puts(".Sh SYNOPSIS");
774: break;
775: case (NODE_REFSECT1):
776: pnode_printrefsect(p, pn);
777: break;
778: case (NODE_TEXT):
779: bufclear(p);
780: bufappend(p, pn);
781: /*
782: * Output all characters, squeezing out whitespace
783: * between newlines.
784: * XXX: all whitespace, including tabs (?).
785: * Remember to escape control characters and escapes.
786: */
787: for (last = '\n', cp = p->b; '\0' != *cp; ) {
788: if ('\n' == last) {
789: /* Consume all whitespace. */
790: if (isspace((int)*cp)) {
791: while (isspace((int)*cp))
792: cp++;
793: continue;
794: } else if ('\'' == *cp || '.' == *cp)
795: fputs("\\&", stdout);
796: }
797: putchar(last = *cp++);
798: /* If we're a character escape, escape us. */
799: if ('\\' == last)
800: putchar('e');
801: }
802: if ('\n' != last)
803: putchar('\n');
804: break;
805: default:
806: break;
807: }
808:
809: TAILQ_FOREACH(pp, &pn->childq, child)
810: pnode_print(p, pp);
811:
812: switch (pn->node) {
813: case (NODE_PROGRAMLISTING):
814: puts(".Ed");
815: break;
816: default:
817: break;
818: }
819: }
820:
821: /*
822: * Loop around the read buffer until we've drained it of all data.
823: * Invoke the parser context with each buffer fill.
824: */
825: static int
826: readfile(XML_Parser xp, int fd,
827: char *b, size_t bsz, const char *fn)
828: {
829: struct parse p;
830: int rc;
831: ssize_t ssz;
832:
833: memset(&p, 0, sizeof(struct parse));
834:
835: p.b = malloc(p.bsz = p.mbsz = 1024);
836:
837: XML_SetCharacterDataHandler(xp, xml_char);
838: XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
839: XML_SetUserData(xp, &p);
840:
841: while ((ssz = read(fd, b, bsz)) >= 0) {
842: if (0 == (rc = XML_Parse(xp, b, ssz, 0 == ssz)))
843: fprintf(stderr, "%s: %s\n", fn,
844: XML_ErrorString
845: (XML_GetErrorCode(xp)));
846: else if ( ! p.stop && ssz > 0)
847: continue;
848: /*
849: * Exit when we've read all or errors have occured
850: * during the parse sequence.
851: */
852: pnode_print(&p, p.root);
853: pnode_free(p.root);
854: free(p.b);
855: return(0 != rc && ! p.stop);
856: }
857:
858: /* Read error has occured. */
859: perror(fn);
860: pnode_free(p.root);
861: free(p.b);
862: return(0);
863: }
864:
865: int
866: main(int argc, char *argv[])
867: {
868: XML_Parser xp;
869: const char *fname;
870: char *buf;
871: int fd, rc;
872:
873: fname = "-";
874: xp = NULL;
875: buf = NULL;
876: rc = 0;
877:
878: if (-1 != getopt(argc, argv, ""))
879: return(EXIT_FAILURE);
880:
881: argc -= optind;
882: argv += optind;
883:
884: if (argc > 1)
885: return(EXIT_FAILURE);
886: else if (argc > 0)
887: fname = argv[0];
888:
889: /* Read from stdin or a file. */
890: fd = 0 == strcmp(fname, "-") ?
891: STDIN_FILENO : open(fname, O_RDONLY, 0);
892:
893: /*
894: * Open file for reading.
895: * Allocate a read buffer.
896: * Create the parser context.
897: * Dive directly into the parse.
898: */
899: if (-1 == fd)
900: perror(fname);
901: else if (NULL == (buf = malloc(4096)))
902: perror(NULL);
903: else if (NULL == (xp = XML_ParserCreate(NULL)))
904: perror(NULL);
905: else if ( ! readfile(xp, fd, buf, 4096, fname))
906: rc = 1;
907:
908: XML_ParserFree(xp);
909: free(buf);
910: if (STDIN_FILENO != fd)
911: close(fd);
912: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
913: }
CVSweb