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