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