xref: /openbsd-src/usr.bin/mandoc/mdoc_validate.c (revision cd1eb269cafb12c415be1749cd4a4b5422710415)
1 /*	$Id: mdoc_validate.c,v 1.48 2010/04/07 23:15:05 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 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 above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/types.h>
18 
19 #include <assert.h>
20 #include <ctype.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "libmdoc.h"
27 #include "libmandoc.h"
28 
29 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
30 /* TODO: ignoring Pp (it's superfluous in some invocations). */
31 
32 #define	PRE_ARGS  struct mdoc *mdoc, const struct mdoc_node *n
33 #define	POST_ARGS struct mdoc *mdoc
34 
35 typedef	int	(*v_pre)(PRE_ARGS);
36 typedef	int	(*v_post)(POST_ARGS);
37 
38 struct	valids {
39 	v_pre	*pre;
40 	v_post	*post;
41 };
42 
43 static	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
44 static	int	 check_msec(PRE_ARGS, ...);
45 static	int	 check_sec(PRE_ARGS, ...);
46 static	int	 check_stdarg(PRE_ARGS);
47 static	int	 check_text(struct mdoc *, int, int, const char *);
48 static	int	 check_argv(struct mdoc *,
49 			const struct mdoc_node *,
50 			const struct mdoc_argv *);
51 static	int	 check_args(struct mdoc *,
52 			const struct mdoc_node *);
53 static	int	 err_child_lt(struct mdoc *, const char *, int);
54 static	int	 warn_child_lt(struct mdoc *, const char *, int);
55 static	int	 err_child_gt(struct mdoc *, const char *, int);
56 static	int	 warn_child_gt(struct mdoc *, const char *, int);
57 static	int	 err_child_eq(struct mdoc *, const char *, int);
58 static	int	 warn_child_eq(struct mdoc *, const char *, int);
59 static	int	 warn_print(struct mdoc *, int, int);
60 static	int	 warn_count(struct mdoc *, const char *,
61 			int, const char *, int);
62 static	int	 err_count(struct mdoc *, const char *,
63 			int, const char *, int);
64 
65 static	int	 berr_ge1(POST_ARGS);
66 static	int	 bwarn_ge1(POST_ARGS);
67 static	int	 ebool(POST_ARGS);
68 static	int	 eerr_eq0(POST_ARGS);
69 static	int	 eerr_eq1(POST_ARGS);
70 static	int	 eerr_ge1(POST_ARGS);
71 static	int	 eerr_le1(POST_ARGS);
72 static	int	 ewarn_ge1(POST_ARGS);
73 static	int	 herr_eq0(POST_ARGS);
74 static	int	 herr_ge1(POST_ARGS);
75 static	int	 hwarn_eq1(POST_ARGS);
76 static	int	 hwarn_eq0(POST_ARGS);
77 static	int	 hwarn_le1(POST_ARGS);
78 
79 static	int	 post_an(POST_ARGS);
80 static	int	 post_at(POST_ARGS);
81 static	int	 post_bf(POST_ARGS);
82 static	int	 post_bl(POST_ARGS);
83 static	int	 post_bl_head(POST_ARGS);
84 static	int	 post_it(POST_ARGS);
85 static	int	 post_lb(POST_ARGS);
86 static	int	 post_nm(POST_ARGS);
87 static	int	 post_root(POST_ARGS);
88 static	int	 post_rs(POST_ARGS);
89 static	int	 post_sh(POST_ARGS);
90 static	int	 post_sh_body(POST_ARGS);
91 static	int	 post_sh_head(POST_ARGS);
92 static	int	 post_st(POST_ARGS);
93 static	int	 post_vt(POST_ARGS);
94 static	int	 pre_an(PRE_ARGS);
95 static	int	 pre_bd(PRE_ARGS);
96 static	int	 pre_bl(PRE_ARGS);
97 static	int	 pre_cd(PRE_ARGS);
98 static	int	 pre_dd(PRE_ARGS);
99 static	int	 pre_display(PRE_ARGS);
100 static	int	 pre_dt(PRE_ARGS);
101 static	int	 pre_ex(PRE_ARGS);
102 static	int	 pre_fd(PRE_ARGS);
103 static	int	 pre_it(PRE_ARGS);
104 static	int	 pre_lb(PRE_ARGS);
105 static	int	 pre_os(PRE_ARGS);
106 static	int	 pre_rv(PRE_ARGS);
107 static	int	 pre_sh(PRE_ARGS);
108 static	int	 pre_ss(PRE_ARGS);
109 
110 static	v_post	 posts_an[] = { post_an, NULL };
111 static	v_post	 posts_at[] = { post_at, NULL };
112 static	v_post	 posts_bd[] = { hwarn_eq0, bwarn_ge1, NULL };
113 static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
114 static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
115 static	v_post	 posts_bool[] = { eerr_eq1, ebool, NULL };
116 static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
117 static	v_post	 posts_it[] = { post_it, NULL };
118 static	v_post	 posts_lb[] = { eerr_eq1, post_lb, NULL };
119 static	v_post	 posts_nd[] = { berr_ge1, NULL };
120 static	v_post	 posts_nm[] = { post_nm, NULL };
121 static	v_post	 posts_notext[] = { eerr_eq0, NULL };
122 static	v_post	 posts_rs[] = { berr_ge1, herr_eq0, post_rs, NULL };
123 static	v_post	 posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL };
124 static	v_post	 posts_sp[] = { eerr_le1, NULL };
125 static	v_post	 posts_ss[] = { herr_ge1, NULL };
126 static	v_post	 posts_st[] = { eerr_eq1, post_st, NULL };
127 static	v_post	 posts_text[] = { eerr_ge1, NULL };
128 static	v_post	 posts_text1[] = { eerr_eq1, NULL };
129 static	v_post	 posts_vt[] = { post_vt, NULL };
130 static	v_post	 posts_wline[] = { bwarn_ge1, herr_eq0, NULL };
131 static	v_post	 posts_wtext[] = { ewarn_ge1, NULL };
132 static	v_post	 posts_xr[] = { ewarn_ge1, NULL };
133 static	v_pre	 pres_an[] = { pre_an, NULL };
134 static	v_pre	 pres_bd[] = { pre_display, pre_bd, NULL };
135 static	v_pre	 pres_bl[] = { pre_bl, NULL };
136 static	v_pre	 pres_cd[] = { pre_cd, NULL };
137 static	v_pre	 pres_d1[] = { pre_display, NULL };
138 static	v_pre	 pres_dd[] = { pre_dd, NULL };
139 static	v_pre	 pres_dt[] = { pre_dt, NULL };
140 static	v_pre	 pres_er[] = { NULL, NULL };
141 static	v_pre	 pres_ex[] = { pre_ex, NULL };
142 static	v_pre	 pres_fd[] = { pre_fd, NULL };
143 static	v_pre	 pres_it[] = { pre_it, NULL };
144 static	v_pre	 pres_lb[] = { pre_lb, NULL };
145 static	v_pre	 pres_os[] = { pre_os, NULL };
146 static	v_pre	 pres_rv[] = { pre_rv, NULL };
147 static	v_pre	 pres_sh[] = { pre_sh, NULL };
148 static	v_pre	 pres_ss[] = { pre_ss, NULL };
149 
150 const	struct valids mdoc_valids[MDOC_MAX] = {
151 	{ NULL, NULL },				/* Ap */
152 	{ pres_dd, posts_text },		/* Dd */
153 	{ pres_dt, NULL },			/* Dt */
154 	{ pres_os, NULL },			/* Os */
155 	{ pres_sh, posts_sh },			/* Sh */
156 	{ pres_ss, posts_ss },			/* Ss */
157 	{ NULL, posts_notext },			/* Pp */
158 	{ pres_d1, posts_wline },		/* D1 */
159 	{ pres_d1, posts_wline },		/* Dl */
160 	{ pres_bd, posts_bd },			/* Bd */
161 	{ NULL, NULL },				/* Ed */
162 	{ pres_bl, posts_bl },			/* Bl */
163 	{ NULL, NULL },				/* El */
164 	{ pres_it, posts_it },			/* It */
165 	{ NULL, posts_text },			/* Ad */
166 	{ pres_an, posts_an },			/* An */
167 	{ NULL, NULL },				/* Ar */
168 	{ pres_cd, posts_text },		/* Cd */
169 	{ NULL, NULL },				/* Cm */
170 	{ NULL, NULL },				/* Dv */
171 	{ pres_er, posts_text },		/* Er */
172 	{ NULL, NULL },				/* Ev */
173 	{ pres_ex, NULL },			/* Ex */
174 	{ NULL, NULL },				/* Fa */
175 	{ pres_fd, posts_wtext },		/* Fd */
176 	{ NULL, NULL },				/* Fl */
177 	{ NULL, posts_text },			/* Fn */
178 	{ NULL, posts_wtext },			/* Ft */
179 	{ NULL, posts_text },			/* Ic */
180 	{ NULL, posts_text1 },			/* In */
181 	{ NULL, NULL },				/* Li */
182 	{ NULL, posts_nd },			/* Nd */
183 	{ NULL, posts_nm },			/* Nm */
184 	{ NULL, posts_wline },			/* Op */
185 	{ NULL, NULL },				/* Ot */
186 	{ NULL, NULL },				/* Pa */
187 	{ pres_rv, NULL },			/* Rv */
188 	{ NULL, posts_st },			/* St */
189 	{ NULL, NULL },				/* Va */
190 	{ NULL, posts_vt },			/* Vt */
191 	{ NULL, posts_xr },			/* Xr */
192 	{ NULL, posts_text },			/* %A */
193 	{ NULL, posts_text },			/* %B */ /* FIXME: can be used outside Rs/Re. */
194 	{ NULL, posts_text },			/* %D */ /* FIXME: check date with mandoc_a2time(). */
195 	{ NULL, posts_text },			/* %I */
196 	{ NULL, posts_text },			/* %J */
197 	{ NULL, posts_text },			/* %N */
198 	{ NULL, posts_text },			/* %O */
199 	{ NULL, posts_text },			/* %P */
200 	{ NULL, posts_text },			/* %R */
201 	{ NULL, posts_text },			/* %T */ /* FIXME: can be used outside Rs/Re. */
202 	{ NULL, posts_text },			/* %V */
203 	{ NULL, NULL },				/* Ac */
204 	{ NULL, NULL },				/* Ao */
205 	{ NULL, posts_wline },			/* Aq */
206 	{ NULL, posts_at },			/* At */
207 	{ NULL, NULL },				/* Bc */
208 	{ NULL, posts_bf },			/* Bf */
209 	{ NULL, NULL },				/* Bo */
210 	{ NULL, posts_wline },			/* Bq */
211 	{ NULL, NULL },				/* Bsx */
212 	{ NULL, NULL },				/* Bx */
213 	{ NULL, posts_bool },			/* Db */
214 	{ NULL, NULL },				/* Dc */
215 	{ NULL, NULL },				/* Do */
216 	{ NULL, posts_wline },			/* Dq */
217 	{ NULL, NULL },				/* Ec */
218 	{ NULL, NULL },				/* Ef */
219 	{ NULL, NULL },				/* Em */
220 	{ NULL, NULL },				/* Eo */
221 	{ NULL, NULL },				/* Fx */
222 	{ NULL, posts_text },			/* Ms */
223 	{ NULL, posts_notext },			/* No */
224 	{ NULL, posts_notext },			/* Ns */
225 	{ NULL, NULL },				/* Nx */
226 	{ NULL, NULL },				/* Ox */
227 	{ NULL, NULL },				/* Pc */
228 	{ NULL, posts_text1 },			/* Pf */
229 	{ NULL, NULL },				/* Po */
230 	{ NULL, posts_wline },			/* Pq */
231 	{ NULL, NULL },				/* Qc */
232 	{ NULL, posts_wline },			/* Ql */
233 	{ NULL, NULL },				/* Qo */
234 	{ NULL, posts_wline },			/* Qq */
235 	{ NULL, NULL },				/* Re */
236 	{ NULL, posts_rs },			/* Rs */
237 	{ NULL, NULL },				/* Sc */
238 	{ NULL, NULL },				/* So */
239 	{ NULL, posts_wline },			/* Sq */
240 	{ NULL, posts_bool },			/* Sm */
241 	{ NULL, posts_text },			/* Sx */
242 	{ NULL, posts_text },			/* Sy */
243 	{ NULL, posts_text },			/* Tn */
244 	{ NULL, NULL },				/* Ux */
245 	{ NULL, NULL },				/* Xc */
246 	{ NULL, NULL },				/* Xo */
247 	{ NULL, posts_fo },			/* Fo */
248 	{ NULL, NULL },				/* Fc */
249 	{ NULL, NULL },				/* Oo */
250 	{ NULL, NULL },				/* Oc */
251 	{ NULL, posts_wline },			/* Bk */
252 	{ NULL, NULL },				/* Ek */
253 	{ NULL, posts_notext },			/* Bt */
254 	{ NULL, NULL },				/* Hf */
255 	{ NULL, NULL },				/* Fr */
256 	{ NULL, posts_notext },			/* Ud */
257 	{ pres_lb, posts_lb },			/* Lb */
258 	{ NULL, posts_notext },			/* Lp */
259 	{ NULL, posts_text },			/* Lk */
260 	{ NULL, posts_text },			/* Mt */
261 	{ NULL, posts_wline },			/* Brq */
262 	{ NULL, NULL },				/* Bro */
263 	{ NULL, NULL },				/* Brc */
264 	{ NULL, posts_text },			/* %C */
265 	{ NULL, NULL },				/* Es */
266 	{ NULL, NULL },				/* En */
267 	{ NULL, NULL },				/* Dx */
268 	{ NULL, posts_text },			/* %Q */
269 	{ NULL, posts_notext },			/* br */
270 	{ NULL, posts_sp },			/* sp */
271 	{ NULL, posts_text1 },			/* %U */
272 	{ NULL, NULL },				/* eos */
273 };
274 
275 
276 int
277 mdoc_valid_pre(struct mdoc *mdoc, const struct mdoc_node *n)
278 {
279 	v_pre		*p;
280 	int		 line, pos;
281 	const char	*tp;
282 
283 	if (MDOC_TEXT == n->type) {
284 		tp = n->string;
285 		line = n->line;
286 		pos = n->pos;
287 		return(check_text(mdoc, line, pos, tp));
288 	}
289 
290 	if ( ! check_args(mdoc, n))
291 		return(0);
292 	if (NULL == mdoc_valids[n->tok].pre)
293 		return(1);
294 	for (p = mdoc_valids[n->tok].pre; *p; p++)
295 		if ( ! (*p)(mdoc, n))
296 			return(0);
297 	return(1);
298 }
299 
300 
301 int
302 mdoc_valid_post(struct mdoc *mdoc)
303 {
304 	v_post		*p;
305 
306 	if (MDOC_VALID & mdoc->last->flags)
307 		return(1);
308 	mdoc->last->flags |= MDOC_VALID;
309 
310 	if (MDOC_TEXT == mdoc->last->type)
311 		return(1);
312 	if (MDOC_ROOT == mdoc->last->type)
313 		return(post_root(mdoc));
314 
315 	if (NULL == mdoc_valids[mdoc->last->tok].post)
316 		return(1);
317 	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
318 		if ( ! (*p)(mdoc))
319 			return(0);
320 
321 	return(1);
322 }
323 
324 
325 static int
326 warn_print(struct mdoc *m, int ln, int pos)
327 {
328 
329 	if (MDOC_IGN_CHARS & m->pflags)
330 		return(mdoc_pwarn(m, ln, pos, EPRINT));
331 	return(mdoc_perr(m, ln, pos, EPRINT));
332 }
333 
334 
335 static inline int
336 warn_count(struct mdoc *m, const char *k,
337 		int want, const char *v, int has)
338 {
339 
340 	return(mdoc_vwarn(m, m->last->line, m->last->pos,
341 		"suggests %s %s %d (has %d)", v, k, want, has));
342 }
343 
344 
345 static inline int
346 err_count(struct mdoc *m, const char *k,
347 		int want, const char *v, int has)
348 {
349 
350 	return(mdoc_verr(m, m->last->line, m->last->pos,
351 		"requires %s %s %d (has %d)", v, k, want, has));
352 }
353 
354 
355 /*
356  * Build these up with macros because they're basically the same check
357  * for different inequalities.  Yes, this could be done with functions,
358  * but this is reasonable for now.
359  */
360 
361 #define CHECK_CHILD_DEFN(lvl, name, ineq) 			\
362 static int 							\
363 lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz) 	\
364 { 								\
365 	if (mdoc->last->nchild ineq sz)				\
366 		return(1); 					\
367 	return(lvl##_count(mdoc, #ineq, sz, p, mdoc->last->nchild)); \
368 }
369 
370 #define CHECK_BODY_DEFN(name, lvl, func, num) 			\
371 static int 							\
372 b##lvl##_##name(POST_ARGS) 					\
373 { 								\
374 	if (MDOC_BODY != mdoc->last->type) 			\
375 		return(1); 					\
376 	return(func(mdoc, "multi-line arguments", (num))); 	\
377 }
378 
379 #define CHECK_ELEM_DEFN(name, lvl, func, num) 			\
380 static int							\
381 e##lvl##_##name(POST_ARGS) 					\
382 { 								\
383 	assert(MDOC_ELEM == mdoc->last->type); 			\
384 	return(func(mdoc, "line arguments", (num))); 		\
385 }
386 
387 #define CHECK_HEAD_DEFN(name, lvl, func, num)			\
388 static int 							\
389 h##lvl##_##name(POST_ARGS) 					\
390 { 								\
391 	if (MDOC_HEAD != mdoc->last->type) 			\
392 		return(1); 					\
393 	return(func(mdoc, "line arguments", (num)));	 	\
394 }
395 
396 
397 CHECK_CHILD_DEFN(warn, gt, >)			/* warn_child_gt() */
398 CHECK_CHILD_DEFN(err, gt, >)			/* err_child_gt() */
399 CHECK_CHILD_DEFN(warn, eq, ==)			/* warn_child_eq() */
400 CHECK_CHILD_DEFN(err, eq, ==)			/* err_child_eq() */
401 CHECK_CHILD_DEFN(err, lt, <)			/* err_child_lt() */
402 CHECK_CHILD_DEFN(warn, lt, <)			/* warn_child_lt() */
403 CHECK_BODY_DEFN(ge1, warn, warn_child_gt, 0)	/* bwarn_ge1() */
404 CHECK_BODY_DEFN(ge1, err, err_child_gt, 0)	/* berr_ge1() */
405 CHECK_ELEM_DEFN(ge1, warn, warn_child_gt, 0)	/* ewarn_gt1() */
406 CHECK_ELEM_DEFN(eq1, err, err_child_eq, 1)	/* eerr_eq1() */
407 CHECK_ELEM_DEFN(le1, err, err_child_lt, 2)	/* eerr_le1() */
408 CHECK_ELEM_DEFN(eq0, err, err_child_eq, 0)	/* eerr_eq0() */
409 CHECK_ELEM_DEFN(ge1, err, err_child_gt, 0)	/* eerr_ge1() */
410 CHECK_HEAD_DEFN(eq0, err, err_child_eq, 0)	/* herr_eq0() */
411 CHECK_HEAD_DEFN(le1, warn, warn_child_lt, 2)	/* hwarn_le1() */
412 CHECK_HEAD_DEFN(ge1, err, err_child_gt, 0)	/* herr_ge1() */
413 CHECK_HEAD_DEFN(eq1, warn, warn_child_eq, 1)	/* hwarn_eq1() */
414 CHECK_HEAD_DEFN(eq0, warn, warn_child_eq, 0)	/* hwarn_eq0() */
415 
416 
417 static int
418 check_stdarg(PRE_ARGS)
419 {
420 
421 	if (n->args && 1 == n->args->argc)
422 		if (MDOC_Std == n->args->argv[0].arg)
423 			return(1);
424 	return(mdoc_nwarn(mdoc, n, EARGVAL));
425 }
426 
427 
428 static int
429 check_sec(PRE_ARGS, ...)
430 {
431 	enum mdoc_sec	 sec;
432 	va_list		 ap;
433 
434 	va_start(ap, n);
435 
436 	for (;;) {
437 		/* LINTED */
438 		sec = (enum mdoc_sec)va_arg(ap, int);
439 		if (SEC_CUSTOM == sec)
440 			break;
441 		if (sec != mdoc->lastsec)
442 			continue;
443 		va_end(ap);
444 		return(1);
445 	}
446 
447 	va_end(ap);
448 	return(mdoc_nwarn(mdoc, n, EBADSEC));
449 }
450 
451 
452 static int
453 check_msec(PRE_ARGS, ...)
454 {
455 	va_list		 ap;
456 	int		 msec;
457 
458 	va_start(ap, n);
459 	for (;;) {
460 		/* LINTED */
461 		if (0 == (msec = va_arg(ap, int)))
462 			break;
463 		if (msec != mdoc->meta.msec)
464 			continue;
465 		va_end(ap);
466 		return(1);
467 	}
468 
469 	va_end(ap);
470 	return(mdoc_nwarn(mdoc, n, EBADMSEC));
471 }
472 
473 
474 static int
475 check_args(struct mdoc *m, const struct mdoc_node *n)
476 {
477 	int		 i;
478 
479 	if (NULL == n->args)
480 		return(1);
481 
482 	assert(n->args->argc);
483 	for (i = 0; i < (int)n->args->argc; i++)
484 		if ( ! check_argv(m, n, &n->args->argv[i]))
485 			return(0);
486 
487 	return(1);
488 }
489 
490 
491 static int
492 check_argv(struct mdoc *m, const struct mdoc_node *n,
493 		const struct mdoc_argv *v)
494 {
495 	int		 i;
496 
497 	for (i = 0; i < (int)v->sz; i++)
498 		if ( ! check_text(m, v->line, v->pos, v->value[i]))
499 			return(0);
500 
501 	if (MDOC_Std == v->arg) {
502 		/* `Nm' name must be set. */
503 		if (v->sz || m->meta.name)
504 			return(1);
505 		return(mdoc_nerr(m, n, ENAME));
506 	}
507 
508 	return(1);
509 }
510 
511 
512 static int
513 check_text(struct mdoc *mdoc, int line, int pos, const char *p)
514 {
515 	int		 c;
516 
517 	for ( ; *p; p++, pos++) {
518 		if ('\t' == *p) {
519 			if ( ! (MDOC_LITERAL & mdoc->flags))
520 				if ( ! warn_print(mdoc, line, pos))
521 					return(0);
522 		} else if ( ! isprint((u_char)*p))
523 			if ( ! warn_print(mdoc, line, pos))
524 				return(0);
525 
526 		if ('\\' != *p)
527 			continue;
528 
529 		c = mandoc_special(p);
530 		if (c) {
531 			p += c - 1;
532 			pos += c - 1;
533 			continue;
534 		}
535 		if ( ! (MDOC_IGN_ESCAPE & mdoc->pflags))
536 			return(mdoc_perr(mdoc, line, pos, EESCAPE));
537 		if ( ! mdoc_pwarn(mdoc, line, pos, EESCAPE))
538 			return(0);
539 	}
540 
541 	return(1);
542 }
543 
544 
545 
546 
547 static int
548 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
549 {
550 
551 	assert(n->parent);
552 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
553 			(t == n->parent->type))
554 		return(1);
555 
556 	return(mdoc_verr(mdoc, n->line, n->pos, "require parent %s",
557 		MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]));
558 }
559 
560 
561 
562 static int
563 pre_display(PRE_ARGS)
564 {
565 	struct mdoc_node *node;
566 
567 	/* Display elements (`Bd', `D1'...) cannot be nested. */
568 
569 	if (MDOC_BLOCK != n->type)
570 		return(1);
571 
572 	/* LINTED */
573 	for (node = mdoc->last->parent; node; node = node->parent)
574 		if (MDOC_BLOCK == node->type)
575 			if (MDOC_Bd == node->tok)
576 				break;
577 	if (NULL == node)
578 		return(1);
579 
580 	return(mdoc_nerr(mdoc, n, ENESTDISP));
581 }
582 
583 
584 static int
585 pre_bl(PRE_ARGS)
586 {
587 	int		 pos, type, width, offset;
588 
589 	if (MDOC_BLOCK != n->type)
590 		return(1);
591 	if (NULL == n->args)
592 		return(mdoc_nerr(mdoc, n, ELISTTYPE));
593 
594 	/* Make sure that only one type of list is specified.  */
595 
596 	type = offset = width = -1;
597 
598 	/* LINTED */
599 	for (pos = 0; pos < (int)n->args->argc; pos++)
600 		switch (n->args->argv[pos].arg) {
601 		case (MDOC_Bullet):
602 			/* FALLTHROUGH */
603 		case (MDOC_Dash):
604 			/* FALLTHROUGH */
605 		case (MDOC_Enum):
606 			/* FALLTHROUGH */
607 		case (MDOC_Hyphen):
608 			/* FALLTHROUGH */
609 		case (MDOC_Item):
610 			/* FALLTHROUGH */
611 		case (MDOC_Tag):
612 			/* FALLTHROUGH */
613 		case (MDOC_Diag):
614 			/* FALLTHROUGH */
615 		case (MDOC_Hang):
616 			/* FALLTHROUGH */
617 		case (MDOC_Ohang):
618 			/* FALLTHROUGH */
619 		case (MDOC_Inset):
620 			/* FALLTHROUGH */
621 		case (MDOC_Column):
622 			/*
623 			 * Note that if a duplicate is detected, we
624 			 * remove the duplicate instead of passing it
625 			 * over.  If we don't do this, mdoc_action will
626 			 * become confused when it scans over multiple
627 			 * types whilst setting its bitmasks.
628 			 *
629 			 * FIXME: this should occur in mdoc_action.c.
630 			 */
631 			if (type >= 0) {
632 				if ( ! mdoc_nwarn(mdoc, n, EMULTILIST))
633 					return(0);
634 				mdoc_argn_free(n->args, pos);
635 				break;
636 			}
637 			type = n->args->argv[pos].arg;
638 			break;
639 		case (MDOC_Compact):
640 			if (type < 0 && ! mdoc_nwarn(mdoc, n, ENOTYPE))
641 				return(0);
642 			break;
643 		case (MDOC_Width):
644 			if (width >= 0)
645 				return(mdoc_nerr(mdoc, n, EARGREP));
646 			if (type < 0 && ! mdoc_nwarn(mdoc, n, ENOTYPE))
647 				return(0);
648 			width = n->args->argv[pos].arg;
649 			break;
650 		case (MDOC_Offset):
651 			if (offset >= 0)
652 				return(mdoc_nerr(mdoc, n, EARGREP));
653 			if (type < 0 && ! mdoc_nwarn(mdoc, n, ENOTYPE))
654 				return(0);
655 			offset = n->args->argv[pos].arg;
656 			break;
657 		default:
658 			break;
659 		}
660 
661 	if (type < 0)
662 		return(mdoc_nerr(mdoc, n, ELISTTYPE));
663 
664 	/*
665 	 * Validate the width field.  Some list types don't need width
666 	 * types and should be warned about them.  Others should have it
667 	 * and must also be warned.
668 	 */
669 
670 	switch (type) {
671 	case (MDOC_Tag):
672 		if (width < 0 && ! mdoc_nwarn(mdoc, n, EMISSWIDTH))
673 			return(0);
674 		break;
675 	case (MDOC_Column):
676 		/* FALLTHROUGH */
677 	case (MDOC_Diag):
678 		/* FALLTHROUGH */
679 	case (MDOC_Ohang):
680 		/* FALLTHROUGH */
681 	case (MDOC_Inset):
682 		/* FALLTHROUGH */
683 	case (MDOC_Item):
684 		if (width >= 0 && ! mdoc_nwarn(mdoc, n, ENOWIDTH))
685 			return(0);
686 		break;
687 	default:
688 		break;
689 	}
690 
691 	return(1);
692 }
693 
694 
695 static int
696 pre_bd(PRE_ARGS)
697 {
698 	int		 i, type, err;
699 
700 	if (MDOC_BLOCK != n->type)
701 		return(1);
702 	if (NULL == n->args)
703 		return(mdoc_nerr(mdoc, n, EDISPTYPE));
704 
705 	/* Make sure that only one type of display is specified.  */
706 
707 	/* LINTED */
708 	for (i = 0, err = type = 0; ! err &&
709 			i < (int)n->args->argc; i++)
710 		switch (n->args->argv[i].arg) {
711 		case (MDOC_Centred):
712 			/* FALLTHROUGH */
713 		case (MDOC_Ragged):
714 			/* FALLTHROUGH */
715 		case (MDOC_Unfilled):
716 			/* FALLTHROUGH */
717 		case (MDOC_Filled):
718 			/* FALLTHROUGH */
719 		case (MDOC_Literal):
720 			if (0 == type++)
721 				break;
722 			return(mdoc_nerr(mdoc, n, EMULTIDISP));
723 		default:
724 			break;
725 		}
726 
727 	if (type)
728 		return(1);
729 	return(mdoc_nerr(mdoc, n, EDISPTYPE));
730 }
731 
732 
733 static int
734 pre_ss(PRE_ARGS)
735 {
736 
737 	if (MDOC_BLOCK != n->type)
738 		return(1);
739 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
740 }
741 
742 
743 static int
744 pre_sh(PRE_ARGS)
745 {
746 
747 	if (MDOC_BLOCK != n->type)
748 		return(1);
749 	return(check_parent(mdoc, n, -1, MDOC_ROOT));
750 }
751 
752 
753 static int
754 pre_it(PRE_ARGS)
755 {
756 
757 	if (MDOC_BLOCK != n->type)
758 		return(1);
759 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
760 }
761 
762 
763 static int
764 pre_an(PRE_ARGS)
765 {
766 
767 	if (NULL == n->args || 1 == n->args->argc)
768 		return(1);
769 	return(mdoc_verr(mdoc, n->line, n->pos,
770 				"only one argument allowed"));
771 }
772 
773 
774 static int
775 pre_lb(PRE_ARGS)
776 {
777 
778 	return(check_sec(mdoc, n, SEC_LIBRARY, SEC_CUSTOM));
779 }
780 
781 
782 static int
783 pre_rv(PRE_ARGS)
784 {
785 
786 	if ( ! check_msec(mdoc, n, 2, 3, 0))
787 		return(0);
788 	return(check_stdarg(mdoc, n));
789 }
790 
791 
792 static int
793 pre_ex(PRE_ARGS)
794 {
795 
796 	if ( ! check_msec(mdoc, n, 1, 6, 8, 0))
797 		return(0);
798 	return(check_stdarg(mdoc, n));
799 }
800 
801 
802 static int
803 pre_cd(PRE_ARGS)
804 {
805 
806 	return(check_msec(mdoc, n, 4, 0));
807 }
808 
809 
810 static int
811 pre_dt(PRE_ARGS)
812 {
813 
814 	/* FIXME: make sure is capitalised. */
815 
816 	if (0 == mdoc->meta.date || mdoc->meta.os)
817 		if ( ! mdoc_nwarn(mdoc, n, EPROLOOO))
818 			return(0);
819 	if (mdoc->meta.title)
820 		if ( ! mdoc_nwarn(mdoc, n, EPROLREP))
821 			return(0);
822 	return(1);
823 }
824 
825 
826 static int
827 pre_os(PRE_ARGS)
828 {
829 
830 	if (NULL == mdoc->meta.title || 0 == mdoc->meta.date)
831 		if ( ! mdoc_nwarn(mdoc, n, EPROLOOO))
832 			return(0);
833 	if (mdoc->meta.os)
834 		if ( ! mdoc_nwarn(mdoc, n, EPROLREP))
835 			return(0);
836 	return(1);
837 }
838 
839 
840 static int
841 pre_dd(PRE_ARGS)
842 {
843 
844 	if (mdoc->meta.title || mdoc->meta.os)
845 		if ( ! mdoc_nwarn(mdoc, n, EPROLOOO))
846 			return(0);
847 	if (mdoc->meta.date)
848 		if ( ! mdoc_nwarn(mdoc, n, EPROLREP))
849 			return(0);
850 	return(1);
851 }
852 
853 
854 static int
855 post_bf(POST_ARGS)
856 {
857 	char		 *p;
858 	struct mdoc_node *head;
859 
860 	if (MDOC_BLOCK != mdoc->last->type)
861 		return(1);
862 
863 	head = mdoc->last->head;
864 
865 	if (mdoc->last->args && head->child)
866 		return(mdoc_nerr(mdoc, mdoc->last, ELINE));
867 	else if (mdoc->last->args)
868 		return(1);
869 
870 	if (NULL == head->child || MDOC_TEXT != head->child->type)
871 		return(mdoc_nerr(mdoc, mdoc->last, ELINE));
872 
873 	p = head->child->string;
874 
875 	if (0 == strcmp(p, "Em"))
876 		return(1);
877 	else if (0 == strcmp(p, "Li"))
878 		return(1);
879 	else if (0 == strcmp(p, "Sy"))
880 		return(1);
881 
882 	return(mdoc_nerr(mdoc, head, EFONT));
883 }
884 
885 
886 static int
887 post_lb(POST_ARGS)
888 {
889 
890 	if (mdoc_a2lib(mdoc->last->child->string))
891 		return(1);
892 	return(mdoc_nwarn(mdoc, mdoc->last, ELIB));
893 }
894 
895 
896 static int
897 post_vt(POST_ARGS)
898 {
899 	const struct mdoc_node *n;
900 
901 	/*
902 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
903 	 * have different syntaxes (yet more context-sensitive
904 	 * behaviour).  ELEM types must have a child; BLOCK types,
905 	 * specifically the BODY, should only have TEXT children.
906 	 */
907 
908 	if (MDOC_ELEM == mdoc->last->type)
909 		return(eerr_ge1(mdoc));
910 	if (MDOC_BODY != mdoc->last->type)
911 		return(1);
912 
913 	for (n = mdoc->last->child; n; n = n->next)
914 		if (MDOC_TEXT != n->type &&
915 		    (MDOC_ELEM != n->type || MDOC_eos != n->tok))
916 			if ( ! mdoc_nwarn(mdoc, n, EBADCHILD))
917 				return(0);
918 
919 	return(1);
920 }
921 
922 
923 static int
924 post_nm(POST_ARGS)
925 {
926 
927 	if (mdoc->last->child)
928 		return(1);
929 	if (mdoc->meta.name)
930 		return(1);
931 	return(mdoc_nerr(mdoc, mdoc->last, ENAME));
932 }
933 
934 
935 static int
936 post_at(POST_ARGS)
937 {
938 
939 	if (NULL == mdoc->last->child)
940 		return(1);
941 	if (MDOC_TEXT != mdoc->last->child->type)
942 		return(mdoc_nerr(mdoc, mdoc->last, EATT));
943 	if (mdoc_a2att(mdoc->last->child->string))
944 		return(1);
945 	return(mdoc_nerr(mdoc, mdoc->last, EATT));
946 }
947 
948 
949 static int
950 post_an(POST_ARGS)
951 {
952 
953 	if (mdoc->last->args) {
954 		if (NULL == mdoc->last->child)
955 			return(1);
956 		return(mdoc_nerr(mdoc, mdoc->last, ENOLINE));
957 	}
958 
959 	if (mdoc->last->child)
960 		return(1);
961 	return(mdoc_nerr(mdoc, mdoc->last, ELINE));
962 }
963 
964 
965 static int
966 post_it(POST_ARGS)
967 {
968 	int		  type, i, cols;
969 	struct mdoc_node *n, *c;
970 
971 	if (MDOC_BLOCK != mdoc->last->type)
972 		return(1);
973 
974 	n = mdoc->last->parent->parent;
975 	if (NULL == n->args)
976 		return(mdoc_nerr(mdoc, mdoc->last, ELISTTYPE));
977 
978 	/* Some types require block-head, some not. */
979 
980 	/* LINTED */
981 	for (cols = type = -1, i = 0; -1 == type &&
982 			i < (int)n->args->argc; i++)
983 		switch (n->args->argv[i].arg) {
984 		case (MDOC_Tag):
985 			/* FALLTHROUGH */
986 		case (MDOC_Diag):
987 			/* FALLTHROUGH */
988 		case (MDOC_Hang):
989 			/* FALLTHROUGH */
990 		case (MDOC_Ohang):
991 			/* FALLTHROUGH */
992 		case (MDOC_Inset):
993 			/* FALLTHROUGH */
994 		case (MDOC_Bullet):
995 			/* FALLTHROUGH */
996 		case (MDOC_Dash):
997 			/* FALLTHROUGH */
998 		case (MDOC_Enum):
999 			/* FALLTHROUGH */
1000 		case (MDOC_Hyphen):
1001 			/* FALLTHROUGH */
1002 		case (MDOC_Item):
1003 			type = n->args->argv[i].arg;
1004 			break;
1005 		case (MDOC_Column):
1006 			type = n->args->argv[i].arg;
1007 			cols = (int)n->args->argv[i].sz;
1008 			break;
1009 		default:
1010 			break;
1011 		}
1012 
1013 	if (-1 == type)
1014 		return(mdoc_nerr(mdoc, mdoc->last, ELISTTYPE));
1015 
1016 	switch (type) {
1017 	case (MDOC_Tag):
1018 		if (NULL == mdoc->last->head->child)
1019 			if ( ! mdoc_nwarn(mdoc, mdoc->last, ELINE))
1020 				return(0);
1021 		break;
1022 	case (MDOC_Hang):
1023 		/* FALLTHROUGH */
1024 	case (MDOC_Ohang):
1025 		/* FALLTHROUGH */
1026 	case (MDOC_Inset):
1027 		/* FALLTHROUGH */
1028 	case (MDOC_Diag):
1029 		if (NULL == mdoc->last->head->child)
1030 			if ( ! mdoc_nwarn(mdoc, mdoc->last, ELINE))
1031 				return(0);
1032 		if (NULL == mdoc->last->body->child)
1033 			if ( ! mdoc_nwarn(mdoc, mdoc->last, EMULTILINE))
1034 				return(0);
1035 		break;
1036 	case (MDOC_Bullet):
1037 		/* FALLTHROUGH */
1038 	case (MDOC_Dash):
1039 		/* FALLTHROUGH */
1040 	case (MDOC_Enum):
1041 		/* FALLTHROUGH */
1042 	case (MDOC_Hyphen):
1043 		/* FALLTHROUGH */
1044 	case (MDOC_Item):
1045 		if (mdoc->last->head->child)
1046 			if ( ! mdoc_nwarn(mdoc, mdoc->last, ENOLINE))
1047 				return(0);
1048 		if (NULL == mdoc->last->body->child)
1049 			if ( ! mdoc_nwarn(mdoc, mdoc->last, EMULTILINE))
1050 				return(0);
1051 		break;
1052 	case (MDOC_Column):
1053 		if (NULL == mdoc->last->head->child)
1054 			if ( ! mdoc_nwarn(mdoc, mdoc->last, ELINE))
1055 				return(0);
1056 		if (mdoc->last->body->child)
1057 			if ( ! mdoc_nwarn(mdoc, mdoc->last, ENOMULTILINE))
1058 				return(0);
1059 		c = mdoc->last->child;
1060 		for (i = 0; c && MDOC_HEAD == c->type; c = c->next)
1061 			i++;
1062 
1063 		if (i < cols || i == (cols + 1)) {
1064 			if ( ! mdoc_vwarn(mdoc, mdoc->last->line,
1065 					mdoc->last->pos, "column "
1066 					"mismatch: have %d, want %d",
1067 					i, cols))
1068 				return(0);
1069 			break;
1070 		} else if (i == cols)
1071 			break;
1072 
1073 		return(mdoc_verr(mdoc, mdoc->last->line,
1074 				mdoc->last->pos, "column mismatch: "
1075 				"have %d, want %d", i, cols));
1076 	default:
1077 		break;
1078 	}
1079 
1080 	return(1);
1081 }
1082 
1083 
1084 static int
1085 post_bl_head(POST_ARGS)
1086 {
1087 	int			i;
1088 	const struct mdoc_node *n;
1089 	const struct mdoc_argv *a;
1090 
1091 	n = mdoc->last->parent;
1092 	assert(n->args);
1093 
1094 	for (i = 0; i < (int)n->args->argc; i++) {
1095 		a = &n->args->argv[i];
1096 		if (a->arg == MDOC_Column) {
1097 			if (a->sz && mdoc->last->nchild)
1098 				return(mdoc_nerr(mdoc, n, ECOLMIS));
1099 			return(1);
1100 		}
1101 	}
1102 
1103 	if (0 == (i = mdoc->last->nchild))
1104 		return(1);
1105 	return(warn_count(mdoc, "==", 0, "line arguments", i));
1106 }
1107 
1108 
1109 static int
1110 post_bl(POST_ARGS)
1111 {
1112 	struct mdoc_node	*n;
1113 
1114 	if (MDOC_HEAD == mdoc->last->type)
1115 		return(post_bl_head(mdoc));
1116 	if (MDOC_BODY != mdoc->last->type)
1117 		return(1);
1118 	if (NULL == mdoc->last->child)
1119 		return(1);
1120 
1121 	/*
1122 	 * We only allow certain children of `Bl'.  This is usually on
1123 	 * `It', but apparently `Sm' occurs here and there, so we let
1124 	 * that one through, too.
1125 	 */
1126 
1127 	/* LINTED */
1128 	for (n = mdoc->last->child; n; n = n->next) {
1129 		if (MDOC_BLOCK == n->type && MDOC_It == n->tok)
1130 			continue;
1131 		if (MDOC_Sm == n->tok)
1132 			continue;
1133 		return(mdoc_nerr(mdoc, n, EBADCHILD));
1134 	}
1135 
1136 	return(1);
1137 }
1138 
1139 
1140 static int
1141 ebool(struct mdoc *mdoc)
1142 {
1143 	struct mdoc_node *n;
1144 
1145 	/* LINTED */
1146 	for (n = mdoc->last->child; n; n = n->next) {
1147 		if (MDOC_TEXT != n->type)
1148 			break;
1149 		if (0 == strcmp(n->string, "on"))
1150 			continue;
1151 		if (0 == strcmp(n->string, "off"))
1152 			continue;
1153 		break;
1154 	}
1155 
1156 	if (NULL == n)
1157 		return(1);
1158 	return(mdoc_nerr(mdoc, n, EBOOL));
1159 }
1160 
1161 
1162 static int
1163 post_root(POST_ARGS)
1164 {
1165 
1166 	if (NULL == mdoc->first->child)
1167 		return(mdoc_nerr(mdoc, mdoc->first, ENODAT));
1168 	if ( ! (MDOC_PBODY & mdoc->flags))
1169 		return(mdoc_nerr(mdoc, mdoc->first, ENOPROLOGUE));
1170 
1171 	if (MDOC_BLOCK != mdoc->first->child->type)
1172 		return(mdoc_nerr(mdoc, mdoc->first, ENODAT));
1173 	if (MDOC_Sh != mdoc->first->child->tok)
1174 		return(mdoc_nerr(mdoc, mdoc->first, ENODAT));
1175 
1176 	return(1);
1177 }
1178 
1179 
1180 static int
1181 post_st(POST_ARGS)
1182 {
1183 
1184 	if (mdoc_a2st(mdoc->last->child->string))
1185 		return(1);
1186 	return(mdoc_nerr(mdoc, mdoc->last, EBADSTAND));
1187 }
1188 
1189 
1190 static int
1191 post_rs(POST_ARGS)
1192 {
1193 	struct mdoc_node	*nn;
1194 
1195 	if (MDOC_BODY != mdoc->last->type)
1196 		return(1);
1197 
1198 	for (nn = mdoc->last->child; nn; nn = nn->next)
1199 		switch (nn->tok) {
1200 		case(MDOC__U):
1201 			/* FALLTHROUGH */
1202 		case(MDOC__Q):
1203 			/* FALLTHROUGH */
1204 		case(MDOC__C):
1205 			/* FALLTHROUGH */
1206 		case(MDOC__A):
1207 			/* FALLTHROUGH */
1208 		case(MDOC__B):
1209 			/* FALLTHROUGH */
1210 		case(MDOC__D):
1211 			/* FALLTHROUGH */
1212 		case(MDOC__I):
1213 			/* FALLTHROUGH */
1214 		case(MDOC__J):
1215 			/* FALLTHROUGH */
1216 		case(MDOC__N):
1217 			/* FALLTHROUGH */
1218 		case(MDOC__O):
1219 			/* FALLTHROUGH */
1220 		case(MDOC__P):
1221 			/* FALLTHROUGH */
1222 		case(MDOC__R):
1223 			/* FALLTHROUGH */
1224 		case(MDOC__T):
1225 			/* FALLTHROUGH */
1226 		case(MDOC__V):
1227 			break;
1228 		default:
1229 			return(mdoc_nerr(mdoc, nn, EBADCHILD));
1230 		}
1231 
1232 	return(1);
1233 }
1234 
1235 
1236 static int
1237 post_sh(POST_ARGS)
1238 {
1239 
1240 	if (MDOC_HEAD == mdoc->last->type)
1241 		return(post_sh_head(mdoc));
1242 	if (MDOC_BODY == mdoc->last->type)
1243 		return(post_sh_body(mdoc));
1244 
1245 	return(1);
1246 }
1247 
1248 
1249 static int
1250 post_sh_body(POST_ARGS)
1251 {
1252 	struct mdoc_node *n;
1253 
1254 	if (SEC_NAME != mdoc->lastsec)
1255 		return(1);
1256 
1257 	/*
1258 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1259 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1260 	 * children of the BODY declaration can also be "text".
1261 	 */
1262 
1263 	if (NULL == (n = mdoc->last->child))
1264 		return(mdoc_nwarn(mdoc, mdoc->last, ENAMESECINC));
1265 
1266 	for ( ; n && n->next; n = n->next) {
1267 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1268 			continue;
1269 		if (MDOC_TEXT == n->type)
1270 			continue;
1271 		if ( ! mdoc_nwarn(mdoc, mdoc->last, ENAMESECINC))
1272 			return(0);
1273 	}
1274 
1275 	assert(n);
1276 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1277 		return(1);
1278 	return(mdoc_nwarn(mdoc, mdoc->last, ENAMESECINC));
1279 }
1280 
1281 
1282 static int
1283 post_sh_head(POST_ARGS)
1284 {
1285 	char		        buf[64];
1286 	enum mdoc_sec	        sec;
1287 	const struct mdoc_node *n;
1288 
1289 	/*
1290 	 * Process a new section.  Sections are either "named" or
1291 	 * "custom"; custom sections are user-defined, while named ones
1292 	 * usually follow a conventional order and may only appear in
1293 	 * certain manual sections.
1294 	 */
1295 
1296 	buf[0] = 0;
1297 
1298 	for (n = mdoc->last->child; n; n = n->next) {
1299 		/* XXX - copied from compact(). */
1300 		assert(MDOC_TEXT == n->type);
1301 
1302 		if (strlcat(buf, n->string, 64) >= 64)
1303 			return(mdoc_nerr(mdoc, n, ETOOLONG));
1304 		if (NULL == n->next)
1305 			continue;
1306 		if (strlcat(buf, " ", 64) >= 64)
1307 			return(mdoc_nerr(mdoc, n, ETOOLONG));
1308 	}
1309 
1310 	sec = mdoc_atosec(buf);
1311 
1312 	/*
1313 	 * Check: NAME should always be first, CUSTOM has no roles,
1314 	 * non-CUSTOM has a conventional order to be followed.
1315 	 */
1316 
1317 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed &&
1318 			! mdoc_nwarn(mdoc, mdoc->last, ESECNAME))
1319 		return(0);
1320 	if (SEC_CUSTOM == sec)
1321 		return(1);
1322 	if (sec == mdoc->lastnamed)
1323 		if ( ! mdoc_nwarn(mdoc, mdoc->last, ESECREP))
1324 			return(0);
1325 	if (sec < mdoc->lastnamed)
1326 		if ( ! mdoc_nwarn(mdoc, mdoc->last, ESECOOO))
1327 			return(0);
1328 
1329 	/*
1330 	 * Check particular section/manual conventions.  LIBRARY can
1331 	 * only occur in msec 2, 3 (TODO: are there more of these?).
1332 	 */
1333 
1334 	switch (sec) {
1335 	case (SEC_LIBRARY):
1336 		switch (mdoc->meta.msec) {
1337 		case (2):
1338 			/* FALLTHROUGH */
1339 		case (3):
1340 			break;
1341 		default:
1342 			return(mdoc_nwarn(mdoc, mdoc->last, EWRONGMSEC));
1343 		}
1344 		break;
1345 	default:
1346 		break;
1347 	}
1348 
1349 	return(1);
1350 }
1351 
1352 
1353 static int
1354 pre_fd(PRE_ARGS)
1355 {
1356 
1357 	return(check_sec(mdoc, n, SEC_SYNOPSIS, SEC_CUSTOM));
1358 }
1359