Annotation of src/usr.bin/mandoc/man_validate.c, Revision 1.25
1.25 ! schwarze 1: /* $Id: man_validate.c,v 1.24 2010/05/23 20:57:16 schwarze Exp $ */
1.1 kristaps 2: /*
1.2 schwarze 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
17: #include <sys/types.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
1.6 schwarze 21: #include <errno.h>
22: #include <limits.h>
1.1 kristaps 23: #include <stdarg.h>
24: #include <stdlib.h>
25:
1.25 ! schwarze 26: #include "mandoc.h"
1.1 kristaps 27: #include "libman.h"
1.5 schwarze 28: #include "libmandoc.h"
1.1 kristaps 29:
1.7 schwarze 30: #define CHKARGS struct man *m, const struct man_node *n
1.1 kristaps 31:
1.7 schwarze 32: typedef int (*v_check)(CHKARGS);
1.1 kristaps 33:
34: struct man_valid {
1.7 schwarze 35: v_check *pres;
36: v_check *posts;
1.1 kristaps 37: };
38:
1.7 schwarze 39: static int check_bline(CHKARGS);
40: static int check_eq0(CHKARGS);
1.10 schwarze 41: static int check_le1(CHKARGS);
1.7 schwarze 42: static int check_ge2(CHKARGS);
43: static int check_le5(CHKARGS);
44: static int check_par(CHKARGS);
1.8 schwarze 45: static int check_part(CHKARGS);
1.7 schwarze 46: static int check_root(CHKARGS);
47: static int check_sec(CHKARGS);
48: static int check_text(CHKARGS);
1.15 schwarze 49: static int check_title(CHKARGS);
1.7 schwarze 50:
51: static v_check posts_eq0[] = { check_eq0, NULL };
1.15 schwarze 52: static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL };
1.7 schwarze 53: static v_check posts_par[] = { check_par, NULL };
1.8 schwarze 54: static v_check posts_part[] = { check_part, NULL };
1.7 schwarze 55: static v_check posts_sec[] = { check_sec, NULL };
1.13 schwarze 56: static v_check posts_le1[] = { check_le1, NULL };
1.7 schwarze 57: static v_check pres_bline[] = { check_bline, NULL };
1.1 kristaps 58:
59: static const struct man_valid man_valids[MAN_MAX] = {
1.12 schwarze 60: { NULL, posts_eq0 }, /* br */
1.15 schwarze 61: { pres_bline, posts_th }, /* TH */
1.7 schwarze 62: { pres_bline, posts_sec }, /* SH */
63: { pres_bline, posts_sec }, /* SS */
64: { pres_bline, posts_par }, /* TP */
65: { pres_bline, posts_par }, /* LP */
66: { pres_bline, posts_par }, /* PP */
67: { pres_bline, posts_par }, /* P */
68: { pres_bline, posts_par }, /* IP */
69: { pres_bline, posts_par }, /* HP */
1.8 schwarze 70: { NULL, NULL }, /* SM */
71: { NULL, NULL }, /* SB */
1.7 schwarze 72: { NULL, NULL }, /* BI */
73: { NULL, NULL }, /* IB */
74: { NULL, NULL }, /* BR */
75: { NULL, NULL }, /* RB */
1.8 schwarze 76: { NULL, NULL }, /* R */
77: { NULL, NULL }, /* B */
78: { NULL, NULL }, /* I */
1.7 schwarze 79: { NULL, NULL }, /* IR */
80: { NULL, NULL }, /* RI */
1.12 schwarze 81: { NULL, posts_eq0 }, /* na */
1.7 schwarze 82: { NULL, NULL }, /* i */
1.13 schwarze 83: { NULL, posts_le1 }, /* sp */
1.7 schwarze 84: { pres_bline, posts_eq0 }, /* nf */
85: { pres_bline, posts_eq0 }, /* fi */
86: { NULL, NULL }, /* r */
1.8 schwarze 87: { NULL, NULL }, /* RE */
88: { NULL, posts_part }, /* RS */
89: { NULL, NULL }, /* DT */
1.9 schwarze 90: { NULL, NULL }, /* UC */
1.11 schwarze 91: { NULL, NULL }, /* PD */
1.13 schwarze 92: { NULL, posts_le1 }, /* Sp */
93: { pres_bline, posts_le1 }, /* Vb */
94: { pres_bline, posts_eq0 }, /* Ve */
1.24 schwarze 95: { NULL, NULL }, /* AT */
1.1 kristaps 96: };
97:
98:
99: int
1.7 schwarze 100: man_valid_pre(struct man *m, const struct man_node *n)
101: {
102: v_check *cp;
103:
104: if (MAN_TEXT == n->type)
105: return(1);
106: if (MAN_ROOT == n->type)
107: return(1);
108:
109: if (NULL == (cp = man_valids[n->tok].pres))
110: return(1);
111: for ( ; *cp; cp++)
112: if ( ! (*cp)(m, n))
113: return(0);
114: return(1);
115: }
116:
117:
118: int
1.1 kristaps 119: man_valid_post(struct man *m)
120: {
1.7 schwarze 121: v_check *cp;
1.1 kristaps 122:
123: if (MAN_VALID & m->last->flags)
124: return(1);
125: m->last->flags |= MAN_VALID;
126:
127: switch (m->last->type) {
128: case (MAN_TEXT):
1.4 schwarze 129: return(check_text(m, m->last));
1.1 kristaps 130: case (MAN_ROOT):
1.4 schwarze 131: return(check_root(m, m->last));
1.1 kristaps 132: default:
133: break;
134: }
135:
136: if (NULL == (cp = man_valids[m->last->tok].posts))
137: return(1);
138: for ( ; *cp; cp++)
139: if ( ! (*cp)(m, m->last))
140: return(0);
141:
142: return(1);
143: }
144:
145:
1.4 schwarze 146: static int
1.7 schwarze 147: check_root(CHKARGS)
1.4 schwarze 148: {
1.7 schwarze 149:
150: if (MAN_BLINE & m->flags)
1.25 ! schwarze 151: return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
1.7 schwarze 152: if (MAN_ELINE & m->flags)
1.25 ! schwarze 153: return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
1.8 schwarze 154:
155: m->flags &= ~MAN_BLINE;
156: m->flags &= ~MAN_ELINE;
1.7 schwarze 157:
1.25 ! schwarze 158: if (NULL == m->first->child) {
! 159: man_nmsg(m, n, MANDOCERR_NODOCBODY);
! 160: return(0);
! 161: } else if (NULL == m->meta.title) {
! 162: if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE))
1.17 schwarze 163: return(0);
1.18 schwarze 164: /*
165: * If a title hasn't been set, do so now (by
166: * implication, date and section also aren't set).
167: *
168: * FIXME: this should be in man_action.c.
169: */
1.17 schwarze 170: m->meta.title = mandoc_strdup("unknown");
1.18 schwarze 171: m->meta.date = time(NULL);
1.21 schwarze 172: m->meta.msec = mandoc_strdup("1");
1.17 schwarze 173: }
1.15 schwarze 174:
175: return(1);
176: }
177:
178:
179: static int
180: check_title(CHKARGS)
181: {
182: const char *p;
183:
184: assert(n->child);
1.25 ! schwarze 185: /* FIXME: is this sufficient? */
! 186: if ('\0' == *n->child->string) {
! 187: man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
! 188: return(0);
! 189: }
1.15 schwarze 190:
191: for (p = n->child->string; '\0' != *p; p++)
192: if (isalpha((u_char)*p) && ! isupper((u_char)*p))
1.25 ! schwarze 193: if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE))
1.15 schwarze 194: return(0);
1.4 schwarze 195:
196: return(1);
197: }
198:
199:
200: static int
1.7 schwarze 201: check_text(CHKARGS)
1.4 schwarze 202: {
203: const char *p;
1.5 schwarze 204: int pos, c;
1.4 schwarze 205:
206: assert(n->string);
207:
208: for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
1.5 schwarze 209: if ('\\' == *p) {
210: c = mandoc_special(p);
211: if (c) {
212: p += c - 1;
213: pos += c - 1;
214: continue;
215: }
1.25 ! schwarze 216:
! 217: c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
! 218: if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c)
! 219: return(c);
1.5 schwarze 220: }
221:
222: if ('\t' == *p || isprint((u_char)*p))
1.4 schwarze 223: continue;
1.25 ! schwarze 224: if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR))
! 225: return(0);
1.4 schwarze 226: }
227:
228: return(1);
1.1 kristaps 229: }
230:
231:
232: #define INEQ_DEFINE(x, ineq, name) \
233: static int \
1.7 schwarze 234: check_##name(CHKARGS) \
1.1 kristaps 235: { \
1.4 schwarze 236: if (n->nchild ineq (x)) \
1.1 kristaps 237: return(1); \
1.25 ! schwarze 238: man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
! 239: "line arguments %s %d (have %d)", \
! 240: #ineq, (x), n->nchild); \
! 241: return(0); \
1.1 kristaps 242: }
243:
244: INEQ_DEFINE(0, ==, eq0)
1.10 schwarze 245: INEQ_DEFINE(1, <=, le1)
1.1 kristaps 246: INEQ_DEFINE(2, >=, ge2)
247: INEQ_DEFINE(5, <=, le5)
1.7 schwarze 248:
249:
250: static int
251: check_sec(CHKARGS)
252: {
1.6 schwarze 253:
1.25 ! schwarze 254: if (MAN_HEAD == n->type && 0 == n->nchild) {
! 255: man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
! 256: return(0);
! 257: } else if (MAN_BODY == n->type && 0 == n->nchild)
! 258: return(man_nmsg(m, n, MANDOCERR_NOBODY));
! 259:
1.6 schwarze 260: return(1);
261: }
1.7 schwarze 262:
263:
264: static int
1.8 schwarze 265: check_part(CHKARGS)
266: {
267:
268: if (MAN_BODY == n->type && 0 == n->nchild)
1.25 ! schwarze 269: return(man_nmsg(m, n, MANDOCERR_NOBODY));
1.8 schwarze 270: return(1);
271: }
272:
273:
274: static int
1.7 schwarze 275: check_par(CHKARGS)
276: {
277:
278: if (MAN_BODY == n->type)
279: switch (n->tok) {
280: case (MAN_IP):
281: /* FALLTHROUGH */
282: case (MAN_HP):
283: /* FALLTHROUGH */
284: case (MAN_TP):
285: /* Body-less lists are ok. */
286: break;
287: default:
288: if (n->nchild)
289: break;
1.25 ! schwarze 290: return(man_nmsg(m, n, MANDOCERR_NOBODY));
1.7 schwarze 291: }
292: if (MAN_HEAD == n->type)
293: switch (n->tok) {
294: case (MAN_PP):
295: /* FALLTHROUGH */
296: case (MAN_P):
297: /* FALLTHROUGH */
298: case (MAN_LP):
299: if (0 == n->nchild)
300: break;
1.25 ! schwarze 301: return(man_nmsg(m, n, MANDOCERR_ARGSLOST));
1.7 schwarze 302: default:
303: if (n->nchild)
304: break;
1.25 ! schwarze 305: return(man_nmsg(m, n, MANDOCERR_NOARGS));
1.7 schwarze 306: }
307:
308: return(1);
309: }
310:
311:
312: static int
313: check_bline(CHKARGS)
314: {
315:
1.8 schwarze 316: assert( ! (MAN_ELINE & m->flags));
1.25 ! schwarze 317: if (MAN_BLINE & m->flags) {
! 318: man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
! 319: return(0);
! 320: }
1.14 schwarze 321:
1.7 schwarze 322: return(1);
323: }
324: