]> git.cameronkatri.com Git - mandoc.git/blob - html4_strict.c
Fixed clarity in libmdocml.c.
[mandoc.git] / html4_strict.c
1 /* $Id: html4_strict.c,v 1.4 2008/11/23 23:12:47 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 <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
30 enum roffd {
31 ROFF_ENTER = 0,
32 ROFF_EXIT
33 };
34
35 enum rofftype {
36 ROFF_TITLE,
37 ROFF_COMMENT,
38 ROFF_TEXT,
39 ROFF_LAYOUT
40 };
41
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
46
47 struct rofftree;
48
49 struct rofftok {
50 int id;
51 char name[2];
52 int (*cb)(ROFFCALL_ARGS);
53 enum rofftype type;
54 int flags;
55 #define ROFF_NESTED (1 << 0)
56 #define ROFF_PARSED (1 << 1)
57 #define ROFF_CALLABLE (1 << 2)
58 };
59
60 struct roffnode {
61 int tok;
62 struct roffnode *parent;
63 size_t line;
64 };
65
66 struct rofftree {
67 struct roffnode *last;
68 time_t date;
69 char title[256];
70 char section[256];
71 char volume[256];
72 int state;
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)
78 };
79
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
100 static int rofffind(const char *);
101 static int roffparse(const struct md_args *,
102 struct md_mbuf *,
103 const struct md_rbuf *,
104 const char *, size_t,
105 struct rofftree *);
106 static int textparse(struct md_mbuf *,
107 const struct md_rbuf *,
108 const char *, size_t,
109 const struct rofftree *);
110
111 static void dbg_enter(const struct md_args *, int);
112 static void dbg_leave(const struct md_args *, int);
113
114
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
127 int
128 md_exit_html4_strict(const struct md_args *args, struct md_mbuf *out,
129 const struct md_rbuf *in, int error, void *data)
130 {
131 struct rofftree *tree;
132
133 assert(args);
134 assert(data);
135 tree = (struct rofftree *)data;
136
137 if (-1 == error)
138 out = NULL;
139
140 /* LINTED */
141 while (tree->last)
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 }
150
151 free(tree);
152
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
175 tree->state = ROFF_PRELUDE;
176
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) {
257 warnx("%s: malformed line (line %zu)",
258 in->name, in->line);
259 return(0);
260 } else if (ROFF_Max == (tokid = rofffind(buf + 1))) {
261 warnx("%s: unknown line token `%c%c' (line %zu)",
262 in->name, *(buf + 1),
263 *(buf + 2), in->line);
264 return(0);
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);
287 warnx("%s: body token `%s' in prelude (line %zu)",
288 in->name, tokens[tokid].name, in->line);
289 return(0);
290 case (ROFF_COMMENT):
291 return(1);
292 default:
293 abort();
294 }
295
296 /*
297 * Text-domain checks.
298 */
299
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 }
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;
318 pos = 3;
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;
330 warnx("%s: scope of %s (line %zu) broken by "
331 "%s (line %zu)", in->name,
332 tokens[tokid].name,
333 node->line,
334 tokens[node->tok].name,
335 in->line);
336 return(0);
337 }
338 }
339
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
347 /* LINTED */
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. */
371
372 /* LINTED */
373 for (i = 0; i < ROFF_Max; i++)
374 /* LINTED */
375 if (0 == strncmp(name, tokens[i].name, 2))
376 return((int)i);
377
378 return(ROFF_Max);
379 }
380
381
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
414 static int dbg_lvl = 0;
415
416
417 static void
418 dbg_enter(const struct md_args *args, int tokid)
419 {
420 int i;
421 static char buf[72];
422
423 assert(args);
424 if ( ! (args->dbg & MD_DBG_TREE))
425 return;
426 assert(tokid >= 0 && tokid <= ROFF_Max);
427
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 }
442
443 /* LINTED */
444 for (i = 0; i < dbg_lvl; i++)
445 (void)strlcat(buf, " ", sizeof(buf));
446
447 (void)strlcat(buf, tokens[tokid].name, sizeof(buf));
448
449 (void)printf("%s\n", buf);
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
471 static int
472 roff_Dd(ROFFCALL_ARGS)
473 {
474
475 assert(ROFF_PRELUDE & tree->state);
476 if (ROFF_PRELUDE_Dt & tree->state ||
477 ROFF_PRELUDE_Dd & tree->state) {
478 warnx("%s: prelude `Dd' out-of-order (line %zu)",
479 in->name, in->line);
480 return(0);
481 }
482
483 assert(NULL == tree->last);
484 tree->state |= ROFF_PRELUDE_Dd;
485
486 dbg_enter(arg, ROFF_Dd);
487 return(1);
488 }
489
490
491 static int
492 roff_Dt(ROFFCALL_ARGS)
493 {
494
495 assert(ROFF_PRELUDE & tree->state);
496 if ( ! (ROFF_PRELUDE_Dd & tree->state) ||
497 (ROFF_PRELUDE_Dt & tree->state)) {
498 warnx("%s: prelude `Dt' out-of-order (line %zu)",
499 in->name, in->line);
500 return(0);
501 }
502
503 assert(NULL == tree->last);
504 tree->state |= ROFF_PRELUDE_Dt;
505
506 dbg_enter(arg, ROFF_Dt);
507 return(1);
508 }
509
510
511 static int
512 roff_Os(ROFFCALL_ARGS)
513 {
514
515 if (ROFF_EXIT == type) {
516 roffnode_free(ROFF_Os, tree);
517 dbg_leave(arg, ROFF_Os);
518 return(1);
519 }
520
521 assert(ROFF_PRELUDE & tree->state);
522 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
523 ! (ROFF_PRELUDE_Dd & tree->state)) {
524 warnx("%s: prelude `Os' out-of-order (line %zu)",
525 in->name, in->line);
526 return(0);
527 }
528
529 assert(NULL == tree->last);
530 if (NULL == roffnode_new(ROFF_Os, in->line, tree))
531 return(0);
532
533 tree->state |= ROFF_PRELUDE_Os;
534 tree->state &= ~ROFF_PRELUDE;
535 tree->state |= ROFF_BODY;
536
537 dbg_enter(arg, ROFF_Os);
538 return(1);
539 }
540
541
542 static int
543 roff_Sh(ROFFCALL_ARGS)
544 {
545
546 if (ROFF_EXIT == type) {
547 roffnode_free(ROFF_Sh, tree);
548 dbg_leave(arg, ROFF_Sh);
549 return(1);
550 }
551
552 if (NULL == roffnode_new(ROFF_Sh, in->line, tree))
553 return(0);
554
555 dbg_enter(arg, ROFF_Sh);
556 return(1);
557 }
558
559
560 static int
561 roff_Li(ROFFCALL_ARGS)
562 {
563
564 return(1);
565 }
566
567
568 static int
569 roff_An(ROFFCALL_ARGS)
570 {
571
572 return(1);
573 }