]> git.cameronkatri.com Git - mandoc.git/blob - validate.c
Clean-ups & documentation.
[mandoc.git] / validate.c
1 /* $Id: validate.c,v 1.32 2009/01/16 12:23:25 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 <stdlib.h>
21
22 #include "private.h"
23
24
25 typedef int (*v_pre)(struct mdoc *, struct mdoc_node *);
26 typedef int (*v_post)(struct mdoc *);
27
28
29 struct valids {
30 v_pre *pre;
31 v_post *post;
32 };
33
34
35 static int pre_display(struct mdoc *, struct mdoc_node *);
36 static int pre_bd(struct mdoc *, struct mdoc_node *);
37 static int pre_bl(struct mdoc *, struct mdoc_node *);
38 static int pre_it(struct mdoc *, struct mdoc_node *);
39 static int pre_prologue(struct mdoc *, struct mdoc_node *);
40 static int pre_prologue(struct mdoc *, struct mdoc_node *);
41 static int pre_prologue(struct mdoc *, struct mdoc_node *);
42
43 static int headchild_err_ge1(struct mdoc *);
44 static int headchild_warn_ge1(struct mdoc *);
45 static int headchild_err_eq0(struct mdoc *);
46 static int elemchild_err_eq0(struct mdoc *);
47 static int elemchild_err_ge1(struct mdoc *);
48 static int elemchild_warn_eq0(struct mdoc *);
49 static int bodychild_warn_ge1(struct mdoc *);
50 static int bodychild_err_eq0(struct mdoc *);
51 static int elemchild_warn_ge1(struct mdoc *);
52 static int post_sh(struct mdoc *);
53 static int post_bl(struct mdoc *);
54 static int post_it(struct mdoc *);
55
56 static v_pre pres_prologue[] = { pre_prologue, NULL };
57 static v_pre pres_d1[] = { pre_display, NULL };
58 static v_pre pres_bd[] = { pre_display, pre_bd, NULL };
59 static v_pre pres_bl[] = { pre_bl, NULL };
60 static v_pre pres_it[] = { pre_it, NULL };
61
62 static v_post posts_bd[] = { headchild_err_eq0, bodychild_warn_ge1, NULL };
63 static v_post posts_text[] = { elemchild_err_ge1, NULL };
64 static v_post posts_wtext[] = { elemchild_warn_ge1, NULL };
65 static v_post posts_notext[] = { elemchild_err_eq0, NULL };
66 static v_post posts_wline[] = { headchild_warn_ge1, bodychild_err_eq0, NULL };
67 static v_post posts_sh[] = { headchild_err_ge1, bodychild_warn_ge1, post_sh, NULL };
68 static v_post posts_bl[] = { headchild_err_eq0, bodychild_warn_ge1, post_bl, NULL };
69 static v_post posts_it[] = { post_it, NULL };
70 static v_post posts_ss[] = { headchild_err_ge1, NULL };
71 static v_post posts_pp[] = { elemchild_warn_eq0, NULL };
72 static v_post posts_d1[] = { headchild_err_ge1, NULL };
73
74
75 const struct valids mdoc_valids[MDOC_MAX] = {
76 { NULL, NULL }, /* \" */
77 { pres_prologue, posts_text }, /* Dd */
78 { pres_prologue, NULL }, /* Dt */
79 { pres_prologue, NULL }, /* Os */
80 /* FIXME: preceding Pp. */
81 /* FIXME: NAME section internal ordering. */
82 /* FIXME: can only be a child of root. */
83 { NULL, posts_sh }, /* Sh */
84 /* FIXME: preceding Pp. */
85 /* FIXME: can only be a child of Sh. */
86 { NULL, posts_ss }, /* Ss */
87 /* FIXME: proceeding... */
88 { NULL, posts_pp }, /* Pp */
89 { pres_d1, posts_d1 }, /* D1 */
90 { pres_d1, posts_d1 }, /* Dl */
91 /* FIXME: preceding Pp. */
92 { pres_bd, posts_bd }, /* Bd */
93 { NULL, NULL }, /* Ed */
94 /* FIXME: preceding Pp. */
95 { pres_bl, posts_bl }, /* Bl */
96 { NULL, NULL }, /* El */
97 { pres_it, posts_it }, /* It */
98 { NULL, posts_text }, /* Ad */
99 /* FIXME */
100 { NULL, NULL }, /* An */
101 { NULL, NULL }, /* Ar */
102
103 { NULL, posts_text }, /* Cd */ /* FIXME: section 4 only. */
104 { NULL, NULL }, /* Cm */
105 { NULL, posts_text }, /* Dv */
106 { NULL, posts_text }, /* Er */ /* FIXME: section 2 only. */
107 { NULL, posts_text }, /* Ev */
108 { NULL, posts_notext }, /* Ex */ /* FIXME: sections 1,6,8 only. */ /* -std required */
109 { NULL, posts_text }, /* Fa */
110 { NULL, NULL }, /* Fd */ /* FIXME: SYNOPSIS section. */
111 { NULL, NULL }, /* Fl */
112 { NULL, posts_text }, /* Fn */
113 { NULL, NULL }, /* Ft */
114 { NULL, posts_text }, /* Ic */
115 { NULL, posts_wtext }, /* In */
116 { NULL, posts_text }, /* Li */
117 { NULL, posts_wtext }, /* Nd */
118 { NULL, NULL }, /* Nm */ /* FIXME: If name not set? */
119 { NULL, posts_wline }, /* Op */
120 { NULL, NULL }, /* Ot */
121 { NULL, NULL }, /* Pa */
122 { NULL, posts_notext }, /* Rv */ /* -std required */
123 { NULL, posts_notext }, /* St */ /* arg required */
124 { NULL, posts_text }, /* Va */
125 { NULL, posts_text }, /* Vt */
126 { NULL, NULL }, /* Xr */ /* FIXME */
127 { NULL, posts_text }, /* %A */
128 { NULL, posts_text }, /* %B */
129 { NULL, posts_text }, /* %D */
130 { NULL, posts_text }, /* %I */
131 { NULL, posts_text }, /* %J */
132 { NULL, posts_text }, /* %N */
133 { NULL, posts_text }, /* %O */
134 { NULL, posts_text }, /* %P */
135 { NULL, posts_text }, /* %R */
136 { NULL, posts_text }, /* %T */
137 { NULL, posts_text }, /* %V */
138 { NULL, NULL }, /* Ac */
139 { NULL, NULL }, /* Ao */
140 { NULL, posts_wline }, /* Aq */
141 { NULL, NULL }, /* At */ /* FIXME */
142 { NULL, NULL }, /* Bc */
143 { NULL, NULL }, /* Bf */
144 { NULL, NULL }, /* Bo */
145 { NULL, posts_wline }, /* Bq */
146 { NULL, NULL }, /* Bsx */
147 { NULL, NULL }, /* Bx */
148 { NULL, NULL }, /* Db */ /* FIXME: boolean */
149 { NULL, NULL }, /* Dc */
150 { NULL, NULL }, /* Do */
151 { NULL, posts_wline }, /* Dq */
152 { NULL, NULL }, /* Ec */
153 { NULL, NULL }, /* Ef */ /* -symbolic, etc. */
154 { NULL, posts_text }, /* Em */
155 { NULL, NULL }, /* Eo */
156 { NULL, NULL }, /* Fx */
157 { NULL, posts_text }, /* Ms */ /* FIXME: which symbols? */
158 { NULL, posts_notext }, /* No */
159 { NULL, posts_notext }, /* Ns */
160 { NULL, NULL }, /* Nx */
161 { NULL, NULL }, /* Ox */
162 { NULL, NULL }, /* Pc */
163 { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */
164 { NULL, NULL }, /* Po */
165 { NULL, posts_wline }, /* Pq */ /* FIXME: ignore following Sh/Ss */
166 { NULL, NULL }, /* Qc */
167 { NULL, posts_wline }, /* Ql */
168 { NULL, NULL }, /* Qo */
169 { NULL, posts_wline }, /* Qq */
170 { NULL, NULL }, /* Re */
171 { NULL, NULL }, /* Rs */
172 { NULL, NULL }, /* Sc */
173 { NULL, NULL }, /* So */
174 { NULL, posts_wline }, /* Sq */
175 { NULL, NULL }, /* Sm */ /* FIXME: boolean */
176 { NULL, posts_text }, /* Sx */
177 { NULL, posts_text }, /* Sy */
178 { NULL, posts_text }, /* Tn */
179 { NULL, NULL }, /* Ux */
180 { NULL, NULL }, /* Xc */
181 { NULL, NULL }, /* Xo */
182 { NULL, NULL }, /* Fo */
183 { NULL, NULL }, /* Fc */
184 { NULL, NULL }, /* Oo */
185 { NULL, NULL }, /* Oc */
186 { NULL, NULL }, /* Bk */
187 { NULL, NULL }, /* Ek */
188 { NULL, posts_notext }, /* Bt */
189 { NULL, NULL }, /* Hf */
190 { NULL, NULL }, /* Fr */
191 { NULL, posts_notext }, /* Ud */
192 };
193
194
195 static int
196 bodychild_err_eq0(struct mdoc *mdoc)
197 {
198
199 if (MDOC_BODY != mdoc->last->type)
200 return(1);
201 if (NULL == mdoc->last->child)
202 return(1);
203 return(mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests no body children"));
204 }
205
206
207 static int
208 bodychild_warn_ge1(struct mdoc *mdoc)
209 {
210
211 if (MDOC_BODY != mdoc->last->type)
212 return(1);
213 if (mdoc->last->child)
214 return(1);
215 return(mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests one or more body children"));
216 }
217
218
219 static int
220 elemchild_warn_eq0(struct mdoc *mdoc)
221 {
222
223 assert(MDOC_ELEM == mdoc->last->type);
224 if (NULL == mdoc->last->child)
225 return(1);
226 return(mdoc_pwarn(mdoc, mdoc->last->child->line,
227 mdoc->last->child->pos, WARN_SYNTAX, "macro suggests no parameters"));
228 }
229
230
231 static int
232 elemchild_warn_ge1(struct mdoc *mdoc)
233 {
234
235 assert(MDOC_ELEM == mdoc->last->type);
236 if (mdoc->last->child)
237 return(1);
238 return(mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests one or more parameters"));
239 }
240
241
242 static int
243 elemchild_err_eq0(struct mdoc *mdoc)
244 {
245
246 assert(MDOC_ELEM == mdoc->last->type);
247 if (NULL == mdoc->last->child)
248 return(1);
249 return(mdoc_err(mdoc, "macro expects no parameters"));
250 }
251
252
253 static int
254 elemchild_err_ge1(struct mdoc *mdoc)
255 {
256
257 assert(MDOC_ELEM == mdoc->last->type);
258 if (mdoc->last->child)
259 return(1);
260 return(mdoc_err(mdoc, "macro expects one or more parameters"));
261 }
262
263
264 static int
265 headchild_err_eq0(struct mdoc *mdoc)
266 {
267
268 if (MDOC_HEAD != mdoc->last->type)
269 return(1);
270 if (NULL == mdoc->last->child)
271 return(1);
272 return(mdoc_perr(mdoc, mdoc->last->child->line,
273 mdoc->last->child->pos, "macro expects no parameters"));
274 }
275
276
277 static int
278 headchild_warn_ge1(struct mdoc *mdoc)
279 {
280
281 if (MDOC_HEAD != mdoc->last->type)
282 return(1);
283 if (mdoc->last->child)
284 return(1);
285 return(mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests one or more parameters"));
286 }
287
288
289 static int
290 headchild_err_ge1(struct mdoc *mdoc)
291 {
292
293 if (MDOC_HEAD != mdoc->last->type)
294 return(1);
295 if (mdoc->last->child)
296 return(1);
297 return(mdoc_err(mdoc, "macro expects one or more parameters"));
298 }
299
300
301 static int
302 pre_display(struct mdoc *mdoc, struct mdoc_node *node)
303 {
304 struct mdoc_node *n;
305
306 if (MDOC_BLOCK != node->type)
307 return(1);
308
309 assert(mdoc->last);
310 for (n = mdoc->last->parent; n; n = n->parent)
311 if (MDOC_BLOCK == n->type)
312 if (MDOC_Bd == n->tok)
313 break;
314 if (NULL == n)
315 return(1);
316 return(mdoc_nerr(mdoc, node, "displays may not be nested"));
317 }
318
319
320 static int
321 pre_bl(struct mdoc *mdoc, struct mdoc_node *node)
322 {
323 int type, err;
324 struct mdoc_arg *argv;
325 size_t i, argc;
326
327 if (MDOC_BLOCK != node->type)
328 return(1);
329 assert(MDOC_Bl == node->tok);
330
331 argv = NULL;
332 argc = node->data.block.argc;
333
334 for (i = type = err = 0; i < argc; i++) {
335 argv = &node->data.block.argv[(int)i];
336 assert(argv);
337 switch (argv->arg) {
338 case (MDOC_Bullet):
339 /* FALLTHROUGH */
340 case (MDOC_Dash):
341 /* FALLTHROUGH */
342 case (MDOC_Enum):
343 /* FALLTHROUGH */
344 case (MDOC_Hyphen):
345 /* FALLTHROUGH */
346 case (MDOC_Item):
347 /* FALLTHROUGH */
348 case (MDOC_Tag):
349 /* FALLTHROUGH */
350 case (MDOC_Diag):
351 /* FALLTHROUGH */
352 case (MDOC_Hang):
353 /* FALLTHROUGH */
354 case (MDOC_Ohang):
355 /* FALLTHROUGH */
356 case (MDOC_Inset):
357 /* FALLTHROUGH */
358 case (MDOC_Column):
359 if (type)
360 err++;
361 type++;
362 break;
363 default:
364 break;
365 }
366 }
367 if (0 == type)
368 return(mdoc_err(mdoc, "no list type specified"));
369 if (0 == err)
370 return(1);
371 assert(argv);
372 return(mdoc_perr(mdoc, argv->line,
373 argv->pos, "only one list type possible"));
374 }
375
376
377 static int
378 pre_bd(struct mdoc *mdoc, struct mdoc_node *node)
379 {
380 int type, err;
381 struct mdoc_arg *argv;
382 size_t i, argc;
383
384 if (MDOC_BLOCK != node->type)
385 return(1);
386 assert(MDOC_Bd == node->tok);
387
388 argv = NULL;
389 argc = node->data.block.argc;
390
391 for (err = i = type = 0; 0 == err && i < argc; i++) {
392 argv = &node->data.block.argv[(int)i];
393 assert(argv);
394 switch (argv->arg) {
395 case (MDOC_Ragged):
396 /* FALLTHROUGH */
397 case (MDOC_Unfilled):
398 /* FALLTHROUGH */
399 case (MDOC_Filled):
400 /* FALLTHROUGH */
401 case (MDOC_Literal):
402 /* FALLTHROUGH */
403 case (MDOC_File):
404 if (type)
405 err++;
406 type++;
407 break;
408 default:
409 break;
410 }
411 }
412 if (0 == type)
413 return(mdoc_err(mdoc, "no display type specified"));
414 if (0 == err)
415 return(1);
416 assert(argv);
417 return(mdoc_perr(mdoc, argv->line,
418 argv->pos, "only one display type possible"));
419 }
420
421
422 static int
423 pre_it(struct mdoc *mdoc, struct mdoc_node *node)
424 {
425
426 if (MDOC_BLOCK != mdoc->last->type)
427 return(1);
428 assert(MDOC_It == mdoc->last->tok);
429
430 if (MDOC_BODY != mdoc->last->parent->type)
431 return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
432 if (MDOC_Bl != mdoc->last->parent->tok)
433 return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
434
435 return(1);
436 }
437
438
439 static int
440 pre_prologue(struct mdoc *mdoc, struct mdoc_node *node)
441 {
442
443 if (SEC_PROLOGUE != mdoc->sec_lastn)
444 return(mdoc_nerr(mdoc, node, "macro may only be invoked in the prologue"));
445 assert(MDOC_ELEM == node->type);
446
447 /* Check for ordering. */
448
449 switch (node->tok) {
450 case (MDOC_Os):
451 if (mdoc->meta.title[0] && mdoc->meta.date)
452 break;
453 return(mdoc_nerr(mdoc, node, "prologue macro out-of-order"));
454 case (MDOC_Dt):
455 if (0 == mdoc->meta.title[0] && mdoc->meta.date)
456 break;
457 return(mdoc_nerr(mdoc, node, "prologue macro out-of-order"));
458 case (MDOC_Dd):
459 if (0 == mdoc->meta.title[0] && 0 == mdoc->meta.date)
460 break;
461 return(mdoc_nerr(mdoc, node, "prologue macro out-of-order"));
462 default:
463 abort();
464 /* NOTREACHED */
465 }
466
467 /* Check for repetition. */
468
469 switch (node->tok) {
470 case (MDOC_Os):
471 if (0 == mdoc->meta.os[0])
472 return(1);
473 break;
474 case (MDOC_Dd):
475 if (0 == mdoc->meta.date)
476 return(1);
477 break;
478 case (MDOC_Dt):
479 if (0 == mdoc->meta.title[0])
480 return(1);
481 break;
482 default:
483 abort();
484 /* NOTREACHED */
485 }
486
487 return(mdoc_nerr(mdoc, node, "prologue macro repeated"));
488 }
489
490
491 /* Warn if `Bl' type-specific syntax isn't reflected in items. */
492 static int
493 post_it(struct mdoc *mdoc)
494 {
495 int type, sv;
496 #define TYPE_NONE (0)
497 #define TYPE_BODY (1)
498 #define TYPE_HEAD (2)
499 size_t i, argc;
500 struct mdoc_node *n;
501
502 if (MDOC_BLOCK != mdoc->last->type)
503 return(1);
504
505 assert(MDOC_It == mdoc->last->tok);
506
507 n = mdoc->last->parent;
508 assert(n);
509 assert(MDOC_Bl == n->tok);
510
511 n = n->parent;
512 assert(MDOC_BLOCK == n->type);
513 assert(MDOC_Bl == n->tok);
514
515 argc = n->data.block.argc;
516 type = TYPE_NONE;
517
518 /* Some types require block-head, some not. */
519
520 for (i = 0; TYPE_NONE == type && i < argc; i++)
521 switch (n->data.block.argv[(int)i].arg) {
522 case (MDOC_Tag):
523 /* FALLTHROUGH */
524 case (MDOC_Diag):
525 /* FALLTHROUGH */
526 case (MDOC_Hang):
527 /* FALLTHROUGH */
528 case (MDOC_Ohang):
529 /* FALLTHROUGH */
530 case (MDOC_Inset):
531 type = TYPE_HEAD;
532 sv = n->data.block.argv[(int)i].arg;
533 break;
534 case (MDOC_Bullet):
535 /* FALLTHROUGH */
536 case (MDOC_Dash):
537 /* FALLTHROUGH */
538 case (MDOC_Enum):
539 /* FALLTHROUGH */
540 case (MDOC_Hyphen):
541 /* FALLTHROUGH */
542 case (MDOC_Item):
543 /* FALLTHROUGH */
544 case (MDOC_Column):
545 type = TYPE_BODY;
546 sv = n->data.block.argv[(int)i].arg;
547 break;
548 default:
549 break;
550 }
551
552 assert(TYPE_NONE != type);
553
554 if (TYPE_HEAD == type) {
555 if (NULL == (n = mdoc->last->data.block.head)) {
556 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
557 return(0);
558 } else if (NULL == n->child)
559 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
560 return(0);
561
562 if (NULL == (n = mdoc->last->data.block.body)) {
563 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
564 return(0);
565 } else if (NULL == n->child)
566 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
567 return(0);
568
569 return(1);
570 }
571
572 if (NULL == (n = mdoc->last->data.block.head)) {
573 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
574 return(0);
575 } else if (NULL == n->child)
576 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
577 return(0);
578
579 if ((n = mdoc->last->data.block.body) && n->child)
580 if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
581 return(0);
582
583 if (MDOC_Column != sv)
584 return(1);
585
586 /* Make sure the number of columns is sane. */
587
588 sv = mdoc->last->parent->parent->data.block.argv->sz;
589 n = mdoc->last->data.block.head->child;
590
591 for (i = 0; n; n = n->next)
592 i++;
593
594 if (i == (size_t)sv)
595 return(1);
596 return(mdoc_err(mdoc, "expected %d list columns, have %d", sv, (int)i));
597
598 #undef TYPE_NONE
599 #undef TYPE_BODY
600 #undef TYPE_HEAD
601 }
602
603
604 /* Make sure that only `It' macros are our body-children. */
605 static int
606 post_bl(struct mdoc *mdoc)
607 {
608 struct mdoc_node *n;
609
610 if (MDOC_BODY != mdoc->last->type)
611 return(1);
612 assert(MDOC_Bl == mdoc->last->tok);
613
614 for (n = mdoc->last->child; n; n = n->next) {
615 if (MDOC_BLOCK == n->type)
616 if (MDOC_It == n->tok)
617 continue;
618 break;
619 }
620 if (NULL == n)
621 return(1);
622 return(mdoc_nerr(mdoc, n, "invalid child of parent macro `Bl'"));
623 }
624
625
626 /* Warn if conventional sections are out of order. */
627 static int
628 post_sh(struct mdoc *mdoc)
629 {
630 enum mdoc_sec sec;
631 int i;
632 struct mdoc_node *n;
633 char *args[MDOC_LINEARG_MAX];
634
635 if (MDOC_HEAD != mdoc->last->type)
636 return(1);
637
638 assert(MDOC_Sh == mdoc->last->tok);
639
640 n = mdoc->last->child;
641 assert(n);
642
643 for (i = 0; n && i < MDOC_LINEARG_MAX; n = n->next, i++) {
644 assert(MDOC_TEXT == n->type);
645 assert(NULL == n->child);
646 assert(n->data.text.string);
647 args[i] = n->data.text.string;
648 }
649
650 sec = mdoc_atosec((size_t)i, (const char **)args);
651 if (SEC_CUSTOM == sec)
652 return(1);
653 if (sec > mdoc->sec_lastn)
654 return(1);
655
656 if (sec == mdoc->sec_lastn)
657 return(mdoc_warn(mdoc, WARN_SYNTAX, "section repeated"));
658 return(mdoc_warn(mdoc, WARN_SYNTAX, "section out of conventional order"));
659 }
660
661
662 int
663 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *node)
664 {
665 v_pre *p;
666
667 /* TODO: character-escape checks. */
668
669 if (MDOC_TEXT == node->type)
670 return(1);
671 assert(MDOC_ROOT != node->type);
672
673 if (NULL == mdoc_valids[node->tok].pre)
674 return(1);
675 for (p = mdoc_valids[node->tok].pre; *p; p++)
676 if ( ! (*p)(mdoc, node))
677 return(0);
678 return(1);
679 }
680
681
682 int
683 mdoc_valid_post(struct mdoc *mdoc)
684 {
685 v_post *p;
686
687 if (MDOC_TEXT == mdoc->last->type)
688 return(1);
689 if (MDOC_ROOT == mdoc->last->type)
690 return(1);
691
692 if (NULL == mdoc_valids[mdoc->last->tok].post)
693 return(1);
694 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
695 if ( ! (*p)(mdoc))
696 return(0);
697
698 return(1);
699 }
700