Annotation of mandoc/html4_strict.c, Revision 1.4
1.4 ! kristaps 1: /* $Id: html4_strict.c,v 1.3 2008/11/23 22:30:53 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 <assert.h>
20: #include <ctype.h>
21: #include <err.h>
22: #include <stdlib.h>
23: #include <stdio.h>
24: #include <string.h>
25: #include <time.h>
26:
27: #include "libmdocml.h"
28: #include "private.h"
29:
1.3 kristaps 30: enum roffd {
31: ROFF_ENTER = 0,
32: ROFF_EXIT
1.1 kristaps 33: };
34:
1.3 kristaps 35: enum rofftype {
36: ROFF_TITLE,
37: ROFF_COMMENT,
38: ROFF_TEXT,
39: ROFF_LAYOUT
1.1 kristaps 40: };
41:
1.4 ! kristaps 42: #define ROFFCALL_ARGS \
! 43: const struct md_args *arg, struct md_mbuf *out, \
! 44: const struct md_rbuf *in, const char *buf, size_t sz, \
! 45: size_t pos, enum roffd type, struct rofftree *tree
1.1 kristaps 46:
1.3 kristaps 47: struct rofftree;
1.1 kristaps 48:
49: struct rofftok {
1.3 kristaps 50: int id;
1.1 kristaps 51: char name[2];
1.3 kristaps 52: int (*cb)(ROFFCALL_ARGS);
1.1 kristaps 53: enum rofftype type;
54: int flags;
1.4 ! kristaps 55: #define ROFF_NESTED (1 << 0)
! 56: #define ROFF_PARSED (1 << 1)
! 57: #define ROFF_CALLABLE (1 << 2)
1.1 kristaps 58: };
59:
60: struct roffnode {
61: int tok;
62: struct roffnode *parent;
1.3 kristaps 63: size_t line;
1.1 kristaps 64: };
65:
1.3 kristaps 66: struct rofftree {
1.1 kristaps 67: struct roffnode *last;
68: time_t date;
69: char title[256];
70: char section[256];
71: char volume[256];
72: int state;
1.3 kristaps 73: #define ROFF_PRELUDE (1 << 1)
74: #define ROFF_PRELUDE_Os (1 << 2)
75: #define ROFF_PRELUDE_Dt (1 << 3)
76: #define ROFF_PRELUDE_Dd (1 << 4)
77: #define ROFF_BODY (1 << 5)
1.1 kristaps 78: };
79:
1.3 kristaps 80: #define ROFF___ 0
81: #define ROFF_Dd 1
82: #define ROFF_Dt 2
83: #define ROFF_Os 3
84: #define ROFF_Sh 4
85: #define ROFF_An 5
86: #define ROFF_Li 6
87: #define ROFF_Max 7
88:
89: static int roff_Dd(ROFFCALL_ARGS);
90: static int roff_Dt(ROFFCALL_ARGS);
91: static int roff_Os(ROFFCALL_ARGS);
92: static int roff_Sh(ROFFCALL_ARGS);
93: static int roff_An(ROFFCALL_ARGS);
94: static int roff_Li(ROFFCALL_ARGS);
95:
96: static struct roffnode *roffnode_new(int, size_t,
97: struct rofftree *);
98: static void roffnode_free(int, struct rofftree *);
99:
1.2 kristaps 100: static int rofffind(const char *);
101: static int roffparse(const struct md_args *,
1.1 kristaps 102: struct md_mbuf *,
103: const struct md_rbuf *,
104: const char *, size_t,
105: struct rofftree *);
1.2 kristaps 106: static int textparse(struct md_mbuf *,
1.1 kristaps 107: const struct md_rbuf *,
108: const char *, size_t,
109: const struct rofftree *);
110:
1.2 kristaps 111: static void dbg_enter(const struct md_args *, int);
112: static void dbg_leave(const struct md_args *, int);
113:
1.1 kristaps 114:
1.3 kristaps 115: static const struct rofftok tokens[ROFF_Max] =
116: {
117: { ROFF___, "\\\"", NULL, ROFF_COMMENT, 0 },
118: { ROFF_Dd, "Dd", roff_Dd, ROFF_TITLE, 0 },
119: { ROFF_Dt, "Dt", roff_Dt, ROFF_TITLE, 0 },
120: { ROFF_Os, "Os", roff_Os, ROFF_TITLE, 0 },
121: { ROFF_Sh, "Sh", roff_Sh, ROFF_LAYOUT, 0 },
122: { ROFF_An, "An", roff_An, ROFF_TEXT, ROFF_PARSED },
123: { ROFF_Li, "Li", roff_Li, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE },
124: };
125:
126:
1.1 kristaps 127: int
128: md_exit_html4_strict(const struct md_args *args, struct md_mbuf *out,
1.3 kristaps 129: const struct md_rbuf *in, int error, void *data)
1.1 kristaps 130: {
131: struct rofftree *tree;
132:
133: assert(args);
134: assert(data);
135: tree = (struct rofftree *)data;
136:
1.3 kristaps 137: if (-1 == error)
138: out = NULL;
139:
140: /* LINTED */
1.1 kristaps 141: while (tree->last)
1.3 kristaps 142: if ( ! (*tokens[tree->last->tok].cb)(args, out, in,
143: NULL, 0, 0, ROFF_EXIT, tree))
144: out = NULL;
145:
146: if (out && (ROFF_PRELUDE & tree->state)) {
147: warnx("%s: prelude never finished", in->name);
148: error = 1;
149: }
1.1 kristaps 150:
151: free(tree);
1.3 kristaps 152:
1.1 kristaps 153: return(error ? 0 : 1);
154: }
155:
156:
157: int
158: md_init_html4_strict(const struct md_args *args, struct md_mbuf *out,
159: const struct md_rbuf *in, void **data)
160: {
161: struct rofftree *tree;
162:
163: assert(args);
164: assert(in);
165: assert(out);
166: assert(data);
167:
168: /* TODO: write HTML-DTD header. */
169:
170: if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
171: warn("malloc");
172: return(0);
173: }
174:
1.3 kristaps 175: tree->state = ROFF_PRELUDE;
176:
1.1 kristaps 177: *data = tree;
178: return(1);
179: }
180:
181:
182: int
183: md_line_html4_strict(const struct md_args *args, struct md_mbuf *out,
184: const struct md_rbuf *in, const char *buf,
185: size_t sz, void *data)
186: {
187: struct rofftree *tree;
188:
189: assert(args);
190: assert(in);
191: assert(data);
192:
193: tree = (struct rofftree *)data;
194:
195: if (0 == sz) {
196: warnx("%s: blank line (line %zu)", in->name, in->line);
197: return(0);
198: } else if ('.' != *buf)
199: return(textparse(out, in, buf, sz, tree));
200:
201: return(roffparse(args, out, in, buf, sz, tree));
202: }
203:
204:
205: static int
206: textparse(struct md_mbuf *out, const struct md_rbuf *in,
207: const char *buf, size_t sz,
208: const struct rofftree *tree)
209: {
210:
211: assert(tree);
212: assert(out);
213: assert(in);
214: assert(buf);
215: assert(sz > 0);
216:
217: if (NULL == tree->last) {
218: warnx("%s: unexpected text (line %zu)",
219: in->name, in->line);
220: return(0);
221: } else if (NULL == tree->last->parent) {
222: warnx("%s: disallowed text (line %zu)",
223: in->name, in->line);
224: return(0);
225: }
226:
227: if ( ! md_buf_puts(out, buf, sz))
228: return(0);
229: return(md_buf_putstring(out, " "));
230: }
231:
232:
233: static int
234: roffparse(const struct md_args *args, struct md_mbuf *out,
235: const struct md_rbuf *in, const char *buf,
236: size_t sz, struct rofftree *tree)
237: {
238: int tokid, t;
239: size_t pos;
240: struct roffnode *node;
241:
242: assert(args);
243: assert(out);
244: assert(in);
245: assert(buf);
246: assert(sz > 0);
247: assert(tree);
248:
249: /*
250: * Extract the token identifier from the buffer. If there's no
251: * callback for the token (comment, etc.) then exit immediately.
252: * We don't do any error handling (yet), so if the token doesn't
253: * exist, die.
254: */
255:
256: if (3 > sz) {
1.3 kristaps 257: warnx("%s: malformed line (line %zu)",
1.1 kristaps 258: in->name, in->line);
259: return(0);
260: } else if (ROFF_Max == (tokid = rofffind(buf + 1))) {
1.3 kristaps 261: warnx("%s: unknown line token `%c%c' (line %zu)",
1.1 kristaps 262: in->name, *(buf + 1),
263: *(buf + 2), in->line);
264: return(0);
1.3 kristaps 265: }
266:
267: /* Domain cross-contamination (and sanity) checks. */
268:
269: switch (tokens[tokid].type) {
270: case (ROFF_TITLE):
271: if (ROFF_PRELUDE & tree->state) {
272: assert( ! (ROFF_BODY & tree->state));
273: break;
274: }
275: assert(ROFF_BODY & tree->state);
276: warnx("%s: prelude token `%s' in body (line %zu)",
277: in->name, tokens[tokid].name, in->line);
278: return(0);
279: case (ROFF_LAYOUT):
280: /* FALLTHROUGH */
281: case (ROFF_TEXT):
282: if (ROFF_BODY & tree->state) {
283: assert( ! (ROFF_PRELUDE & tree->state));
284: break;
285: }
286: assert(ROFF_PRELUDE & tree->state);
1.4 ! kristaps 287: warnx("%s: body token `%s' in prelude (line %zu)",
1.3 kristaps 288: in->name, tokens[tokid].name, in->line);
289: return(0);
1.4 ! kristaps 290: case (ROFF_COMMENT):
! 291: return(1);
1.3 kristaps 292: default:
1.4 ! kristaps 293: abort();
1.3 kristaps 294: }
295:
296: /*
297: * Text-domain checks.
298: */
1.1 kristaps 299:
1.3 kristaps 300: if (ROFF_TEXT == tokens[tokid].type &&
301: ! (ROFF_PARSED & tokens[tokid].flags)) {
302: warnx("%s: text token `%s' not callable (line %zu)",
303: in->name, tokens[tokid].name, in->line);
304: return(0);
305: }
1.1 kristaps 306:
307: /*
308: * If this is a non-nestable layout token and we're below a
309: * token of the same type, then recurse upward to the token,
310: * closing out the interim scopes.
311: *
312: * If there's a nested token on the chain, then raise an error
313: * as nested tokens have corresponding "ending" tokens and we're
314: * breaking their scope.
315: */
316:
317: node = NULL;
1.3 kristaps 318: pos = 3;
1.1 kristaps 319:
320: if (ROFF_LAYOUT == tokens[tokid].type &&
321: ! (ROFF_NESTED & tokens[tokid].flags)) {
322: for (node = tree->last; node; node = node->parent) {
323: if (node->tok == tokid)
324: break;
325:
326: /* Don't break nested scope. */
327:
328: if ( ! (ROFF_NESTED & tokens[node->tok].flags))
329: continue;
1.3 kristaps 330: warnx("%s: scope of %s (line %zu) broken by "
331: "%s (line %zu)", in->name,
332: tokens[tokid].name,
333: node->line,
1.1 kristaps 334: tokens[node->tok].name,
1.3 kristaps 335: in->line);
1.1 kristaps 336: return(0);
337: }
338: }
1.3 kristaps 339:
1.1 kristaps 340: if (node) {
341: assert(ROFF_LAYOUT == tokens[tokid].type);
342: assert( ! (ROFF_NESTED & tokens[tokid].flags));
343: assert(node->tok == tokid);
344:
345: /* Clear up to last scoped token. */
346:
1.3 kristaps 347: /* LINTED */
1.1 kristaps 348: do {
349: t = tree->last->tok;
350: if ( ! (*tokens[tree->last->tok].cb)
351: (args, out, in, NULL,
352: 0, 0, ROFF_EXIT, tree))
353: return(0);
354: } while (t != tokid);
355: }
356:
357: /* Proceed with actual token processing. */
358:
359: return((*tokens[tokid].cb)(args, out, in, buf, sz,
360: pos, ROFF_ENTER, tree));
361: }
362:
363:
364: static int
365: rofffind(const char *name)
366: {
367: size_t i;
368:
369: assert(name);
370: /* FIXME: use a table, this is slow but ok for now. */
1.3 kristaps 371:
372: /* LINTED */
1.1 kristaps 373: for (i = 0; i < ROFF_Max; i++)
1.3 kristaps 374: /* LINTED */
1.1 kristaps 375: if (0 == strncmp(name, tokens[i].name, 2))
1.3 kristaps 376: return((int)i);
1.1 kristaps 377:
378: return(ROFF_Max);
379: }
380:
381:
1.3 kristaps 382: static struct roffnode *
383: roffnode_new(int tokid, size_t line, struct rofftree *tree)
384: {
385: struct roffnode *p;
386:
387: if (NULL == (p = malloc(sizeof(struct roffnode)))) {
388: warn("malloc");
389: return(NULL);
390: }
391:
392: p->line = line;
393: p->tok = tokid;
394: p->parent = tree->last;
395: tree->last = p;
396: return(p);
397: }
398:
399:
400: static void
401: roffnode_free(int tokid, struct rofftree *tree)
402: {
403: struct roffnode *p;
404:
405: assert(tree->last);
406: assert(tree->last->tok == tokid);
407:
408: p = tree->last;
409: tree->last = tree->last->parent;
410: free(p);
411: }
412:
413:
1.4 ! kristaps 414: static int dbg_lvl = 0;
1.3 kristaps 415:
416:
417: static void
418: dbg_enter(const struct md_args *args, int tokid)
419: {
420: int i;
1.4 ! kristaps 421: static char buf[72];
1.3 kristaps 422:
423: assert(args);
424: if ( ! (args->dbg & MD_DBG_TREE))
425: return;
1.4 ! kristaps 426: assert(tokid >= 0 && tokid <= ROFF_Max);
1.3 kristaps 427:
1.4 ! kristaps 428: buf[0] = 0;
! 429:
! 430: switch (tokens[tokid].type) {
! 431: case (ROFF_LAYOUT):
! 432: /* FALLTHROUGH */
! 433: case (ROFF_TEXT):
! 434: (void)strlcat(buf, "body: ", sizeof(buf));
! 435: break;
! 436: case (ROFF_TITLE):
! 437: (void)strlcat(buf, "prelude: ", sizeof(buf));
! 438: break;
! 439: default:
! 440: abort();
! 441: }
1.3 kristaps 442:
443: /* LINTED */
444: for (i = 0; i < dbg_lvl; i++)
1.4 ! kristaps 445: (void)strlcat(buf, " ", sizeof(buf));
! 446:
! 447: (void)strlcat(buf, tokens[tokid].name, sizeof(buf));
1.3 kristaps 448:
1.4 ! kristaps 449: (void)printf("%s\n", buf);
1.3 kristaps 450:
451: if (ROFF_LAYOUT == tokens[tokid].type)
452: dbg_lvl++;
453: }
454:
455:
456: static void
457: dbg_leave(const struct md_args *args, int tokid)
458: {
459: assert(args);
460: if ( ! (args->dbg & MD_DBG_TREE))
461: return;
462: if (ROFF_LAYOUT != tokens[tokid].type)
463: return;
464:
465: assert(tokid >= 0 && tokid <= ROFF_Max);
466: assert(dbg_lvl > 0);
467: dbg_lvl--;
468: }
469:
470:
1.1 kristaps 471: static int
472: roff_Dd(ROFFCALL_ARGS)
473: {
474:
1.3 kristaps 475: assert(ROFF_PRELUDE & tree->state);
476: if (ROFF_PRELUDE_Dt & tree->state ||
477: ROFF_PRELUDE_Dd & tree->state) {
1.4 ! kristaps 478: warnx("%s: prelude `Dd' out-of-order (line %zu)",
1.3 kristaps 479: in->name, in->line);
1.1 kristaps 480: return(0);
481: }
482:
1.3 kristaps 483: assert(NULL == tree->last);
1.1 kristaps 484: tree->state |= ROFF_PRELUDE_Dd;
1.2 kristaps 485:
1.3 kristaps 486: dbg_enter(arg, ROFF_Dd);
1.1 kristaps 487: return(1);
488: }
489:
490:
491: static int
492: roff_Dt(ROFFCALL_ARGS)
493: {
494:
1.3 kristaps 495: assert(ROFF_PRELUDE & tree->state);
1.1 kristaps 496: if ( ! (ROFF_PRELUDE_Dd & tree->state) ||
497: (ROFF_PRELUDE_Dt & tree->state)) {
1.4 ! kristaps 498: warnx("%s: prelude `Dt' out-of-order (line %zu)",
1.3 kristaps 499: in->name, in->line);
500: return(0);
1.1 kristaps 501: }
502:
1.3 kristaps 503: assert(NULL == tree->last);
1.1 kristaps 504: tree->state |= ROFF_PRELUDE_Dt;
1.2 kristaps 505:
1.3 kristaps 506: dbg_enter(arg, ROFF_Dt);
1.1 kristaps 507: return(1);
508: }
509:
510:
511: static int
512: roff_Os(ROFFCALL_ARGS)
513: {
514:
515: if (ROFF_EXIT == type) {
1.3 kristaps 516: roffnode_free(ROFF_Os, tree);
1.2 kristaps 517: dbg_leave(arg, ROFF_Os);
1.1 kristaps 518: return(1);
519: }
520:
1.3 kristaps 521: assert(ROFF_PRELUDE & tree->state);
522: if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
523: ! (ROFF_PRELUDE_Dd & tree->state)) {
1.4 ! kristaps 524: warnx("%s: prelude `Os' out-of-order (line %zu)",
1.3 kristaps 525: in->name, in->line);
1.1 kristaps 526: return(0);
527: }
528:
1.3 kristaps 529: assert(NULL == tree->last);
530: if (NULL == roffnode_new(ROFF_Os, in->line, tree))
1.1 kristaps 531: return(0);
532:
533: tree->state |= ROFF_PRELUDE_Os;
1.3 kristaps 534: tree->state &= ~ROFF_PRELUDE;
535: tree->state |= ROFF_BODY;
1.1 kristaps 536:
1.2 kristaps 537: dbg_enter(arg, ROFF_Os);
1.1 kristaps 538: return(1);
539: }
540:
541:
1.3 kristaps 542: static int
1.1 kristaps 543: roff_Sh(ROFFCALL_ARGS)
544: {
1.2 kristaps 545:
546: if (ROFF_EXIT == type) {
1.3 kristaps 547: roffnode_free(ROFF_Sh, tree);
1.2 kristaps 548: dbg_leave(arg, ROFF_Sh);
549: return(1);
550: }
551:
1.3 kristaps 552: if (NULL == roffnode_new(ROFF_Sh, in->line, tree))
1.2 kristaps 553: return(0);
554:
555: dbg_enter(arg, ROFF_Sh);
1.1 kristaps 556: return(1);
557: }
558:
1.2 kristaps 559:
1.3 kristaps 560: static int
561: roff_Li(ROFFCALL_ARGS)
1.2 kristaps 562: {
563:
1.3 kristaps 564: return(1);
1.2 kristaps 565: }
566:
567:
1.3 kristaps 568: static int
569: roff_An(ROFFCALL_ARGS)
1.2 kristaps 570: {
571:
1.3 kristaps 572: return(1);
1.2 kristaps 573: }
CVSweb