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