]>
git.cameronkatri.com Git - mandoc.git/blob - validate.c
1 /* $Id: validate.c,v 1.34 2009/01/16 14:15:12 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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
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.
25 typedef int (*v_pre
)(struct mdoc
*, struct mdoc_node
*);
26 typedef int (*v_post
)(struct mdoc
*);
34 static int pre_check_parent(struct mdoc
*, struct mdoc_node
*,
36 static int pre_check_msecs(struct mdoc
*, struct mdoc_node
*,
37 int, enum mdoc_msec
*);
39 static int pre_display(struct mdoc
*, struct mdoc_node
*);
40 static int pre_sh(struct mdoc
*, struct mdoc_node
*);
41 static int pre_ss(struct mdoc
*, struct mdoc_node
*);
42 static int pre_bd(struct mdoc
*, struct mdoc_node
*);
43 static int pre_bl(struct mdoc
*, struct mdoc_node
*);
44 static int pre_it(struct mdoc
*, struct mdoc_node
*);
45 static int pre_cd(struct mdoc
*, struct mdoc_node
*);
46 static int pre_er(struct mdoc
*, struct mdoc_node
*);
47 static int pre_ex(struct mdoc
*, struct mdoc_node
*);
48 static int pre_prologue(struct mdoc
*, struct mdoc_node
*);
49 static int pre_prologue(struct mdoc
*, struct mdoc_node
*);
50 static int pre_prologue(struct mdoc
*, struct mdoc_node
*);
52 static int head_err_ge1(struct mdoc
*);
53 static int head_warn_ge1(struct mdoc
*);
54 static int head_err_eq0(struct mdoc
*);
55 static int elem_err_eq0(struct mdoc
*);
56 static int elem_err_eq1(struct mdoc
*);
57 static int elem_err_ge1(struct mdoc
*);
58 static int elem_warn_eq0(struct mdoc
*);
59 static int body_warn_ge1(struct mdoc
*);
60 static int body_err_eq0(struct mdoc
*);
61 static int elem_warn_ge1(struct mdoc
*);
62 static int elem_bool(struct mdoc
*);
63 static int post_sh(struct mdoc
*);
64 static int post_bl(struct mdoc
*);
65 static int post_it(struct mdoc
*);
67 static v_pre pres_prologue
[] = { pre_prologue
, NULL
};
68 static v_pre pres_d1
[] = { pre_display
, NULL
};
69 static v_pre pres_bd
[] = { pre_display
, pre_bd
, NULL
};
70 static v_pre pres_bl
[] = { pre_bl
, NULL
};
71 static v_pre pres_it
[] = { pre_it
, NULL
};
72 static v_pre pres_ss
[] = { pre_ss
, NULL
};
73 static v_pre pres_sh
[] = { pre_sh
, NULL
};
74 static v_pre pres_cd
[] = { pre_cd
, NULL
};
75 static v_pre pres_er
[] = { pre_er
, NULL
};
76 static v_pre pres_ex
[] = { pre_ex
, NULL
};
78 static v_post posts_bool
[] = { elem_err_eq1
, elem_bool
, NULL
};
79 static v_post posts_bd
[] = { head_err_eq0
, body_warn_ge1
, NULL
};
80 static v_post posts_text
[] = { elem_err_ge1
, NULL
};
81 static v_post posts_wtext
[] = { elem_warn_ge1
, NULL
};
82 static v_post posts_notext
[] = { elem_err_eq0
, NULL
};
83 static v_post posts_wline
[] = { head_warn_ge1
, body_err_eq0
, NULL
};
84 static v_post posts_sh
[] = { head_err_ge1
,
85 body_warn_ge1
, post_sh
, NULL
};
86 static v_post posts_bl
[] = { head_err_eq0
,
87 body_warn_ge1
, post_bl
, NULL
};
88 static v_post posts_it
[] = { post_it
, NULL
};
89 static v_post posts_ss
[] = { head_err_ge1
, NULL
};
90 static v_post posts_pp
[] = { elem_warn_eq0
, NULL
};
91 static v_post posts_d1
[] = { head_err_ge1
, NULL
};
94 const struct valids mdoc_valids
[MDOC_MAX
] = {
95 { NULL
, NULL
}, /* \" */
96 { pres_prologue
, posts_text
}, /* Dd */
97 { pres_prologue
, NULL
}, /* Dt */
98 { pres_prologue
, NULL
}, /* Os */
99 /* FIXME: preceding Pp. */
100 /* FIXME: NAME section internal ordering. */
101 { pres_sh
, posts_sh
}, /* Sh */
102 /* FIXME: preceding Pp. */
103 { pres_ss
, posts_ss
}, /* Ss */
104 /* FIXME: proceeding... */
105 { NULL
, posts_pp
}, /* Pp */
106 { pres_d1
, posts_d1
}, /* D1 */
107 { pres_d1
, posts_d1
}, /* Dl */
108 /* FIXME: preceding Pp. */
109 { pres_bd
, posts_bd
}, /* Bd */
110 { NULL
, NULL
}, /* Ed */
111 /* FIXME: preceding Pp. */
112 { pres_bl
, posts_bl
}, /* Bl */
113 { NULL
, NULL
}, /* El */
114 { pres_it
, posts_it
}, /* It */
115 { NULL
, posts_text
}, /* Ad */
116 /* FIXME: argument OR parameters. */
117 { NULL
, NULL
}, /* An */
118 { NULL
, NULL
}, /* Ar */
119 { pres_cd
, posts_text
}, /* Cd */
120 { NULL
, NULL
}, /* Cm */
121 { NULL
, posts_text
}, /* Dv */
122 { pres_er
, posts_text
}, /* Er */
123 { NULL
, posts_text
}, /* Ev */
124 { pres_ex
, posts_notext
}, /* Ex */ /* FIXME: -std required */
125 { NULL
, posts_text
}, /* Fa */
126 { NULL
, NULL
}, /* Fd */ /* FIXME: SYNOPSIS section. */
127 { NULL
, NULL
}, /* Fl */
128 { NULL
, posts_text
}, /* Fn */
129 { NULL
, NULL
}, /* Ft */
130 { NULL
, posts_text
}, /* Ic */
131 { NULL
, posts_wtext
}, /* In */
132 { NULL
, posts_text
}, /* Li */
133 { NULL
, posts_wtext
}, /* Nd */
134 { NULL
, NULL
}, /* Nm */ /* FIXME: If name not set? */
135 { NULL
, posts_wline
}, /* Op */
136 { NULL
, NULL
}, /* Ot */
137 { NULL
, NULL
}, /* Pa */
138 { NULL
, posts_notext
}, /* Rv */ /* -std required */
139 { NULL
, posts_notext
}, /* St */ /* arg required */
140 { NULL
, posts_text
}, /* Va */
141 { NULL
, posts_text
}, /* Vt */
142 { NULL
, NULL
}, /* Xr */ /* FIXME */
143 { NULL
, posts_text
}, /* %A */
144 { NULL
, posts_text
}, /* %B */
145 { NULL
, posts_text
}, /* %D */
146 { NULL
, posts_text
}, /* %I */
147 { NULL
, posts_text
}, /* %J */
148 { NULL
, posts_text
}, /* %N */
149 { NULL
, posts_text
}, /* %O */
150 { NULL
, posts_text
}, /* %P */
151 { NULL
, posts_text
}, /* %R */
152 { NULL
, posts_text
}, /* %T */
153 { NULL
, posts_text
}, /* %V */
154 { NULL
, NULL
}, /* Ac */
155 { NULL
, NULL
}, /* Ao */
156 { NULL
, posts_wline
}, /* Aq */
157 { NULL
, NULL
}, /* At */ /* FIXME */
158 { NULL
, NULL
}, /* Bc */
159 { NULL
, NULL
}, /* Bf */
160 { NULL
, NULL
}, /* Bo */
161 { NULL
, posts_wline
}, /* Bq */
162 { NULL
, NULL
}, /* Bsx */
163 { NULL
, NULL
}, /* Bx */
164 { NULL
, posts_bool
}, /* Db */
165 { NULL
, NULL
}, /* Dc */
166 { NULL
, NULL
}, /* Do */
167 { NULL
, posts_wline
}, /* Dq */
168 { NULL
, NULL
}, /* Ec */
169 { NULL
, NULL
}, /* Ef */ /* -symbolic, etc. */
170 { NULL
, posts_text
}, /* Em */
171 { NULL
, NULL
}, /* Eo */
172 { NULL
, NULL
}, /* Fx */
173 { NULL
, posts_text
}, /* Ms */ /* FIXME: which symbols? */
174 { NULL
, posts_notext
}, /* No */
175 { NULL
, posts_notext
}, /* Ns */
176 { NULL
, NULL
}, /* Nx */
177 { NULL
, NULL
}, /* Ox */
178 { NULL
, NULL
}, /* Pc */
179 { NULL
, NULL
}, /* Pf */ /* FIXME: 2 or more arguments */ /* First should be text. */
180 { NULL
, NULL
}, /* Po */
181 { NULL
, posts_wline
}, /* Pq */ /* FIXME: ignore following Sh/Ss */
182 { NULL
, NULL
}, /* Qc */
183 { NULL
, posts_wline
}, /* Ql */
184 { NULL
, NULL
}, /* Qo */
185 { NULL
, posts_wline
}, /* Qq */
186 { NULL
, NULL
}, /* Re */
187 { NULL
, NULL
}, /* Rs */
188 { NULL
, NULL
}, /* Sc */
189 { NULL
, NULL
}, /* So */
190 { NULL
, posts_wline
}, /* Sq */
191 { NULL
, posts_bool
}, /* Sm */
192 { NULL
, posts_text
}, /* Sx */
193 { NULL
, posts_text
}, /* Sy */
194 { NULL
, posts_text
}, /* Tn */
195 { NULL
, NULL
}, /* Ux */
196 { NULL
, NULL
}, /* Xc */
197 { NULL
, NULL
}, /* Xo */
198 { NULL
, NULL
}, /* Fo */
199 { NULL
, NULL
}, /* Fc */
200 { NULL
, NULL
}, /* Oo */
201 { NULL
, NULL
}, /* Oc */
202 { NULL
, NULL
}, /* Bk */
203 { NULL
, NULL
}, /* Ek */
204 { NULL
, posts_notext
}, /* Bt */
205 { NULL
, NULL
}, /* Hf */
206 { NULL
, NULL
}, /* Fr */
207 { NULL
, posts_notext
}, /* Ud */
212 pre_check_msecs(struct mdoc
*mdoc
, struct mdoc_node
*node
,
213 int sz
, enum mdoc_msec
*msecs
)
217 for (i
= 0; i
< sz
; i
++)
218 if (msecs
[i
] == mdoc
->meta
.msec
)
220 return(mdoc_nwarn(mdoc
, node
, WARN_COMPAT
,
221 "macro is not appropriate for this manual section"));
226 pre_check_parent(struct mdoc
*mdoc
, struct mdoc_node
*node
,
227 int tok
, enum mdoc_type type
)
230 if (type
!= mdoc
->last
->parent
->type
)
231 return(mdoc_nerr(mdoc
, node
, "invalid macro parent class %s, expected %s",
232 mdoc_type2a(mdoc
->last
->parent
->type
),
234 if (MDOC_ROOT
!= type
&& tok
== mdoc
->last
->parent
->tok
)
235 return(mdoc_nerr(mdoc
, node
, "invalid macro parent `%s', expected `%s'",
236 mdoc_macronames
[mdoc
->last
->parent
->tok
],
237 mdoc_macronames
[tok
]));
243 body_err_eq0(struct mdoc
*mdoc
)
246 if (MDOC_BODY
!= mdoc
->last
->type
)
248 if (NULL
== mdoc
->last
->child
)
250 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests no body children"));
255 body_warn_ge1(struct mdoc
*mdoc
)
258 if (MDOC_BODY
!= mdoc
->last
->type
)
260 if (mdoc
->last
->child
)
262 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests one or more body children"));
267 elem_warn_eq0(struct mdoc
*mdoc
)
270 assert(MDOC_ELEM
== mdoc
->last
->type
);
271 if (NULL
== mdoc
->last
->child
)
273 return(mdoc_pwarn(mdoc
, mdoc
->last
->child
->line
,
274 mdoc
->last
->child
->pos
, WARN_SYNTAX
, "macro suggests no parameters"));
279 elem_warn_ge1(struct mdoc
*mdoc
)
282 assert(MDOC_ELEM
== mdoc
->last
->type
);
283 if (mdoc
->last
->child
)
285 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests one or more parameters"));
290 elem_err_eq1(struct mdoc
*mdoc
)
293 assert(MDOC_ELEM
== mdoc
->last
->type
);
294 if (NULL
== mdoc
->last
->child
)
295 return(mdoc_err(mdoc
, "macro expects one parameter"));
296 if (mdoc
->last
->child
->next
)
297 return(mdoc_err(mdoc
, "macro expects one parameter"));
303 elem_err_eq0(struct mdoc
*mdoc
)
306 assert(MDOC_ELEM
== mdoc
->last
->type
);
307 if (NULL
== mdoc
->last
->child
)
309 return(mdoc_err(mdoc
, "macro expects no parameters"));
314 elem_err_ge1(struct mdoc
*mdoc
)
317 assert(MDOC_ELEM
== mdoc
->last
->type
);
318 if (mdoc
->last
->child
)
320 return(mdoc_err(mdoc
, "macro expects one or more parameters"));
325 head_err_eq0(struct mdoc
*mdoc
)
328 if (MDOC_HEAD
!= mdoc
->last
->type
)
330 if (NULL
== mdoc
->last
->child
)
332 return(mdoc_perr(mdoc
, mdoc
->last
->child
->line
,
333 mdoc
->last
->child
->pos
, "macro expects no parameters"));
338 head_warn_ge1(struct mdoc
*mdoc
)
341 if (MDOC_HEAD
!= mdoc
->last
->type
)
343 if (mdoc
->last
->child
)
345 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests one or more parameters"));
350 head_err_ge1(struct mdoc
*mdoc
)
353 if (MDOC_HEAD
!= mdoc
->last
->type
)
355 if (mdoc
->last
->child
)
357 return(mdoc_err(mdoc
, "macro expects one or more parameters"));
362 pre_display(struct mdoc
*mdoc
, struct mdoc_node
*node
)
366 if (MDOC_BLOCK
!= node
->type
)
370 for (n
= mdoc
->last
->parent
; n
; n
= n
->parent
)
371 if (MDOC_BLOCK
== n
->type
)
372 if (MDOC_Bd
== n
->tok
)
376 return(mdoc_nerr(mdoc
, node
, "displays may not be nested"));
381 pre_bl(struct mdoc
*mdoc
, struct mdoc_node
*node
)
384 struct mdoc_arg
*argv
;
387 if (MDOC_BLOCK
!= node
->type
)
389 assert(MDOC_Bl
== node
->tok
);
392 argc
= node
->data
.block
.argc
;
394 for (i
= type
= err
= 0; i
< argc
; i
++) {
395 argv
= &node
->data
.block
.argv
[(int)i
];
428 return(mdoc_err(mdoc
, "no list type specified"));
432 return(mdoc_perr(mdoc
, argv
->line
,
433 argv
->pos
, "only one list type possible"));
438 pre_bd(struct mdoc
*mdoc
, struct mdoc_node
*node
)
441 struct mdoc_arg
*argv
;
444 if (MDOC_BLOCK
!= node
->type
)
446 assert(MDOC_Bd
== node
->tok
);
449 argc
= node
->data
.block
.argc
;
451 for (err
= i
= type
= 0; 0 == err
&& i
< argc
; i
++) {
452 argv
= &node
->data
.block
.argv
[(int)i
];
457 case (MDOC_Unfilled
):
473 return(mdoc_err(mdoc
, "no display type specified"));
477 return(mdoc_perr(mdoc
, argv
->line
,
478 argv
->pos
, "only one display type possible"));
483 pre_ss(struct mdoc
*mdoc
, struct mdoc_node
*node
)
486 if (MDOC_BLOCK
!= mdoc
->last
->type
)
488 assert(MDOC_Sh
== mdoc
->last
->tok
);
489 return(pre_check_parent(mdoc
, node
, MDOC_Sh
, MDOC_BODY
));
494 pre_sh(struct mdoc
*mdoc
, struct mdoc_node
*node
)
497 if (MDOC_BLOCK
!= mdoc
->last
->type
)
499 assert(MDOC_Sh
== mdoc
->last
->tok
);
500 return(pre_check_parent(mdoc
, node
, -1, MDOC_ROOT
));
505 pre_ex(struct mdoc
*mdoc
, struct mdoc_node
*node
)
507 enum mdoc_msec msecs
[3];
512 return(pre_check_msecs(mdoc
, node
, 3, msecs
));
517 pre_er(struct mdoc
*mdoc
, struct mdoc_node
*node
)
519 enum mdoc_msec msecs
[1];
522 return(pre_check_msecs(mdoc
, node
, 1, msecs
));
527 pre_cd(struct mdoc
*mdoc
, struct mdoc_node
*node
)
529 enum mdoc_msec msecs
[1];
532 return(pre_check_msecs(mdoc
, node
, 1, msecs
));
537 pre_it(struct mdoc
*mdoc
, struct mdoc_node
*node
)
540 if (MDOC_BLOCK
!= mdoc
->last
->type
)
542 assert(MDOC_It
== mdoc
->last
->tok
);
543 return(pre_check_parent(mdoc
, node
, MDOC_Bl
, MDOC_BODY
));
548 pre_prologue(struct mdoc
*mdoc
, struct mdoc_node
*node
)
551 if (SEC_PROLOGUE
!= mdoc
->sec_lastn
)
552 return(mdoc_nerr(mdoc
, node
, "macro may only be invoked in the prologue"));
553 assert(MDOC_ELEM
== node
->type
);
555 /* Check for ordering. */
559 if (mdoc
->meta
.title
[0] && mdoc
->meta
.date
)
561 return(mdoc_nerr(mdoc
, node
, "prologue macro out-of-order"));
563 if (0 == mdoc
->meta
.title
[0] && mdoc
->meta
.date
)
565 return(mdoc_nerr(mdoc
, node
, "prologue macro out-of-order"));
567 if (0 == mdoc
->meta
.title
[0] && 0 == mdoc
->meta
.date
)
569 return(mdoc_nerr(mdoc
, node
, "prologue macro out-of-order"));
575 /* Check for repetition. */
579 if (0 == mdoc
->meta
.os
[0])
583 if (0 == mdoc
->meta
.date
)
587 if (0 == mdoc
->meta
.title
[0])
595 return(mdoc_nerr(mdoc
, node
, "prologue macro repeated"));
599 /* Warn if `Bl' type-specific syntax isn't reflected in items. */
601 post_it(struct mdoc
*mdoc
)
604 #define TYPE_NONE (0)
605 #define TYPE_BODY (1)
606 #define TYPE_HEAD (2)
610 if (MDOC_BLOCK
!= mdoc
->last
->type
)
613 assert(MDOC_It
== mdoc
->last
->tok
);
615 n
= mdoc
->last
->parent
;
617 assert(MDOC_Bl
== n
->tok
);
620 assert(MDOC_BLOCK
== n
->type
);
621 assert(MDOC_Bl
== n
->tok
);
623 argc
= n
->data
.block
.argc
;
626 /* Some types require block-head, some not. */
628 for (i
= 0; TYPE_NONE
== type
&& i
< argc
; i
++)
629 switch (n
->data
.block
.argv
[(int)i
].arg
) {
640 sv
= n
->data
.block
.argv
[(int)i
].arg
;
654 sv
= n
->data
.block
.argv
[(int)i
].arg
;
660 assert(TYPE_NONE
!= type
);
662 if (TYPE_HEAD
== type
) {
663 n
= mdoc
->last
->data
.block
.head
;
665 if (NULL
== n
->child
)
666 if ( ! mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests line parameters"))
669 n
= mdoc
->last
->data
.block
.body
;
671 if (NULL
== n
->child
)
672 if ( ! mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests body children"))
678 assert(TYPE_BODY
== type
);
679 assert(mdoc
->last
->data
.block
.head
);
681 n
= mdoc
->last
->data
.block
.head
;
684 if ( ! mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests no line parameters"))
687 n
= mdoc
->last
->data
.block
.body
;
689 if (NULL
== n
->child
)
690 if ( ! mdoc_warn(mdoc
, WARN_SYNTAX
, "macro suggests body children"))
693 if (MDOC_Column
!= sv
)
696 /* Make sure the number of columns is sane. */
698 sv
= mdoc
->last
->parent
->parent
->data
.block
.argv
->sz
;
699 n
= mdoc
->last
->data
.block
.head
->child
;
701 for (i
= 0; n
; n
= n
->next
)
706 return(mdoc_err(mdoc
, "expected %d list columns, have %d", sv
, (int)i
));
714 /* Make sure that only `It' macros are our body-children. */
716 post_bl(struct mdoc
*mdoc
)
720 if (MDOC_BODY
!= mdoc
->last
->type
)
722 assert(MDOC_Bl
== mdoc
->last
->tok
);
724 for (n
= mdoc
->last
->child
; n
; n
= n
->next
) {
725 if (MDOC_BLOCK
== n
->type
)
726 if (MDOC_It
== n
->tok
)
732 return(mdoc_nerr(mdoc
, n
, "invalid child of parent macro `Bl'"));
737 elem_bool(struct mdoc
*mdoc
)
741 assert(MDOC_ELEM
== mdoc
->last
->type
);
742 for (n
= mdoc
->last
->child
; n
; n
= n
->next
) {
743 if (MDOC_TEXT
!= n
->type
)
745 if (xstrcmp(n
->data
.text
.string
, "on"))
747 if (xstrcmp(n
->data
.text
.string
, "off"))
753 return(mdoc_nerr(mdoc
, n
, "expected boolean value [on/off]"));
757 /* Warn if conventional sections are out of order. */
759 post_sh(struct mdoc
*mdoc
)
764 char *args
[MDOC_LINEARG_MAX
];
766 if (MDOC_HEAD
!= mdoc
->last
->type
)
769 assert(MDOC_Sh
== mdoc
->last
->tok
);
771 n
= mdoc
->last
->child
;
774 for (i
= 0; n
&& i
< MDOC_LINEARG_MAX
; n
= n
->next
, i
++) {
775 assert(MDOC_TEXT
== n
->type
);
776 assert(NULL
== n
->child
);
777 assert(n
->data
.text
.string
);
778 args
[i
] = n
->data
.text
.string
;
781 sec
= mdoc_atosec((size_t)i
, (const char **)args
);
782 if (SEC_CUSTOM
== sec
)
784 if (sec
> mdoc
->sec_lastn
)
787 if (sec
== mdoc
->sec_lastn
)
788 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "section repeated"));
789 return(mdoc_warn(mdoc
, WARN_SYNTAX
, "section out of conventional order"));
794 mdoc_valid_pre(struct mdoc
*mdoc
, struct mdoc_node
*node
)
798 /* TODO: character-escape checks. */
800 if (MDOC_TEXT
== node
->type
)
802 assert(MDOC_ROOT
!= node
->type
);
804 if (NULL
== mdoc_valids
[node
->tok
].pre
)
806 for (p
= mdoc_valids
[node
->tok
].pre
; *p
; p
++)
807 if ( ! (*p
)(mdoc
, node
))
814 mdoc_valid_post(struct mdoc
*mdoc
)
818 if (MDOC_TEXT
== mdoc
->last
->type
)
820 if (MDOC_ROOT
== mdoc
->last
->type
) {
821 /* TODO: make sure prologue is complete. */
825 if (NULL
== mdoc_valids
[mdoc
->last
->tok
].post
)
827 for (p
= mdoc_valids
[mdoc
->last
->tok
].post
; *p
; p
++)