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