Annotation of mandoc/xml.c, Revision 1.9
1.9 ! kristaps 1: /* $Id: xml.c,v 1.8 2008/12/02 13:20:24 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <sys/param.h>
20:
21: #include <assert.h>
22: #include <ctype.h>
23: #include <err.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27:
28: #include "libmdocml.h"
29: #include "private.h"
30:
1.6 kristaps 31: #define COLUMNS 72
1.1 kristaps 32:
1.6 kristaps 33: enum md_ns {
34: MD_NS_BLOCK,
35: MD_NS_INLINE,
36: MD_NS_DEFAULT
37: };
38:
1.1 kristaps 39: enum md_tok {
1.8 kristaps 40: MD_BLKIN, /* Controls spacing. */
1.1 kristaps 41: MD_BLKOUT,
42: MD_IN,
43: MD_OUT,
1.8 kristaps 44: MD_TEXT
1.1 kristaps 45: };
46:
47: struct md_xml {
48: const struct md_args *args;
49: const struct md_rbuf *rbuf;
50:
51: struct md_mbuf *mbuf;
52: struct rofftree *tree;
53: size_t indent;
54: size_t pos;
55: enum md_tok last;
56: int flags;
1.8 kristaps 57: #define MD_LITERAL (1 << 0) /* TODO */
58: #define MD_OVERRIDE_ONE (1 << 1)
59: #define MD_OVERRIDE_ALL (1 << 2)
1.1 kristaps 60: };
61:
62: static void roffmsg(void *arg, enum roffmsg,
63: const char *, const char *, char *);
64: static int roffhead(void *);
65: static int rofftail(void *);
66: static int roffin(void *, int, int *, char **);
67: static int roffdata(void *, int, char *);
68: static int roffout(void *, int);
69: static int roffblkin(void *, int, int *, char **);
70: static int roffblkout(void *, int);
1.8 kristaps 71: static int roffspecial(void *, int, int *, char **, char **);
1.1 kristaps 72:
1.8 kristaps 73: static void mbuf_mode(struct md_xml *, enum md_ns);
1.1 kristaps 74: static int mbuf_newline(struct md_xml *);
1.9 ! kristaps 75: static int xml_indent(struct md_xml *);
1.1 kristaps 76: static int mbuf_data(struct md_xml *, int, char *);
1.9 ! kristaps 77: static int xml_nputstring(struct md_xml *,
1.1 kristaps 78: const char *, size_t);
1.9 ! kristaps 79: static int xml_puts(struct md_xml *, const char *);
! 80: static int xml_nputs(struct md_xml *,
1.2 kristaps 81: const char *, size_t);
1.9 ! kristaps 82: static int xml_begintag(struct md_xml *, const char *,
1.6 kristaps 83: enum md_ns, int *, char **);
1.9 ! kristaps 84: static int xml_endtag(struct md_xml *,
1.6 kristaps 85: const char *, enum md_ns);
86:
1.9 ! kristaps 87: #ifdef __linux__ /* FIXME: remove */
! 88: static size_t strlcat(char *, const char *, size_t);
! 89: static size_t strlcpy(char *, const char *, size_t);
! 90: #endif
! 91:
1.6 kristaps 92:
1.8 kristaps 93: static void
94: mbuf_mode(struct md_xml *p, enum md_ns ns)
95: {
96: p->flags &= ~MD_OVERRIDE_ONE;
97: p->last = ns;
98: }
99:
100:
1.6 kristaps 101: static int
1.9 ! kristaps 102: xml_begintag(struct md_xml *p, const char *name, enum md_ns ns,
1.6 kristaps 103: int *argc, char **argv)
104: {
1.9 ! kristaps 105: char buf[64];
! 106: ssize_t sz;
! 107: size_t res;
1.6 kristaps 108:
109: switch (ns) {
1.9 ! kristaps 110: case (MD_NS_BLOCK):
! 111: res = strlcpy(buf, "block:", sizeof(buf));
! 112: assert(res < sizeof(buf));
! 113: break;
! 114: case (MD_NS_INLINE):
! 115: res = strlcpy(buf, "inline:", sizeof(buf));
! 116: assert(res < sizeof(buf));
! 117: break;
! 118: default:
! 119: *buf = 0;
! 120: break;
1.6 kristaps 121: }
122:
1.9 ! kristaps 123: res = strlcat(buf, name, sizeof(buf));
! 124: assert(res < sizeof(buf));
! 125:
! 126: if (-1 == (sz = ml_begintag(p->mbuf, buf, argc, argv)))
1.6 kristaps 127: return(0);
128:
1.9 ! kristaps 129: p->pos += sz;
! 130: return(1);
1.6 kristaps 131: }
132:
133:
134: static int
1.9 ! kristaps 135: xml_endtag(struct md_xml *p, const char *name, enum md_ns ns)
1.6 kristaps 136: {
1.9 ! kristaps 137: char buf[64];
! 138: ssize_t sz;
! 139: size_t res;
1.6 kristaps 140:
141: switch (ns) {
1.9 ! kristaps 142: case (MD_NS_BLOCK):
! 143: res = strlcpy(buf, "block:", sizeof(buf));
! 144: assert(res < sizeof(buf));
! 145: break;
! 146: case (MD_NS_INLINE):
! 147: res = strlcpy(buf, "inline:", sizeof(buf));
! 148: assert(res < sizeof(buf));
! 149: break;
! 150: default:
! 151: *buf = 0;
! 152: break;
1.6 kristaps 153: }
154:
1.9 ! kristaps 155: res = strlcat(buf, name, sizeof(buf));
! 156: assert(res < sizeof(buf));
! 157:
! 158: if (-1 == (sz = ml_endtag(p->mbuf, buf)))
1.6 kristaps 159: return(0);
1.1 kristaps 160:
1.9 ! kristaps 161: p->pos += sz;
! 162: return(1);
1.1 kristaps 163: }
164:
165:
166: static int
1.9 ! kristaps 167: xml_nputstring(struct md_xml *p, const char *buf, size_t sz)
1.1 kristaps 168: {
1.9 ! kristaps 169: ssize_t res;
1.2 kristaps 170:
1.9 ! kristaps 171: if (-1 == (res = ml_nputstring(p->mbuf, buf, sz)))
! 172: return(0);
! 173: p->pos += res;
1.2 kristaps 174: return(1);
175: }
176:
177:
178: static int
1.9 ! kristaps 179: xml_nputs(struct md_xml *p, const char *buf, size_t sz)
1.2 kristaps 180: {
1.9 ! kristaps 181: ssize_t res;
1.1 kristaps 182:
1.9 ! kristaps 183: if (-1 == (res = ml_nputs(p->mbuf, buf, sz)))
! 184: return(0);
! 185: p->pos += res;
! 186: return(1);
1.1 kristaps 187: }
188:
189:
190: static int
1.9 ! kristaps 191: xml_puts(struct md_xml *p, const char *buf)
1.2 kristaps 192: {
193:
1.9 ! kristaps 194: return(xml_nputs(p, buf, strlen(buf)));
1.2 kristaps 195: }
196:
197:
198: static int
1.9 ! kristaps 199: xml_indent(struct md_xml *p)
1.1 kristaps 200: {
1.9 ! kristaps 201: ssize_t res;
1.1 kristaps 202:
1.9 ! kristaps 203: if (-1 == (res = ml_indent(p->mbuf, p->indent)))
! 204: return(0);
! 205: p->pos += res;
1.1 kristaps 206: return(1);
207: }
208:
209:
210: static int
211: mbuf_newline(struct md_xml *p)
212: {
213:
214: if ( ! md_buf_putchar(p->mbuf, '\n'))
215: return(0);
216:
217: p->pos = 0;
218: return(1);
219: }
220:
221:
222: static int
223: mbuf_data(struct md_xml *p, int space, char *buf)
224: {
225: size_t sz;
226: char *bufp;
227:
228: assert(p->mbuf);
229: assert(0 != p->indent);
230:
1.8 kristaps 231: if (MD_OVERRIDE_ONE & p->flags || MD_OVERRIDE_ALL & p->flags)
232: space = 0;
233:
1.1 kristaps 234: if (MD_LITERAL & p->flags)
1.9 ! kristaps 235: return(xml_nputstring(p, buf, sizeof(buf)));
1.1 kristaps 236:
237: while (*buf) {
238: while (*buf && isspace(*buf))
239: buf++;
240:
241: if (0 == *buf)
242: break;
243:
244: bufp = buf;
245: while (*buf && ! isspace(*buf))
246: buf++;
247:
248: if (0 != *buf)
249: *buf++ = 0;
250:
251: sz = strlen(bufp);
252:
253: if (0 == p->pos) {
1.9 ! kristaps 254: if ( ! xml_indent(p))
1.1 kristaps 255: return(0);
1.9 ! kristaps 256: if ( ! xml_nputstring(p, bufp, sz))
1.1 kristaps 257: return(0);
1.8 kristaps 258: if (p->indent * MAXINDENT + sz >= COLUMNS)
1.1 kristaps 259: if ( ! mbuf_newline(p))
260: return(0);
1.8 kristaps 261: if ( ! (MD_OVERRIDE_ALL & p->flags))
262: space = 1;
1.1 kristaps 263: continue;
264: }
265:
266: if (space && sz + p->pos >= COLUMNS) {
267: if ( ! mbuf_newline(p))
268: return(0);
1.9 ! kristaps 269: if ( ! xml_indent(p))
1.1 kristaps 270: return(0);
271: } else if (space) {
1.9 ! kristaps 272: if ( ! xml_nputs(p, " ", 1))
1.1 kristaps 273: return(0);
274: }
275:
1.9 ! kristaps 276: if ( ! xml_nputstring(p, bufp, sz))
1.1 kristaps 277: return(0);
278:
1.8 kristaps 279: if ( ! (MD_OVERRIDE_ALL & p->flags))
280: space = 1;
1.1 kristaps 281: }
282:
283: return(1);
284: }
285:
286:
287: int
288: md_line_xml(void *arg, char *buf)
289: {
290: struct md_xml *p;
291:
292: p = (struct md_xml *)arg;
293: return(roff_engine(p->tree, buf));
294: }
295:
296:
297: int
298: md_exit_xml(void *data, int flush)
299: {
300: int c;
301: struct md_xml *p;
302:
303: p = (struct md_xml *)data;
304: c = roff_free(p->tree, flush);
305: free(p);
306:
307: return(c);
308: }
309:
310:
311: void *
312: md_init_xml(const struct md_args *args,
313: struct md_mbuf *mbuf, const struct md_rbuf *rbuf)
314: {
315: struct roffcb cb;
316: struct md_xml *p;
317:
318: cb.roffhead = roffhead;
319: cb.rofftail = rofftail;
320: cb.roffin = roffin;
321: cb.roffout = roffout;
322: cb.roffblkin = roffblkin;
323: cb.roffblkout = roffblkout;
324: cb.roffspecial = roffspecial;
325: cb.roffmsg = roffmsg;
326: cb.roffdata = roffdata;
327:
328: if (NULL == (p = calloc(1, sizeof(struct md_xml))))
329: err(1, "malloc");
330:
331: p->args = args;
332: p->mbuf = mbuf;
333: p->rbuf = rbuf;
334:
335: assert(mbuf);
336:
337: if (NULL == (p->tree = roff_alloc(&cb, p))) {
338: free(p);
339: return(NULL);
340: }
341:
342: return(p);
343: }
344:
345:
346: /* ARGSUSED */
347: static int
348: roffhead(void *arg)
349: {
350: struct md_xml *p;
351:
352: assert(arg);
353: p = (struct md_xml *)arg;
354:
1.9 ! kristaps 355: if (-1 == xml_puts(p, "<?xml version=\"1.0\" "
1.1 kristaps 356: "encoding=\"UTF-8\"?>\n"))
357: return(0);
1.9 ! kristaps 358: if (-1 == xml_puts(p, "<mdoc xmlns:block=\"block\" "
1.2 kristaps 359: "xmlns:special=\"special\" "
360: "xmlns:inline=\"inline\">"))
1.1 kristaps 361: return(0);
362:
363: p->indent++;
1.8 kristaps 364: mbuf_mode(p, MD_BLKIN);
1.1 kristaps 365: return(mbuf_newline(p));
366: }
367:
368:
369: static int
370: rofftail(void *arg)
371: {
372: struct md_xml *p;
373:
374: assert(arg);
375: p = (struct md_xml *)arg;
376:
377: if (0 != p->pos && ! mbuf_newline(p))
378: return(0);
379:
1.8 kristaps 380: mbuf_mode(p, MD_BLKOUT);
1.9 ! kristaps 381: if ( ! xml_endtag(p, "mdoc", MD_NS_DEFAULT))
1.1 kristaps 382: return(0);
383: return(mbuf_newline(p));
384: }
385:
386:
1.8 kristaps 387: /* ARGSUSED */
1.1 kristaps 388: static int
1.8 kristaps 389: roffspecial(void *arg, int tok, int *argc, char **argv, char **more)
1.1 kristaps 390: {
1.3 kristaps 391: struct md_xml *p;
392:
393: assert(arg);
394: p = (struct md_xml *)arg;
395:
1.6 kristaps 396: /* FIXME: this is completely ad hoc. */
397:
1.3 kristaps 398: switch (tok) {
399: case (ROFF_Ns):
1.8 kristaps 400: p->flags |= MD_OVERRIDE_ONE;
401: break;
402: case (ROFF_Sm):
403: assert(*more);
404: if (0 == strcmp(*more, "on"))
405: p->flags |= MD_OVERRIDE_ALL;
406: else
407: p->flags &= ~MD_OVERRIDE_ALL;
1.3 kristaps 408: break;
409: default:
410: break;
411: }
1.1 kristaps 412:
413: return(1);
414: }
415:
416:
417: static int
418: roffblkin(void *arg, int tok, int *argc, char **argv)
419: {
420: struct md_xml *p;
421:
422: assert(arg);
423: p = (struct md_xml *)arg;
424:
425: if (0 != p->pos) {
426: if ( ! mbuf_newline(p))
427: return(0);
1.9 ! kristaps 428: if ( ! xml_indent(p))
1.1 kristaps 429: return(0);
1.9 ! kristaps 430: } else if ( ! xml_indent(p))
1.1 kristaps 431: return(0);
432:
1.2 kristaps 433: /* FIXME: xml won't like standards args (e.g., p1003.1-90). */
434:
1.6 kristaps 435: p->indent++;
1.8 kristaps 436: mbuf_mode(p, MD_BLKIN);
1.1 kristaps 437:
1.9 ! kristaps 438: if ( ! xml_begintag(p, toknames[tok], MD_NS_BLOCK,
1.6 kristaps 439: argc, argv))
1.1 kristaps 440: return(0);
441: return(mbuf_newline(p));
442: }
443:
444:
445: static int
446: roffblkout(void *arg, int tok)
447: {
448: struct md_xml *p;
449:
450: assert(arg);
451: p = (struct md_xml *)arg;
452:
453: p->indent--;
454:
455: if (0 != p->pos) {
456: if ( ! mbuf_newline(p))
457: return(0);
1.9 ! kristaps 458: if ( ! xml_indent(p))
1.1 kristaps 459: return(0);
1.9 ! kristaps 460: } else if ( ! xml_indent(p))
1.1 kristaps 461: return(0);
462:
1.8 kristaps 463: mbuf_mode(p, MD_BLKOUT);
1.9 ! kristaps 464: if ( ! xml_endtag(p, toknames[tok], MD_NS_BLOCK))
1.1 kristaps 465: return(0);
466: return(mbuf_newline(p));
467: }
468:
469:
470: static int
471: roffin(void *arg, int tok, int *argc, char **argv)
472: {
473: struct md_xml *p;
474:
475: assert(arg);
476: p = (struct md_xml *)arg;
477:
1.8 kristaps 478: if ( ! (MD_OVERRIDE_ONE & p->flags) &&
479: ! (MD_OVERRIDE_ALL & p->flags) &&
480: p->pos + 11 > COLUMNS)
481: if ( ! mbuf_newline(p))
482: return(0);
1.1 kristaps 483:
1.8 kristaps 484: if (0 != p->pos && (MD_TEXT == p->last || MD_OUT == p->last)
485: && ! (MD_OVERRIDE_ONE & p->flags)
486: && ! (MD_OVERRIDE_ALL & p->flags))
1.9 ! kristaps 487: if ( ! xml_nputs(p, " ", 1))
1.1 kristaps 488: return(0);
489:
1.9 ! kristaps 490: if (0 == p->pos && ! xml_indent(p))
1.1 kristaps 491: return(0);
492:
1.8 kristaps 493: mbuf_mode(p, MD_IN);
1.9 ! kristaps 494: return(xml_begintag(p, toknames[tok],
1.6 kristaps 495: MD_NS_INLINE, argc, argv));
1.1 kristaps 496: }
497:
498:
499: static int
500: roffout(void *arg, int tok)
501: {
502: struct md_xml *p;
503:
504: assert(arg);
505: p = (struct md_xml *)arg;
506:
1.9 ! kristaps 507: if (0 == p->pos && ! xml_indent(p))
1.1 kristaps 508: return(0);
509:
1.8 kristaps 510: mbuf_mode(p, MD_OUT);
1.9 ! kristaps 511: return(xml_endtag(p, toknames[tok], MD_NS_INLINE));
1.1 kristaps 512: }
513:
514:
515: static void
516: roffmsg(void *arg, enum roffmsg lvl,
517: const char *buf, const char *pos, char *msg)
518: {
519: char *level;
520: struct md_xml *p;
521:
522: assert(arg);
523: p = (struct md_xml *)arg;
524:
525: switch (lvl) {
526: case (ROFF_WARN):
527: if ( ! (MD_WARN_ALL & p->args->warnings))
528: return;
529: level = "warning";
530: break;
531: case (ROFF_ERROR):
532: level = "error";
533: break;
534: default:
535: abort();
536: }
537:
538: if (pos)
539: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
540: p->rbuf->name, p->rbuf->line, level,
541: msg, pos - buf);
542: else
543: (void)fprintf(stderr, "%s: %s: %s\n",
544: p->rbuf->name, level, msg);
545:
546: }
547:
548:
549: static int
550: roffdata(void *arg, int space, char *buf)
551: {
552: struct md_xml *p;
553:
554: assert(arg);
555: p = (struct md_xml *)arg;
556: if ( ! mbuf_data(p, space, buf))
557: return(0);
558:
1.8 kristaps 559: mbuf_mode(p, MD_TEXT);
1.1 kristaps 560: return(1);
561: }
1.4 kristaps 562:
1.9 ! kristaps 563:
! 564: #ifdef __linux /* FIXME: remove. */
! 565: /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
! 566:
! 567: /*
! 568: * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
! 569: *
! 570: * Permission to use, copy, modify, and distribute this software for any
! 571: * purpose with or without fee is hereby granted, provided that the
! 572: * above copyright notice and this permission notice appear in all
! 573: * copies.
! 574: *
! 575: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
! 576: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
! 577: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
! 578: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
! 579: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
! 580: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
! 581: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
! 582: * PERFORMANCE OF THIS SOFTWARE.
! 583: */
! 584: static size_t
! 585: strlcat(char *dst, const char *src, size_t siz)
! 586: {
! 587: char *d = dst;
! 588: const char *s = src;
! 589: size_t n = siz;
! 590: size_t dlen;
! 591:
! 592: /* Find the end of dst and adjust bytes left but don't go past
! 593: * end */
! 594: while (n-- != 0 && *d != '\0')
! 595: d++;
! 596: dlen = d - dst;
! 597: n = siz - dlen;
! 598:
! 599: if (n == 0)
! 600: return(dlen + strlen(s));
! 601: while (*s != '\0') {
! 602: if (n != 1) {
! 603: *d++ = *s;
! 604: n--;
! 605: }
! 606: s++;
! 607: }
! 608: *d = '\0';
! 609:
! 610: return(dlen + (s - src)); /* count does not include NUL */
! 611: }
! 612:
! 613:
! 614: static size_t
! 615: strlcpy(char *dst, const char *src, size_t siz)
! 616: {
! 617: char *d = dst;
! 618: const char *s = src;
! 619: size_t n = siz;
! 620:
! 621: /* Copy as many bytes as will fit */
! 622: if (n != 0) {
! 623: while (--n != 0) {
! 624: if ((*d++ = *s++) == '\0')
! 625: break;
! 626: }
! 627: }
! 628:
! 629: /* Not enough room in dst, add NUL and traverse rest of src */
! 630: if (n == 0) {
! 631: if (siz != 0)
! 632: *d = '\0'; /* NUL-terminate dst */
! 633: while (*s++)
! 634: ;
! 635: }
! 636:
! 637: return(s - src - 1); /* count does not include NUL */
! 638: }
! 639: #endif /*__linux__*/
CVSweb