Annotation of mandoc/xml.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: xml.c,v 1.5 2008/12/01 16:01:28 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.5 kristaps 31: #define MAXINDENT 8
1.6 ! kristaps 32: #define COLUMNS 72
1.1 kristaps 33:
34: #ifdef __linux__ /* FIXME */
35: #define strlcat strncat
36: #endif
37:
1.6 ! kristaps 38: enum md_ns {
! 39: MD_NS_BLOCK,
! 40: MD_NS_INLINE,
! 41: MD_NS_DEFAULT
! 42: };
! 43:
1.1 kristaps 44: enum md_tok {
45: MD_BLKIN,
46: MD_BLKOUT,
47: MD_IN,
48: MD_OUT,
1.3 kristaps 49: MD_TEXT,
50: MD_OVERRIDE
1.1 kristaps 51: };
52:
53: struct md_xml {
54: const struct md_args *args;
55: const struct md_rbuf *rbuf;
56:
57: struct md_mbuf *mbuf;
58: struct rofftree *tree;
59: size_t indent;
60: size_t pos;
61: enum md_tok last;
62: int flags;
63: #define MD_LITERAL (1 << 0) /* FIXME */
64: };
65:
66: static void roffmsg(void *arg, enum roffmsg,
67: const char *, const char *, char *);
68: static int roffhead(void *);
69: static int rofftail(void *);
70: static int roffin(void *, int, int *, char **);
71: static int roffdata(void *, int, char *);
72: static int roffout(void *, int);
73: static int roffblkin(void *, int, int *, char **);
74: static int roffblkout(void *, int);
75: static int roffspecial(void *, int);
76:
77: static int mbuf_newline(struct md_xml *);
78: static int mbuf_indent(struct md_xml *);
79: static int mbuf_data(struct md_xml *, int, char *);
80: static int mbuf_putstring(struct md_xml *,
81: const char *);
82: static int mbuf_nputstring(struct md_xml *,
83: const char *, size_t);
1.2 kristaps 84: static int mbuf_puts(struct md_xml *, const char *);
85: static int mbuf_nputs(struct md_xml *,
86: const char *, size_t);
1.6 ! kristaps 87: static int mbuf_begintag(struct md_xml *, const char *,
! 88: enum md_ns, int *, char **);
! 89: static int mbuf_endtag(struct md_xml *,
! 90: const char *, enum md_ns);
! 91:
! 92:
! 93: static int
! 94: mbuf_begintag(struct md_xml *p, const char *name, enum md_ns ns,
! 95: int *argc, char **argv)
! 96: {
! 97: int i;
! 98:
! 99: if ( ! mbuf_nputs(p, "<", 1))
! 100: return(0);
! 101:
! 102: switch (ns) {
! 103: case (MD_NS_BLOCK):
! 104: if ( ! mbuf_nputs(p, "block:", 6))
! 105: return(0);
! 106: break;
! 107: case (MD_NS_INLINE):
! 108: if ( ! mbuf_nputs(p, "inline:", 7))
! 109: return(0);
! 110: break;
! 111: default:
! 112: break;
! 113: }
! 114:
! 115: if ( ! mbuf_puts(p, name))
! 116: return(0);
! 117:
! 118: for (i = 0; ROFF_ARGMAX != argc[i]; i++) {
! 119: if ( ! mbuf_nputs(p, " ", 1))
! 120: return(0);
! 121: if ( ! mbuf_puts(p, tokargnames[argc[i]]))
! 122: return(0);
! 123: if ( ! mbuf_nputs(p, "=\"", 2))
! 124: return(0);
! 125: if ( ! mbuf_putstring(p, argv[i] ? argv[i] : "true"))
! 126: return(0);
! 127: if ( ! mbuf_nputs(p, "\"", 1))
! 128: return(0);
! 129: }
! 130: return(mbuf_nputs(p, ">", 1));
! 131: }
! 132:
! 133:
! 134: static int
! 135: mbuf_endtag(struct md_xml *p, const char *tag, enum md_ns ns)
! 136: {
! 137: if ( ! mbuf_nputs(p, "</", 2))
! 138: return(0);
! 139:
! 140: switch (ns) {
! 141: case (MD_NS_BLOCK):
! 142: if ( ! mbuf_nputs(p, "block:", 6))
! 143: return(0);
! 144: break;
! 145: case (MD_NS_INLINE):
! 146: if ( ! mbuf_nputs(p, "inline:", 7))
! 147: return(0);
! 148: break;
! 149: default:
! 150: break;
! 151: }
! 152:
! 153: if ( ! mbuf_puts(p, tag))
! 154: return(0);
! 155: return(mbuf_nputs(p, ">", 1));
! 156: }
1.1 kristaps 157:
158:
159: static int
160: mbuf_putstring(struct md_xml *p, const char *buf)
161: {
162:
163: return(mbuf_nputstring(p, buf, strlen(buf)));
164: }
165:
166:
167: static int
168: mbuf_nputstring(struct md_xml *p, const char *buf, size_t sz)
169: {
1.2 kristaps 170: size_t i;
171:
172: for (i = 0; i < sz; i++) {
173: switch (buf[i]) {
174: case ('&'):
175: if ( ! md_buf_puts(p->mbuf, "&", 5))
176: return(0);
177: p->pos += 5;
178: break;
179: case ('"'):
180: if ( ! md_buf_puts(p->mbuf, """, 6))
181: return(0);
182: p->pos += 6;
183: break;
1.6 ! kristaps 184: case ('<'):
! 185: if ( ! md_buf_puts(p->mbuf, "<", 4))
! 186: return(0);
! 187: p->pos += 4;
! 188: break;
! 189: case ('>'):
! 190: if ( ! md_buf_puts(p->mbuf, ">", 4))
! 191: return(0);
! 192: p->pos += 4;
! 193: break;
1.2 kristaps 194: default:
195: if ( ! md_buf_putchar(p->mbuf, buf[i]))
196: return(0);
197: p->pos++;
198: break;
199: }
200: }
201: return(1);
202: }
203:
204:
205: static int
206: mbuf_nputs(struct md_xml *p, const char *buf, size_t sz)
207: {
1.1 kristaps 208:
209: p->pos += sz;
210: return(md_buf_puts(p->mbuf, buf, sz));
211: }
212:
213:
214: static int
1.2 kristaps 215: mbuf_puts(struct md_xml *p, const char *buf)
216: {
217:
218: return(mbuf_nputs(p, buf, strlen(buf)));
219: }
220:
221:
222: static int
1.1 kristaps 223: mbuf_indent(struct md_xml *p)
224: {
225: size_t i;
226:
227: assert(p->pos == 0);
228:
229: /* LINTED */
1.5 kristaps 230: for (i = 0; i < MIN(p->indent, MAXINDENT); i++)
1.1 kristaps 231: if ( ! md_buf_putstring(p->mbuf, " "))
232: return(0);
233:
1.5 kristaps 234: p->pos += i * 4;
1.1 kristaps 235: return(1);
236: }
237:
238:
239: static int
240: mbuf_newline(struct md_xml *p)
241: {
242:
243: if ( ! md_buf_putchar(p->mbuf, '\n'))
244: return(0);
245:
246: p->pos = 0;
247: return(1);
248: }
249:
250:
251: static int
252: mbuf_data(struct md_xml *p, int space, char *buf)
253: {
254: size_t sz;
255: char *bufp;
256:
257: assert(p->mbuf);
258: assert(0 != p->indent);
259:
260: if (MD_LITERAL & p->flags)
261: return(mbuf_putstring(p, buf));
262:
263: while (*buf) {
264: while (*buf && isspace(*buf))
265: buf++;
266:
267: if (0 == *buf)
268: break;
269:
270: bufp = buf;
271: while (*buf && ! isspace(*buf))
272: buf++;
273:
274: if (0 != *buf)
275: *buf++ = 0;
276:
277: sz = strlen(bufp);
278:
279: if (0 == p->pos) {
280: if ( ! mbuf_indent(p))
281: return(0);
282: if ( ! mbuf_nputstring(p, bufp, sz))
283: return(0);
1.5 kristaps 284: if (p->indent * MAXINDENT + sz >= COLUMNS) {
1.1 kristaps 285: if ( ! mbuf_newline(p))
286: return(0);
287: continue;
288: }
289: continue;
290: }
291:
292: if (space && sz + p->pos >= COLUMNS) {
293: if ( ! mbuf_newline(p))
294: return(0);
295: if ( ! mbuf_indent(p))
296: return(0);
297: } else if (space) {
1.2 kristaps 298: if ( ! mbuf_nputs(p, " ", 1))
1.1 kristaps 299: return(0);
300: }
301:
302: if ( ! mbuf_nputstring(p, bufp, sz))
303: return(0);
304:
305: if ( ! space && p->pos >= COLUMNS)
306: if ( ! mbuf_newline(p))
307: return(0);
308: }
309:
310: return(1);
311: }
312:
313:
314: int
315: md_line_xml(void *arg, char *buf)
316: {
317: struct md_xml *p;
318:
319: p = (struct md_xml *)arg;
320: return(roff_engine(p->tree, buf));
321: }
322:
323:
324: int
325: md_exit_xml(void *data, int flush)
326: {
327: int c;
328: struct md_xml *p;
329:
330: p = (struct md_xml *)data;
331: c = roff_free(p->tree, flush);
332: free(p);
333:
334: return(c);
335: }
336:
337:
338: void *
339: md_init_xml(const struct md_args *args,
340: struct md_mbuf *mbuf, const struct md_rbuf *rbuf)
341: {
342: struct roffcb cb;
343: struct md_xml *p;
344:
345: cb.roffhead = roffhead;
346: cb.rofftail = rofftail;
347: cb.roffin = roffin;
348: cb.roffout = roffout;
349: cb.roffblkin = roffblkin;
350: cb.roffblkout = roffblkout;
351: cb.roffspecial = roffspecial;
352: cb.roffmsg = roffmsg;
353: cb.roffdata = roffdata;
354:
355: if (NULL == (p = calloc(1, sizeof(struct md_xml))))
356: err(1, "malloc");
357:
358: p->args = args;
359: p->mbuf = mbuf;
360: p->rbuf = rbuf;
361:
362: assert(mbuf);
363:
364: if (NULL == (p->tree = roff_alloc(&cb, p))) {
365: free(p);
366: return(NULL);
367: }
368:
369: return(p);
370: }
371:
372:
373: /* ARGSUSED */
374: static int
375: roffhead(void *arg)
376: {
377: struct md_xml *p;
378:
379: assert(arg);
380: p = (struct md_xml *)arg;
381:
1.2 kristaps 382: if ( ! mbuf_puts(p, "<?xml version=\"1.0\" "
1.1 kristaps 383: "encoding=\"UTF-8\"?>\n"))
384: return(0);
1.2 kristaps 385: if ( ! mbuf_puts(p, "<mdoc xmlns:block=\"block\" "
386: "xmlns:special=\"special\" "
387: "xmlns:inline=\"inline\">"))
1.1 kristaps 388: return(0);
389:
390: p->indent++;
391: p->last = MD_BLKIN;
392: return(mbuf_newline(p));
393: }
394:
395:
396: static int
397: rofftail(void *arg)
398: {
399: struct md_xml *p;
400:
401: assert(arg);
402: p = (struct md_xml *)arg;
403:
404: if (0 != p->pos && ! mbuf_newline(p))
405: return(0);
406:
1.6 ! kristaps 407: p->last = MD_BLKOUT;
! 408: if ( ! mbuf_endtag(p, "mdoc", MD_NS_DEFAULT))
1.1 kristaps 409: return(0);
410:
411: return(mbuf_newline(p));
412: }
413:
414:
415: static int
416: roffspecial(void *arg, int tok)
417: {
1.3 kristaps 418: struct md_xml *p;
419:
420: assert(arg);
421: p = (struct md_xml *)arg;
422:
1.6 ! kristaps 423: /* FIXME: this is completely ad hoc. */
! 424:
1.3 kristaps 425: switch (tok) {
426: case (ROFF_Ns):
427: p->last = MD_OVERRIDE;
428: break;
429: default:
430: break;
431: }
1.1 kristaps 432:
433: return(1);
434: }
435:
436:
437: static int
438: roffblkin(void *arg, int tok, int *argc, char **argv)
439: {
440: struct md_xml *p;
441:
442: assert(arg);
443: p = (struct md_xml *)arg;
444:
445: if (0 != p->pos) {
446: if ( ! mbuf_newline(p))
447: return(0);
448: if ( ! mbuf_indent(p))
449: return(0);
450: } else if ( ! mbuf_indent(p))
451: return(0);
452:
1.2 kristaps 453: /* FIXME: xml won't like standards args (e.g., p1003.1-90). */
454:
1.6 ! kristaps 455: p->last = MD_BLKIN;
! 456: p->indent++;
1.1 kristaps 457:
1.6 ! kristaps 458: if ( ! mbuf_begintag(p, toknames[tok], MD_NS_BLOCK,
! 459: argc, argv))
1.1 kristaps 460: return(0);
461: return(mbuf_newline(p));
462: }
463:
464:
465: static int
466: roffblkout(void *arg, int tok)
467: {
468: struct md_xml *p;
469:
470: assert(arg);
471: p = (struct md_xml *)arg;
472:
473: p->indent--;
474:
475: if (0 != p->pos) {
476: if ( ! mbuf_newline(p))
477: return(0);
478: if ( ! mbuf_indent(p))
479: return(0);
480: } else if ( ! mbuf_indent(p))
481: return(0);
482:
1.6 ! kristaps 483: p->last = MD_BLKOUT;
! 484:
! 485: if ( ! mbuf_endtag(p, toknames[tok], MD_NS_BLOCK))
1.1 kristaps 486: return(0);
487: return(mbuf_newline(p));
488: }
489:
490:
491: static int
492: roffin(void *arg, int tok, int *argc, char **argv)
493: {
494: struct md_xml *p;
495:
496: assert(arg);
497: p = (struct md_xml *)arg;
498:
499: /*
500: * FIXME: put all of this in a buffer, then check the buffer
501: * length versus the column width for nicer output. This is a
502: * bit hacky.
503: */
504:
505: if (p->pos + 11 > COLUMNS)
506: if ( ! mbuf_newline(p))
507: return(0);
508:
509: if (0 != p->pos) {
510: switch (p->last) {
511: case (MD_TEXT):
512: /* FALLTHROUGH */
513: case (MD_OUT):
1.2 kristaps 514: if ( ! mbuf_nputs(p, " ", 1))
1.1 kristaps 515: return(0);
516: break;
517: default:
518: break;
519: }
520: } else if ( ! mbuf_indent(p))
521: return(0);
522:
523: p->last = MD_IN;
1.6 ! kristaps 524: return(mbuf_begintag(p, toknames[tok],
! 525: MD_NS_INLINE, argc, argv));
1.1 kristaps 526: }
527:
528:
529: static int
530: roffout(void *arg, int tok)
531: {
532: struct md_xml *p;
533:
534: assert(arg);
535: p = (struct md_xml *)arg;
536:
537: if (0 == p->pos && ! mbuf_indent(p))
538: return(0);
539:
540: p->last = MD_OUT;
1.6 ! kristaps 541: return(mbuf_endtag(p, toknames[tok], MD_NS_INLINE));
1.1 kristaps 542: }
543:
544:
545: static void
546: roffmsg(void *arg, enum roffmsg lvl,
547: const char *buf, const char *pos, char *msg)
548: {
549: char *level;
550: struct md_xml *p;
551:
552: assert(arg);
553: p = (struct md_xml *)arg;
554:
555: switch (lvl) {
556: case (ROFF_WARN):
557: if ( ! (MD_WARN_ALL & p->args->warnings))
558: return;
559: level = "warning";
560: break;
561: case (ROFF_ERROR):
562: level = "error";
563: break;
564: default:
565: abort();
566: }
567:
568: if (pos)
569: (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
570: p->rbuf->name, p->rbuf->line, level,
571: msg, pos - buf);
572: else
573: (void)fprintf(stderr, "%s: %s: %s\n",
574: p->rbuf->name, level, msg);
575:
576: }
577:
578:
579: static int
580: roffdata(void *arg, int space, char *buf)
581: {
582: struct md_xml *p;
583:
584: assert(arg);
585: p = (struct md_xml *)arg;
586: if ( ! mbuf_data(p, space, buf))
587: return(0);
588:
589: p->last = MD_TEXT;
590: return(1);
591: }
1.4 kristaps 592:
CVSweb