Annotation of mandoc/validate.c, Revision 1.57
1.57 ! kristaps 1: /* $Id: validate.c,v 1.56 2009/02/24 13:46:54 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
1.56 kristaps 20: #include <ctype.h>
1.8 kristaps 21: #include <stdlib.h>
1.1 kristaps 22:
23: #include "private.h"
24:
1.44 kristaps 25: /*
26: * Pre- and post-validate macros as they're parsed. Pre-validation
27: * occurs when the macro has been detected and its arguments parsed.
28: * Post-validation occurs when all child macros have also been parsed.
29: * In the ELEMENT case, this is simply the parameters of the macro; in
30: * the BLOCK case, this is the HEAD, BODY, TAIL and so on.
31: */
32:
1.55 kristaps 33: #define PRE_ARGS struct mdoc *mdoc, const struct mdoc_node *n
34: #define POST_ARGS struct mdoc *mdoc
35:
36: typedef int (*v_pre)(PRE_ARGS);
37: typedef int (*v_post)(POST_ARGS);
1.14 kristaps 38:
1.36 kristaps 39: /* FIXME: some sections should only occur in specific msecs. */
40: /* FIXME: ignoring Pp. */
41: /* FIXME: math symbols. */
42:
1.14 kristaps 43: struct valids {
1.24 kristaps 44: v_pre *pre;
1.17 kristaps 45: v_post *post;
1.14 kristaps 46: };
1.1 kristaps 47:
1.37 kristaps 48: /* Utility checks. */
49:
1.55 kristaps 50: static int check_parent(PRE_ARGS, int, enum mdoc_type);
51: static int check_msec(PRE_ARGS, int, enum mdoc_msec *);
52: static int check_stdarg(PRE_ARGS);
53:
54: static int check_text(struct mdoc *,
55: size_t, size_t, const char *);
56:
1.51 kristaps 57: static int err_child_lt(struct mdoc *, const char *, int);
58: static int err_child_gt(struct mdoc *, const char *, int);
59: static int warn_child_gt(struct mdoc *, const char *, int);
60: static int err_child_eq(struct mdoc *, const char *, int);
61: static int warn_child_eq(struct mdoc *, const char *, int);
62:
63: /* Utility auxiliaries. */
64:
65: static inline int count_child(struct mdoc *);
66: static inline int warn_count(struct mdoc *, const char *,
67: int, const char *, int);
68: static inline int err_count(struct mdoc *, const char *,
69: int, const char *, int);
1.11 kristaps 70:
1.37 kristaps 71: /* Specific pre-child-parse routines. */
72:
1.55 kristaps 73: static int pre_display(PRE_ARGS);
74: static int pre_sh(PRE_ARGS);
75: static int pre_ss(PRE_ARGS);
76: static int pre_bd(PRE_ARGS);
77: static int pre_bl(PRE_ARGS);
78: static int pre_it(PRE_ARGS);
79: static int pre_cd(PRE_ARGS);
80: static int pre_er(PRE_ARGS);
81: static int pre_ex(PRE_ARGS);
82: static int pre_rv(PRE_ARGS);
83: static int pre_an(PRE_ARGS);
84: static int pre_st(PRE_ARGS);
85: static int pre_prologue(PRE_ARGS);
86: static int pre_prologue(PRE_ARGS);
87: static int pre_prologue(PRE_ARGS);
1.24 kristaps 88:
1.37 kristaps 89: /* Specific post-child-parse routines. */
90:
1.55 kristaps 91: static int herr_ge1(POST_ARGS);
92: static int herr_le1(POST_ARGS);
93: static int herr_eq0(POST_ARGS);
94: static int eerr_eq0(POST_ARGS);
95: static int eerr_le1(POST_ARGS);
96: static int eerr_le2(POST_ARGS);
97: static int eerr_eq1(POST_ARGS);
98: static int eerr_ge1(POST_ARGS);
99: static int ewarn_eq0(POST_ARGS);
100: static int ewarn_eq1(POST_ARGS);
101: static int bwarn_ge1(POST_ARGS);
102: static int ewarn_ge1(POST_ARGS);
103: static int ebool(POST_ARGS);
104:
105: static int post_sh(POST_ARGS);
106: static int post_sh_body(POST_ARGS);
107: static int post_sh_head(POST_ARGS);
1.57 ! kristaps 108: static int post_fd(POST_ARGS);
1.55 kristaps 109: static int post_bl(POST_ARGS);
110: static int post_it(POST_ARGS);
111: static int post_ex(POST_ARGS);
112: static int post_an(POST_ARGS);
113: static int post_at(POST_ARGS);
114: static int post_xr(POST_ARGS);
115: static int post_nm(POST_ARGS);
116: static int post_bf(POST_ARGS);
117: static int post_root(POST_ARGS);
1.37 kristaps 118:
119: /* Collections of pre-child-parse routines. */
1.17 kristaps 120:
1.24 kristaps 121: static v_pre pres_prologue[] = { pre_prologue, NULL };
122: static v_pre pres_d1[] = { pre_display, NULL };
123: static v_pre pres_bd[] = { pre_display, pre_bd, NULL };
124: static v_pre pres_bl[] = { pre_bl, NULL };
1.25 kristaps 125: static v_pre pres_it[] = { pre_it, NULL };
1.33 kristaps 126: static v_pre pres_ss[] = { pre_ss, NULL };
127: static v_pre pres_sh[] = { pre_sh, NULL };
128: static v_pre pres_cd[] = { pre_cd, NULL };
129: static v_pre pres_er[] = { pre_er, NULL };
130: static v_pre pres_ex[] = { pre_ex, NULL };
1.36 kristaps 131: static v_pre pres_rv[] = { pre_rv, NULL };
1.35 kristaps 132: static v_pre pres_an[] = { pre_an, NULL };
1.36 kristaps 133: static v_pre pres_st[] = { pre_st, NULL };
1.24 kristaps 134:
1.37 kristaps 135: /* Collections of post-child-parse routines. */
136:
137: static v_post posts_bool[] = { eerr_eq1, ebool, NULL };
138: static v_post posts_bd[] = { herr_eq0, bwarn_ge1, NULL };
139: static v_post posts_text[] = { eerr_ge1, NULL };
140: static v_post posts_wtext[] = { ewarn_ge1, NULL };
141: static v_post posts_notext[] = { eerr_eq0, NULL };
1.43 kristaps 142: static v_post posts_wline[] = { bwarn_ge1, herr_eq0, NULL };
1.37 kristaps 143: static v_post posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL };
144: static v_post posts_bl[] = { herr_eq0, bwarn_ge1, post_bl, NULL };
1.25 kristaps 145: static v_post posts_it[] = { post_it, NULL };
1.39 kristaps 146: static v_post posts_in[] = { ewarn_eq1, NULL };
1.37 kristaps 147: static v_post posts_ss[] = { herr_ge1, NULL };
1.52 kristaps 148: static v_post posts_pf[] = { eerr_eq1, NULL };
1.37 kristaps 149: static v_post posts_pp[] = { ewarn_eq0, NULL };
150: static v_post posts_ex[] = { eerr_le1, post_ex, NULL };
1.35 kristaps 151: static v_post posts_an[] = { post_an, NULL };
1.37 kristaps 152: static v_post posts_at[] = { post_at, NULL };
153: static v_post posts_xr[] = { eerr_ge1, eerr_le2, post_xr, NULL };
154: static v_post posts_nm[] = { post_nm, NULL };
1.41 kristaps 155: static v_post posts_bf[] = { herr_le1, post_bf, NULL };
1.45 kristaps 156: static v_post posts_rs[] = { herr_eq0, bwarn_ge1, NULL };
157: static v_post posts_fo[] = { bwarn_ge1, NULL };
158: static v_post posts_bk[] = { herr_eq0, bwarn_ge1, NULL };
1.57 ! kristaps 159: static v_post posts_fd[] = { ewarn_ge1, post_fd, NULL };
1.9 kristaps 160:
1.37 kristaps 161: /* Per-macro pre- and post-child-check routine collections. */
1.12 kristaps 162:
1.9 kristaps 163: const struct valids mdoc_valids[MDOC_MAX] = {
1.51 kristaps 164: { NULL, NULL }, /* \" */
165: { pres_prologue, posts_text }, /* Dd */
166: { pres_prologue, NULL }, /* Dt */
167: { pres_prologue, NULL }, /* Os */
168: { pres_sh, posts_sh }, /* Sh */
169: { pres_ss, posts_ss }, /* Ss */
170: { NULL, posts_pp }, /* Pp */
171: { pres_d1, posts_wline }, /* D1 */
172: { pres_d1, posts_wline }, /* Dl */
173: { pres_bd, posts_bd }, /* Bd */
174: { NULL, NULL }, /* Ed */
175: { pres_bl, posts_bl }, /* Bl */
176: { NULL, NULL }, /* El */
177: { pres_it, posts_it }, /* It */
178: { NULL, posts_text }, /* Ad */
179: { pres_an, posts_an }, /* An */
180: { NULL, NULL }, /* Ar */
181: { pres_cd, posts_text }, /* Cd */
182: { NULL, NULL }, /* Cm */
183: { NULL, posts_text }, /* Dv */
184: { pres_er, posts_text }, /* Er */
185: { NULL, posts_text }, /* Ev */
186: { pres_ex, posts_ex }, /* Ex */
187: { NULL, posts_text }, /* Fa */
1.57 ! kristaps 188: { NULL, posts_fd }, /* Fd */
1.51 kristaps 189: { NULL, NULL }, /* Fl */
190: { NULL, posts_text }, /* Fn */
191: { NULL, posts_wtext }, /* Ft */
192: { NULL, posts_text }, /* Ic */
193: { NULL, posts_in }, /* In */
194: { NULL, posts_text }, /* Li */
195: { NULL, posts_wtext }, /* Nd */
196: { NULL, posts_nm }, /* Nm */
197: { NULL, posts_wline }, /* Op */
198: { NULL, NULL }, /* Ot */
199: { NULL, NULL }, /* Pa */
200: { pres_rv, posts_notext }, /* Rv */
201: { pres_st, posts_notext }, /* St */
202: { NULL, posts_text }, /* Va */
203: { NULL, posts_text }, /* Vt */
204: { NULL, posts_xr }, /* Xr */
205: { NULL, posts_text }, /* %A */
206: { NULL, posts_text }, /* %B */
207: { NULL, posts_text }, /* %D */
208: { NULL, posts_text }, /* %I */
209: { NULL, posts_text }, /* %J */
210: { NULL, posts_text }, /* %N */
211: { NULL, posts_text }, /* %O */
212: { NULL, posts_text }, /* %P */
213: { NULL, posts_text }, /* %R */
214: { NULL, posts_text }, /* %T */
215: { NULL, posts_text }, /* %V */
216: { NULL, NULL }, /* Ac */
217: { NULL, NULL }, /* Ao */
218: { NULL, posts_wline }, /* Aq */
219: { NULL, posts_at }, /* At */
220: { NULL, NULL }, /* Bc */
221: { NULL, posts_bf }, /* Bf */
222: { NULL, NULL }, /* Bo */
223: { NULL, posts_wline }, /* Bq */
224: { NULL, NULL }, /* Bsx */
225: { NULL, NULL }, /* Bx */
226: { NULL, posts_bool }, /* Db */
227: { NULL, NULL }, /* Dc */
228: { NULL, NULL }, /* Do */
229: { NULL, posts_wline }, /* Dq */
230: { NULL, NULL }, /* Ec */
231: { NULL, NULL }, /* Ef */
232: { NULL, posts_text }, /* Em */
233: { NULL, NULL }, /* Eo */
234: { NULL, NULL }, /* Fx */
235: { NULL, posts_text }, /* Ms */
236: { NULL, posts_notext }, /* No */
237: { NULL, posts_notext }, /* Ns */
238: { NULL, NULL }, /* Nx */
239: { NULL, NULL }, /* Ox */
240: { NULL, NULL }, /* Pc */
1.52 kristaps 241: { NULL, posts_pf }, /* Pf */
1.51 kristaps 242: { NULL, NULL }, /* Po */
243: { NULL, posts_wline }, /* Pq */
244: { NULL, NULL }, /* Qc */
245: { NULL, posts_wline }, /* Ql */
246: { NULL, NULL }, /* Qo */
247: { NULL, posts_wline }, /* Qq */
248: { NULL, NULL }, /* Re */
249: { NULL, posts_rs }, /* Rs */
250: { NULL, NULL }, /* Sc */
251: { NULL, NULL }, /* So */
252: { NULL, posts_wline }, /* Sq */
253: { NULL, posts_bool }, /* Sm */
254: { NULL, posts_text }, /* Sx */
255: { NULL, posts_text }, /* Sy */
256: { NULL, posts_text }, /* Tn */
257: { NULL, NULL }, /* Ux */
258: { NULL, NULL }, /* Xc */
259: { NULL, NULL }, /* Xo */
260: { NULL, posts_fo }, /* Fo */
261: { NULL, NULL }, /* Fc */
262: { NULL, NULL }, /* Oo */
263: { NULL, NULL }, /* Oc */
264: { NULL, posts_bk }, /* Bk */
265: { NULL, NULL }, /* Ek */
266: { NULL, posts_notext }, /* Bt */
267: { NULL, NULL }, /* Hf */
268: { NULL, NULL }, /* Fr */
269: { NULL, posts_notext }, /* Ud */
1.9 kristaps 270: };
1.6 kristaps 271:
272:
1.57 ! kristaps 273: int
! 274: mdoc_valid_pre(struct mdoc *mdoc,
! 275: const struct mdoc_node *node)
! 276: {
! 277: v_pre *p;
! 278: struct mdoc_arg *argv;
! 279: size_t argc, i, j, line, pos;
! 280: const char *tp;
! 281:
! 282: if (MDOC_TEXT == node->type) {
! 283: tp = node->data.text.string;
! 284: line = node->line;
! 285: pos = node->pos;
! 286: return(check_text(mdoc, line, pos, tp));
! 287: }
! 288:
! 289: if (MDOC_BLOCK == node->type || MDOC_ELEM == node->type) {
! 290: argv = MDOC_BLOCK == node->type ?
! 291: node->data.block.argv :
! 292: node->data.elem.argv;
! 293: argc = MDOC_BLOCK == node->type ?
! 294: node->data.block.argc :
! 295: node->data.elem.argc;
! 296:
! 297: for (i = 0; i < argc; i++) {
! 298: if (0 == argv[i].sz)
! 299: continue;
! 300: for (j = 0; j < argv[i].sz; j++) {
! 301: tp = argv[i].value[j];
! 302: line = argv[i].line;
! 303: pos = argv[i].pos;
! 304: if ( ! check_text(mdoc, line, pos, tp))
! 305: return(0);
! 306: }
! 307: }
! 308: }
! 309:
! 310: if (NULL == mdoc_valids[node->tok].pre)
! 311: return(1);
! 312: for (p = mdoc_valids[node->tok].pre; *p; p++)
! 313: if ( ! (*p)(mdoc, node))
! 314: return(0);
! 315: return(1);
! 316: }
! 317:
! 318:
! 319: int
! 320: mdoc_valid_post(struct mdoc *mdoc)
! 321: {
! 322: v_post *p;
! 323:
! 324: /*
! 325: * This check occurs after the macro's children have been filled
! 326: * in: postfix validation. Since this happens when we're
! 327: * rewinding the scope tree, it's possible to have multiple
! 328: * invocations (as by design, for now), we set bit MDOC_VALID to
! 329: * indicate that we've validated.
! 330: */
! 331:
! 332: if (MDOC_VALID & mdoc->last->flags)
! 333: return(1);
! 334: mdoc->last->flags |= MDOC_VALID;
! 335:
! 336: if (MDOC_TEXT == mdoc->last->type)
! 337: return(1);
! 338: if (MDOC_ROOT == mdoc->last->type)
! 339: return(post_root(mdoc));
! 340:
! 341: if (NULL == mdoc_valids[mdoc->last->tok].post)
! 342: return(1);
! 343: for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
! 344: if ( ! (*p)(mdoc))
! 345: return(0);
! 346:
! 347: return(1);
! 348: }
! 349:
! 350:
! 351:
1.51 kristaps 352: static inline int
353: warn_count(struct mdoc *m, const char *k,
354: int want, const char *v, int has)
355: {
356:
1.55 kristaps 357: return(mdoc_warn(m, WARN_SYNTAX,
358: "suggests %s %d %s (has %d)",
359: v, want, k, has));
1.51 kristaps 360: }
361:
362:
363: static inline int
364: err_count(struct mdoc *m, const char *k,
365: int want, const char *v, int has)
366: {
367:
368: return(mdoc_err(m, "requires %s %d %s (has %d)",
369: v, want, k, has));
370: }
371:
372:
373: static inline int
374: count_child(struct mdoc *mdoc)
1.36 kristaps 375: {
1.51 kristaps 376: int i;
1.36 kristaps 377: struct mdoc_node *n;
378:
379: for (i = 0, n = mdoc->last->child; n; n = n->next, i++)
380: /* Do nothing */ ;
1.53 kristaps 381:
1.36 kristaps 382: return(i);
383: }
384:
385:
1.53 kristaps 386: /*
387: * Build these up with macros because they're basically the same check
388: * for different inequalities. Yes, this could be done with functions,
389: * but this is reasonable for now.
390: */
1.36 kristaps 391:
1.53 kristaps 392: #define CHECK_CHILD_DEFN(lvl, name, ineq) \
393: static int \
394: lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz) \
395: { \
396: int i; \
397: if ((i = count_child(mdoc)) ineq sz) \
398: return(1); \
399: return(lvl##_count(mdoc, #ineq, sz, p, i)); \
400: }
401:
402: #define CHECK_BODY_DEFN(name, lvl, func, num) \
403: static int \
1.55 kristaps 404: b##lvl##_##name(POST_ARGS) \
1.53 kristaps 405: { \
406: if (MDOC_BODY != mdoc->last->type) \
407: return(1); \
408: return(func(mdoc, "multiline parameters", (num))); \
409: }
410:
411: #define CHECK_ELEM_DEFN(name, lvl, func, num) \
412: static int \
1.55 kristaps 413: e##lvl##_##name(POST_ARGS) \
1.53 kristaps 414: { \
415: assert(MDOC_ELEM == mdoc->last->type); \
416: return(func(mdoc, "line parameters", (num))); \
417: }
418:
419: #define CHECK_HEAD_DEFN(name, lvl, func, num) \
420: static int \
1.55 kristaps 421: h##lvl##_##name(POST_ARGS) \
1.53 kristaps 422: { \
423: if (MDOC_HEAD != mdoc->last->type) \
424: return(1); \
425: return(func(mdoc, "multiline parameters", (num))); \
1.36 kristaps 426: }
427:
428:
1.53 kristaps 429: CHECK_CHILD_DEFN(warn, gt, >) /* warn_child_gt() */
430: CHECK_CHILD_DEFN(err, gt, >) /* err_child_gt() */
431: CHECK_CHILD_DEFN(warn, eq, ==) /* warn_child_eq() */
432: CHECK_CHILD_DEFN(err, eq, ==) /* err_child_eq() */
433: CHECK_CHILD_DEFN(err, lt, <) /* err_child_lt() */
434: CHECK_BODY_DEFN(ge1, warn, warn_child_gt, 0) /* bwarn_ge1() */
435: CHECK_ELEM_DEFN(eq1, warn, warn_child_eq, 1) /* ewarn_eq1() */
436: CHECK_ELEM_DEFN(eq0, warn, warn_child_eq, 0) /* ewarn_eq0() */
437: CHECK_ELEM_DEFN(ge1, warn, warn_child_gt, 0) /* ewarn_gt1() */
438: CHECK_ELEM_DEFN(eq1, err, err_child_eq, 1) /* eerr_eq1() */
439: CHECK_ELEM_DEFN(le2, err, err_child_lt, 3) /* eerr_le2() */
440: CHECK_ELEM_DEFN(le1, err, err_child_lt, 2) /* eerr_le1() */
441: CHECK_ELEM_DEFN(eq0, err, err_child_eq, 0) /* eerr_eq0() */
442: CHECK_ELEM_DEFN(ge1, err, err_child_gt, 0) /* eerr_ge1() */
443: CHECK_HEAD_DEFN(eq0, err, err_child_eq, 0) /* herr_eq0() */
444: CHECK_HEAD_DEFN(le1, err, err_child_lt, 2) /* herr_le1() */
445: CHECK_HEAD_DEFN(ge1, err, err_child_gt, 0) /* herr_ge1() */
1.36 kristaps 446:
447:
448: static int
1.55 kristaps 449: check_stdarg(PRE_ARGS)
1.36 kristaps 450: {
451:
1.55 kristaps 452: if (MDOC_Std == n->data.elem.argv[0].arg &&
453: 1 == n->data.elem.argc)
1.36 kristaps 454: return(1);
1.51 kristaps 455:
1.55 kristaps 456: return(mdoc_nwarn(mdoc, n, WARN_COMPAT,
1.53 kristaps 457: "one argument suggested"));
1.36 kristaps 458: }
459:
460:
461: static int
1.55 kristaps 462: check_msec(PRE_ARGS, int sz, enum mdoc_msec *msecs)
1.33 kristaps 463: {
464: int i;
465:
466: for (i = 0; i < sz; i++)
467: if (msecs[i] == mdoc->meta.msec)
468: return(1);
1.55 kristaps 469: return(mdoc_nwarn(mdoc, n, WARN_COMPAT,
470: "invalid manual section"));
471: }
472:
473:
474: static int
475: check_text(struct mdoc *mdoc, size_t line, size_t pos, const char *p)
476: {
477: size_t c;
478:
479: for ( ; *p; p++) {
1.56 kristaps 480: if ( ! isprint(*p) && '\t' != *p)
481: return(mdoc_perr(mdoc, line, pos,
482: "invalid characters"));
1.55 kristaps 483: if ('\\' != *p)
484: continue;
485: if ((c = mdoc_isescape(p))) {
486: p += (c - 1);
487: continue;
488: }
489: return(mdoc_perr(mdoc, line, pos,
1.56 kristaps 490: "invalid escape sequence"));
1.55 kristaps 491: }
492:
493: return(1);
1.14 kristaps 494: }
495:
496:
1.55 kristaps 497:
498:
1.14 kristaps 499: static int
1.55 kristaps 500: check_parent(PRE_ARGS, int tok, enum mdoc_type t)
1.54 kristaps 501: {
502:
503: assert(n->parent);
504: if ((MDOC_ROOT == t || tok == n->parent->tok) &&
505: (t == n->parent->type))
506: return(1);
507:
508: return(mdoc_nerr(mdoc, n, "require parent %s",
509: MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]));
510: }
511:
512:
513:
514: static int
1.55 kristaps 515: pre_display(PRE_ARGS)
1.23 kristaps 516: {
1.55 kristaps 517: struct mdoc_node *node;
1.23 kristaps 518:
1.53 kristaps 519: /* Display elements (`Bd', `D1'...) cannot be nested. */
520:
1.55 kristaps 521: if (MDOC_BLOCK != n->type)
1.24 kristaps 522: return(1);
523:
1.38 kristaps 524: /* LINTED */
1.55 kristaps 525: for (node = mdoc->last->parent; node; node = node->parent)
526: if (MDOC_BLOCK == node->type)
527: if (MDOC_Bd == node->tok)
1.23 kristaps 528: break;
1.55 kristaps 529: if (NULL == node)
1.23 kristaps 530: return(1);
1.53 kristaps 531:
1.55 kristaps 532: return(mdoc_nerr(mdoc, n, "displays may not be nested"));
1.23 kristaps 533: }
534:
535:
536: static int
1.55 kristaps 537: pre_bl(PRE_ARGS)
1.24 kristaps 538: {
1.53 kristaps 539: int type, err, i;
1.24 kristaps 540: struct mdoc_arg *argv;
1.53 kristaps 541: size_t argc;
1.24 kristaps 542:
1.55 kristaps 543: if (MDOC_BLOCK != n->type)
1.24 kristaps 544: return(1);
545:
1.55 kristaps 546: argc = n->data.block.argc;
1.24 kristaps 547:
1.53 kristaps 548: /* Make sure that only one type of list is specified. */
549:
1.38 kristaps 550: /* LINTED */
1.53 kristaps 551: for (i = 0, type = err = 0; i < (int)argc; i++) {
1.55 kristaps 552: argv = &n->data.block.argv[i];
1.53 kristaps 553:
1.24 kristaps 554: switch (argv->arg) {
555: case (MDOC_Bullet):
556: /* FALLTHROUGH */
557: case (MDOC_Dash):
558: /* FALLTHROUGH */
559: case (MDOC_Enum):
560: /* FALLTHROUGH */
561: case (MDOC_Hyphen):
562: /* FALLTHROUGH */
563: case (MDOC_Item):
564: /* FALLTHROUGH */
565: case (MDOC_Tag):
566: /* FALLTHROUGH */
567: case (MDOC_Diag):
568: /* FALLTHROUGH */
569: case (MDOC_Hang):
570: /* FALLTHROUGH */
571: case (MDOC_Ohang):
572: /* FALLTHROUGH */
573: case (MDOC_Inset):
1.26 kristaps 574: /* FALLTHROUGH */
575: case (MDOC_Column):
1.53 kristaps 576: if (0 == type++)
577: break;
578: return(mdoc_perr(mdoc, argv->line, argv->pos,
579: "multiple types specified"));
1.24 kristaps 580: default:
581: break;
582: }
583: }
1.53 kristaps 584:
585: if (type)
586: return(1);
587: return(mdoc_err(mdoc, "no type specified"));
1.24 kristaps 588: }
589:
590:
591: static int
1.55 kristaps 592: pre_bd(PRE_ARGS)
1.24 kristaps 593: {
1.53 kristaps 594: int type, err, i;
1.24 kristaps 595: struct mdoc_arg *argv;
1.53 kristaps 596: size_t argc;
1.24 kristaps 597:
1.55 kristaps 598: if (MDOC_BLOCK != n->type)
1.24 kristaps 599: return(1);
600:
1.55 kristaps 601: argc = n->data.block.argc;
1.24 kristaps 602:
1.53 kristaps 603: /* Make sure that only one type of display is specified. */
604:
1.38 kristaps 605: /* LINTED */
1.53 kristaps 606: for (i = 0, err = type = 0; ! err && i < (int)argc; i++) {
1.55 kristaps 607: argv = &n->data.block.argv[i];
1.53 kristaps 608:
1.24 kristaps 609: switch (argv->arg) {
610: case (MDOC_Ragged):
611: /* FALLTHROUGH */
612: case (MDOC_Unfilled):
613: /* FALLTHROUGH */
1.29 kristaps 614: case (MDOC_Filled):
615: /* FALLTHROUGH */
1.24 kristaps 616: case (MDOC_Literal):
617: /* FALLTHROUGH */
618: case (MDOC_File):
1.53 kristaps 619: if (0 == type++)
620: break;
621: return(mdoc_perr(mdoc, argv->line, argv->pos,
622: "multiple types specified"));
1.24 kristaps 623: default:
624: break;
625: }
626: }
1.53 kristaps 627:
628: if (type)
629: return(1);
630: return(mdoc_err(mdoc, "no type specified"));
1.24 kristaps 631: }
632:
633:
634: static int
1.55 kristaps 635: pre_ss(PRE_ARGS)
1.33 kristaps 636: {
637:
1.55 kristaps 638: if (MDOC_BLOCK != n->type)
1.33 kristaps 639: return(1);
1.55 kristaps 640: return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
1.33 kristaps 641: }
642:
643:
644: static int
1.55 kristaps 645: pre_sh(PRE_ARGS)
1.33 kristaps 646: {
647:
1.55 kristaps 648: if (MDOC_BLOCK != n->type)
1.33 kristaps 649: return(1);
1.55 kristaps 650: return(check_parent(mdoc, n, -1, MDOC_ROOT));
1.33 kristaps 651: }
652:
653:
654: static int
1.55 kristaps 655: pre_it(PRE_ARGS)
1.53 kristaps 656: {
657:
658: /* TODO: -width attribute must be specified for -tag. */
659: /* TODO: children too big for -width? */
660:
1.55 kristaps 661: if (MDOC_BLOCK != n->type)
1.53 kristaps 662: return(1);
1.55 kristaps 663: return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
1.53 kristaps 664: }
665:
666:
667: static int
1.55 kristaps 668: pre_st(PRE_ARGS)
1.36 kristaps 669: {
670:
1.55 kristaps 671: if (1 == n->data.elem.argc)
1.36 kristaps 672: return(1);
1.55 kristaps 673: return(mdoc_nerr(mdoc, n, "one argument required"));
1.36 kristaps 674: }
675:
676:
677: static int
1.55 kristaps 678: pre_an(PRE_ARGS)
1.35 kristaps 679: {
1.36 kristaps 680:
1.55 kristaps 681: if (1 >= n->data.elem.argc)
1.35 kristaps 682: return(1);
1.55 kristaps 683: return(mdoc_nerr(mdoc, n, "one argument allowed"));
1.35 kristaps 684: }
685:
686:
687: static int
1.55 kristaps 688: pre_rv(PRE_ARGS)
1.36 kristaps 689: {
1.53 kristaps 690: enum mdoc_msec msecs[] = { MSEC_2, MSEC_3 };
1.36 kristaps 691:
1.55 kristaps 692: if ( ! check_msec(mdoc, n, 2, msecs))
1.36 kristaps 693: return(0);
1.55 kristaps 694: return(check_stdarg(mdoc, n));
1.36 kristaps 695: }
696:
697:
698: static int
1.55 kristaps 699: pre_ex(PRE_ARGS)
1.33 kristaps 700: {
1.53 kristaps 701: enum mdoc_msec msecs[] = { MSEC_1, MSEC_6, MSEC_8 };
1.35 kristaps 702:
1.55 kristaps 703: if ( ! check_msec(mdoc, n, 3, msecs))
1.35 kristaps 704: return(0);
1.55 kristaps 705: return(check_stdarg(mdoc, n));
1.33 kristaps 706: }
707:
708:
709: static int
1.55 kristaps 710: pre_er(PRE_ARGS)
1.33 kristaps 711: {
1.53 kristaps 712: enum mdoc_msec msecs[] = { MSEC_2 };
1.33 kristaps 713:
1.55 kristaps 714: return(check_msec(mdoc, n, 1, msecs));
1.33 kristaps 715: }
716:
717:
718: static int
1.55 kristaps 719: pre_cd(PRE_ARGS)
1.33 kristaps 720: {
1.53 kristaps 721: enum mdoc_msec msecs[] = { MSEC_4 };
1.33 kristaps 722:
1.55 kristaps 723: return(check_msec(mdoc, n, 1, msecs));
1.33 kristaps 724: }
725:
726:
727: static int
1.55 kristaps 728: pre_prologue(PRE_ARGS)
1.20 kristaps 729: {
730:
1.46 kristaps 731: if (SEC_PROLOGUE != mdoc->lastnamed)
1.55 kristaps 732: return(mdoc_nerr(mdoc, n, "prologue only"));
1.20 kristaps 733:
734: /* Check for ordering. */
735:
1.55 kristaps 736: switch (n->tok) {
1.20 kristaps 737: case (MDOC_Os):
1.36 kristaps 738: if (mdoc->meta.title && mdoc->meta.date)
1.20 kristaps 739: break;
1.55 kristaps 740: return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20 kristaps 741: case (MDOC_Dt):
1.36 kristaps 742: if (NULL == mdoc->meta.title && mdoc->meta.date)
1.20 kristaps 743: break;
1.55 kristaps 744: return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20 kristaps 745: case (MDOC_Dd):
1.36 kristaps 746: if (NULL == mdoc->meta.title && 0 == mdoc->meta.date)
1.20 kristaps 747: break;
1.55 kristaps 748: return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20 kristaps 749: default:
750: abort();
751: /* NOTREACHED */
752: }
753:
754: /* Check for repetition. */
755:
1.55 kristaps 756: switch (n->tok) {
1.20 kristaps 757: case (MDOC_Os):
1.36 kristaps 758: if (NULL == mdoc->meta.os)
1.20 kristaps 759: return(1);
760: break;
761: case (MDOC_Dd):
762: if (0 == mdoc->meta.date)
763: return(1);
764: break;
765: case (MDOC_Dt):
1.36 kristaps 766: if (NULL == mdoc->meta.title)
1.20 kristaps 767: return(1);
768: break;
769: default:
770: abort();
771: /* NOTREACHED */
772: }
773:
1.55 kristaps 774: return(mdoc_nerr(mdoc, n, "prologue repetition"));
1.20 kristaps 775: }
776:
777:
1.35 kristaps 778: static int
1.55 kristaps 779: post_bf(POST_ARGS)
1.41 kristaps 780: {
781: char *p;
782: struct mdoc_node *head;
783:
784: if (MDOC_BLOCK != mdoc->last->type)
785: return(1);
1.53 kristaps 786:
1.41 kristaps 787: head = mdoc->last->data.block.head;
788:
789: if (0 == mdoc->last->data.block.argc) {
1.53 kristaps 790: if (NULL == head->child)
791: return(mdoc_err(mdoc, "argument expected"));
792:
793: p = head->child->data.text.string;
794: if (xstrcmp(p, "Em"))
795: return(1);
796: else if (xstrcmp(p, "Li"))
797: return(1);
798: else if (xstrcmp(p, "Sm"))
799: return(1);
800: return(mdoc_nerr(mdoc, head->child, "invalid font"));
1.41 kristaps 801: }
1.53 kristaps 802:
1.41 kristaps 803: if (head->child)
1.53 kristaps 804: return(mdoc_err(mdoc, "argument expected"));
805:
1.41 kristaps 806: if (1 == mdoc->last->data.block.argc)
807: return(1);
1.53 kristaps 808: return(mdoc_err(mdoc, "argument expected"));
1.41 kristaps 809: }
810:
811:
812: static int
1.55 kristaps 813: post_nm(POST_ARGS)
1.37 kristaps 814: {
815:
816: if (mdoc->last->child)
817: return(1);
818: if (mdoc->meta.name)
819: return(1);
1.53 kristaps 820: return(mdoc_err(mdoc, "not yet invoked with name"));
1.37 kristaps 821: }
822:
823:
824: static int
1.55 kristaps 825: post_xr(POST_ARGS)
1.36 kristaps 826: {
827: struct mdoc_node *n;
828:
829: if (NULL == (n = mdoc->last->child->next))
830: return(1);
831: if (MSEC_DEFAULT != mdoc_atomsec(n->data.text.string))
832: return(1);
833: return(mdoc_nerr(mdoc, n, "invalid manual section"));
834: }
835:
836:
837: static int
1.55 kristaps 838: post_at(POST_ARGS)
1.36 kristaps 839: {
840:
1.37 kristaps 841: if (NULL == mdoc->last->child)
842: return(1);
1.36 kristaps 843: if (ATT_DEFAULT != mdoc_atoatt(mdoc->last->child->data.text.string))
844: return(1);
1.53 kristaps 845: return(mdoc_err(mdoc, "require valid symbol"));
1.36 kristaps 846: }
847:
848:
849: static int
1.55 kristaps 850: post_an(POST_ARGS)
1.35 kristaps 851: {
852:
853: if (0 != mdoc->last->data.elem.argc) {
854: if (NULL == mdoc->last->child)
855: return(1);
1.53 kristaps 856: return(mdoc_err(mdoc, "argument(s) expected"));
1.35 kristaps 857: }
858:
859: if (mdoc->last->child)
860: return(1);
1.53 kristaps 861: return(mdoc_err(mdoc, "argument(s) expected"));
1.35 kristaps 862: }
863:
864:
865: static int
1.55 kristaps 866: post_ex(POST_ARGS)
1.35 kristaps 867: {
868:
869: if (0 == mdoc->last->data.elem.argc) {
870: if (mdoc->last->child)
871: return(1);
1.53 kristaps 872: return(mdoc_err(mdoc, "argument(s) expected"));
1.35 kristaps 873: }
874: if (mdoc->last->child)
1.53 kristaps 875: return(mdoc_err(mdoc, "argument(s) expected"));
1.35 kristaps 876: if (1 != mdoc->last->data.elem.argc)
1.53 kristaps 877: return(mdoc_err(mdoc, "argument(s) expected"));
1.35 kristaps 878: if (MDOC_Std != mdoc->last->data.elem.argv[0].arg)
1.53 kristaps 879: return(mdoc_err(mdoc, "argument(s) expected"));
880:
1.35 kristaps 881: return(1);
882: }
883:
884:
1.25 kristaps 885: static int
1.55 kristaps 886: post_it(POST_ARGS)
1.25 kristaps 887: {
1.53 kristaps 888: int type, sv, i;
1.25 kristaps 889: #define TYPE_NONE (0)
890: #define TYPE_BODY (1)
891: #define TYPE_HEAD (2)
1.47 kristaps 892: #define TYPE_OHEAD (3)
1.53 kristaps 893: size_t argc;
1.25 kristaps 894: struct mdoc_node *n;
895:
896: if (MDOC_BLOCK != mdoc->last->type)
897: return(1);
898:
1.53 kristaps 899: n = mdoc->last->parent->parent;
1.25 kristaps 900:
901: argc = n->data.block.argc;
902: type = TYPE_NONE;
1.38 kristaps 903: sv = -1;
1.26 kristaps 904:
905: /* Some types require block-head, some not. */
1.25 kristaps 906:
1.38 kristaps 907: /* LINTED */
1.53 kristaps 908: for (i = 0; TYPE_NONE == type && i < (int)argc; i++)
909: switch (n->data.block.argv[i].arg) {
1.25 kristaps 910: case (MDOC_Tag):
911: /* FALLTHROUGH */
912: case (MDOC_Diag):
913: /* FALLTHROUGH */
914: case (MDOC_Hang):
915: /* FALLTHROUGH */
916: case (MDOC_Ohang):
917: /* FALLTHROUGH */
918: case (MDOC_Inset):
919: type = TYPE_HEAD;
1.53 kristaps 920: sv = n->data.block.argv[i].arg;
1.25 kristaps 921: break;
922: case (MDOC_Bullet):
923: /* FALLTHROUGH */
924: case (MDOC_Dash):
925: /* FALLTHROUGH */
926: case (MDOC_Enum):
927: /* FALLTHROUGH */
928: case (MDOC_Hyphen):
929: /* FALLTHROUGH */
930: case (MDOC_Item):
1.47 kristaps 931: type = TYPE_BODY;
1.53 kristaps 932: sv = n->data.block.argv[i].arg;
1.47 kristaps 933: break;
1.25 kristaps 934: case (MDOC_Column):
1.47 kristaps 935: type = TYPE_OHEAD;
1.53 kristaps 936: sv = n->data.block.argv[i].arg;
1.25 kristaps 937: break;
938: default:
939: break;
940: }
941:
942: assert(TYPE_NONE != type);
943:
1.47 kristaps 944: n = mdoc->last->data.block.head;
945:
1.25 kristaps 946: if (TYPE_HEAD == type) {
1.33 kristaps 947: if (NULL == n->child)
1.53 kristaps 948: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
949: "argument(s) suggested"))
1.25 kristaps 950: return(0);
951:
1.33 kristaps 952: n = mdoc->last->data.block.body;
953: if (NULL == n->child)
1.53 kristaps 954: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
955: "multiline body suggested"))
1.25 kristaps 956: return(0);
957:
1.47 kristaps 958: } else if (TYPE_BODY == type) {
959: if (n->child)
1.53 kristaps 960: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
961: "no argument suggested"))
1.47 kristaps 962: return(0);
963:
964: n = mdoc->last->data.block.body;
965: if (NULL == n->child)
1.53 kristaps 966: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
967: "multiline body suggested"))
1.47 kristaps 968: return(0);
969: } else {
970: if (NULL == n->child)
1.53 kristaps 971: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
972: "argument(s) suggested"))
1.47 kristaps 973: return(0);
974:
975: n = mdoc->last->data.block.body;
976: if (n->child)
1.53 kristaps 977: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
978: "no multiline body suggested"))
1.47 kristaps 979: return(0);
1.25 kristaps 980: }
981:
1.47 kristaps 982: if (MDOC_Column != sv)
1.26 kristaps 983: return(1);
984:
1.38 kristaps 985: argc = mdoc->last->parent->parent->data.block.argv->sz;
1.26 kristaps 986: n = mdoc->last->data.block.head->child;
1.25 kristaps 987:
1.26 kristaps 988: for (i = 0; n; n = n->next)
989: i++;
990:
1.53 kristaps 991: if (i == (int)argc)
1.26 kristaps 992: return(1);
1.53 kristaps 993:
994: return(mdoc_err(mdoc, "need %zu columns (have %d)", argc, i));
1.25 kristaps 995: #undef TYPE_NONE
996: #undef TYPE_BODY
997: #undef TYPE_HEAD
1.47 kristaps 998: #undef TYPE_OHEAD
1.25 kristaps 999: }
1000:
1001:
1.24 kristaps 1002: static int
1.55 kristaps 1003: post_bl(POST_ARGS)
1.24 kristaps 1004: {
1.53 kristaps 1005: struct mdoc_node *n;
1.24 kristaps 1006:
1007: if (MDOC_BODY != mdoc->last->type)
1008: return(1);
1009:
1.38 kristaps 1010: /* LINTED */
1.24 kristaps 1011: for (n = mdoc->last->child; n; n = n->next) {
1012: if (MDOC_BLOCK == n->type)
1.25 kristaps 1013: if (MDOC_It == n->tok)
1.24 kristaps 1014: continue;
1015: break;
1016: }
1.53 kristaps 1017:
1.24 kristaps 1018: if (NULL == n)
1019: return(1);
1.53 kristaps 1020:
1021: return(mdoc_nerr(mdoc, n, "bad child of parent list"));
1.24 kristaps 1022: }
1023:
1024:
1.34 kristaps 1025: static int
1.37 kristaps 1026: ebool(struct mdoc *mdoc)
1.34 kristaps 1027: {
1028: struct mdoc_node *n;
1029:
1.38 kristaps 1030: /* LINTED */
1.34 kristaps 1031: for (n = mdoc->last->child; n; n = n->next) {
1032: if (MDOC_TEXT != n->type)
1033: break;
1034: if (xstrcmp(n->data.text.string, "on"))
1035: continue;
1036: if (xstrcmp(n->data.text.string, "off"))
1037: continue;
1038: break;
1039: }
1.53 kristaps 1040:
1.34 kristaps 1041: if (NULL == n)
1042: return(1);
1.53 kristaps 1043: return(mdoc_nerr(mdoc, n, "expected boolean"));
1.37 kristaps 1044: }
1045:
1046:
1047: static int
1.55 kristaps 1048: post_root(POST_ARGS)
1.37 kristaps 1049: {
1050:
1.46 kristaps 1051: if (NULL == mdoc->first->child)
1.53 kristaps 1052: return(mdoc_err(mdoc, "document lacks data"));
1.46 kristaps 1053: if (SEC_PROLOGUE == mdoc->lastnamed)
1.53 kristaps 1054: return(mdoc_err(mdoc, "document lacks prologue"));
1055:
1.46 kristaps 1056: if (MDOC_BLOCK != mdoc->first->child->type)
1.54 kristaps 1057: return(mdoc_err(mdoc, "lacking post-prologue %s",
1.53 kristaps 1058: mdoc_macronames[MDOC_Sh]));
1.46 kristaps 1059: if (MDOC_Sh != mdoc->first->child->tok)
1.54 kristaps 1060: return(mdoc_err(mdoc, "lacking post-prologue %s",
1.53 kristaps 1061: mdoc_macronames[MDOC_Sh]));
1062:
1.37 kristaps 1063: return(1);
1.34 kristaps 1064: }
1065:
1066:
1.20 kristaps 1067: static int
1.55 kristaps 1068: post_sh(POST_ARGS)
1.14 kristaps 1069: {
1.46 kristaps 1070:
1071: if (MDOC_HEAD == mdoc->last->type)
1072: return(post_sh_head(mdoc));
1073: if (MDOC_BODY == mdoc->last->type)
1074: return(post_sh_body(mdoc));
1.53 kristaps 1075:
1.46 kristaps 1076: return(1);
1077: }
1078:
1079:
1080: static int
1.55 kristaps 1081: post_sh_body(POST_ARGS)
1.46 kristaps 1082: {
1083: struct mdoc_node *n;
1084:
1085: if (SEC_NAME != mdoc->lastnamed)
1086: return(1);
1087:
1.51 kristaps 1088: /*
1089: * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1090: * macros (can have multiple `Nm' and one `Nd'). Note that the
1091: * children of the BODY declaration can also be "text".
1092: */
1093:
1.46 kristaps 1094: if (NULL == (n = mdoc->last->child))
1.54 kristaps 1095: return(mdoc_warn(mdoc, WARN_SYNTAX,
1096: "section should have %s and %s",
1.51 kristaps 1097: mdoc_macronames[MDOC_Nm],
1098: mdoc_macronames[MDOC_Nd]));
1099:
1100: for ( ; n && n->next; n = n->next) {
1101: if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1102: continue;
1103: if (MDOC_TEXT == n->type)
1104: continue;
1.54 kristaps 1105: if ( ! (mdoc_nwarn(mdoc, n, WARN_SYNTAX,
1106: "section should have %s first",
1.51 kristaps 1107: mdoc_macronames[MDOC_Nm])))
1108: return(0);
1109: }
1110:
1111: if (MDOC_ELEM == n->type && MDOC_Nd == n->tok)
1.46 kristaps 1112: return(1);
1113:
1.54 kristaps 1114: return(mdoc_warn(mdoc, WARN_SYNTAX,
1115: "section should have %s last",
1.51 kristaps 1116: mdoc_macronames[MDOC_Nd]));
1.46 kristaps 1117: }
1118:
1119:
1120: static int
1.55 kristaps 1121: post_sh_head(POST_ARGS)
1.46 kristaps 1122: {
1.36 kristaps 1123: char buf[64];
1.21 kristaps 1124: enum mdoc_sec sec;
1125:
1.25 kristaps 1126: assert(MDOC_Sh == mdoc->last->tok);
1.21 kristaps 1127:
1.54 kristaps 1128: if ( ! xstrlcats(buf, mdoc->last->child, sizeof(buf)))
1129: return(mdoc_err(mdoc, "argument too long"));
1.14 kristaps 1130:
1.46 kristaps 1131: sec = mdoc_atosec(buf);
1132:
1133: if (SEC_BODY == mdoc->lastnamed && SEC_NAME != sec)
1.54 kristaps 1134: return(mdoc_warn(mdoc, WARN_SYNTAX,
1135: "section NAME should be first"));
1.46 kristaps 1136: if (SEC_CUSTOM == sec)
1.21 kristaps 1137: return(1);
1.46 kristaps 1138: if (sec == mdoc->lastnamed)
1.54 kristaps 1139: return(mdoc_warn(mdoc, WARN_SYNTAX,
1140: "section repeated"));
1.46 kristaps 1141: if (sec < mdoc->lastnamed)
1.54 kristaps 1142: return(mdoc_warn(mdoc, WARN_SYNTAX,
1143: "section out of order"));
1.46 kristaps 1144:
1145: return(1);
1.11 kristaps 1146: }
1147:
1148:
1.57 ! kristaps 1149: static int
! 1150: post_fd(POST_ARGS)
1.11 kristaps 1151: {
1.39 kristaps 1152:
1.57 ! kristaps 1153: if (SEC_SYNOPSIS == mdoc->last->sec)
1.25 kristaps 1154: return(1);
1.57 ! kristaps 1155: return(mdoc_warn(mdoc, WARN_COMPAT,
! 1156: "suggested only in section SYNOPSIS"));
1.11 kristaps 1157: }
CVSweb