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