]> git.cameronkatri.com Git - mandoc.git/blob - man_validate.c
In the SYNOPSIS, .Nm at the beginning of an input line starts
[mandoc.git] / man_validate.c
1 /* $Id: man_validate.c,v 1.46 2010/07/20 14:56:42 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
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 above
7 * copyright notice and this permission notice appear in all copies.
8 *
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.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "mandoc.h"
32 #include "libman.h"
33 #include "libmandoc.h"
34
35 #define CHKARGS struct man *m, struct man_node *n
36
37 typedef int (*v_check)(CHKARGS);
38
39 struct man_valid {
40 v_check *pres;
41 v_check *posts;
42 };
43
44 static int check_bline(CHKARGS);
45 static int check_eq0(CHKARGS);
46 static int check_le1(CHKARGS);
47 static int check_ge2(CHKARGS);
48 static int check_le5(CHKARGS);
49 static int check_par(CHKARGS);
50 static int check_part(CHKARGS);
51 static int check_root(CHKARGS);
52 static int check_sec(CHKARGS);
53 static int check_text(CHKARGS);
54 static int check_title(CHKARGS);
55
56 static v_check posts_eq0[] = { check_eq0, NULL };
57 static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL };
58 static v_check posts_par[] = { check_par, NULL };
59 static v_check posts_part[] = { check_part, NULL };
60 static v_check posts_sec[] = { check_sec, NULL };
61 static v_check posts_le1[] = { check_le1, NULL };
62 static v_check pres_bline[] = { check_bline, NULL };
63
64 static const struct man_valid man_valids[MAN_MAX] = {
65 { NULL, posts_eq0 }, /* br */
66 { pres_bline, posts_th }, /* TH */
67 { pres_bline, posts_sec }, /* SH */
68 { pres_bline, posts_sec }, /* SS */
69 { pres_bline, posts_par }, /* TP */
70 { pres_bline, posts_par }, /* LP */
71 { pres_bline, posts_par }, /* PP */
72 { pres_bline, posts_par }, /* P */
73 { pres_bline, posts_par }, /* IP */
74 { pres_bline, posts_par }, /* HP */
75 { NULL, NULL }, /* SM */
76 { NULL, NULL }, /* SB */
77 { NULL, NULL }, /* BI */
78 { NULL, NULL }, /* IB */
79 { NULL, NULL }, /* BR */
80 { NULL, NULL }, /* RB */
81 { NULL, NULL }, /* R */
82 { NULL, NULL }, /* B */
83 { NULL, NULL }, /* I */
84 { NULL, NULL }, /* IR */
85 { NULL, NULL }, /* RI */
86 { NULL, posts_eq0 }, /* na */
87 { NULL, NULL }, /* i */
88 { NULL, posts_le1 }, /* sp */
89 { pres_bline, posts_eq0 }, /* nf */
90 { pres_bline, posts_eq0 }, /* fi */
91 { NULL, NULL }, /* r */
92 { NULL, NULL }, /* RE */
93 { NULL, posts_part }, /* RS */
94 { NULL, NULL }, /* DT */
95 { NULL, NULL }, /* UC */
96 { NULL, NULL }, /* PD */
97 { NULL, posts_le1 }, /* Sp */
98 { pres_bline, posts_le1 }, /* Vb */
99 { pres_bline, posts_eq0 }, /* Ve */
100 { NULL, NULL }, /* AT */
101 };
102
103
104 int
105 man_valid_pre(struct man *m, struct man_node *n)
106 {
107 v_check *cp;
108
109 if (MAN_TEXT == n->type)
110 return(1);
111 if (MAN_ROOT == n->type)
112 return(1);
113
114 if (NULL == (cp = man_valids[n->tok].pres))
115 return(1);
116 for ( ; *cp; cp++)
117 if ( ! (*cp)(m, n))
118 return(0);
119 return(1);
120 }
121
122
123 int
124 man_valid_post(struct man *m)
125 {
126 v_check *cp;
127
128 if (MAN_VALID & m->last->flags)
129 return(1);
130 m->last->flags |= MAN_VALID;
131
132 switch (m->last->type) {
133 case (MAN_TEXT):
134 return(check_text(m, m->last));
135 case (MAN_ROOT):
136 return(check_root(m, m->last));
137 default:
138 break;
139 }
140
141 if (NULL == (cp = man_valids[m->last->tok].posts))
142 return(1);
143 for ( ; *cp; cp++)
144 if ( ! (*cp)(m, m->last))
145 return(0);
146
147 return(1);
148 }
149
150
151 static int
152 check_root(CHKARGS)
153 {
154
155 if (MAN_BLINE & m->flags)
156 return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
157 if (MAN_ELINE & m->flags)
158 return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
159
160 m->flags &= ~MAN_BLINE;
161 m->flags &= ~MAN_ELINE;
162
163 if (NULL == m->first->child) {
164 man_nmsg(m, n, MANDOCERR_NODOCBODY);
165 return(0);
166 } else if (NULL == m->meta.title) {
167 if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE))
168 return(0);
169 /*
170 * If a title hasn't been set, do so now (by
171 * implication, date and section also aren't set).
172 *
173 * FIXME: this should be in man_action.c.
174 */
175 m->meta.title = mandoc_strdup("unknown");
176 m->meta.date = time(NULL);
177 m->meta.msec = mandoc_strdup("1");
178 }
179
180 return(1);
181 }
182
183
184 static int
185 check_title(CHKARGS)
186 {
187 const char *p;
188
189 assert(n->child);
190 /* FIXME: is this sufficient? */
191 if ('\0' == *n->child->string) {
192 man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
193 return(0);
194 }
195
196 for (p = n->child->string; '\0' != *p; p++)
197 if (isalpha((u_char)*p) && ! isupper((u_char)*p))
198 if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE))
199 return(0);
200
201 return(1);
202 }
203
204
205 static int
206 check_text(CHKARGS)
207 {
208 char *p;
209 int pos, c;
210 size_t sz;
211
212 for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
213 sz = strcspn(p, "\t\\");
214 p += (int)sz;
215
216 if ('\0' == *p)
217 break;
218
219 pos += (int)sz;
220
221 if ('\t' == *p) {
222 if (MAN_LITERAL & m->flags)
223 continue;
224 if (man_pmsg(m, n->line, pos, MANDOCERR_BADTAB))
225 continue;
226 return(0);
227 }
228
229 /* Check the special character. */
230
231 c = mandoc_special(p);
232 if (c) {
233 p += c - 1;
234 pos += c - 1;
235 continue;
236 }
237
238 c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
239 if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c)
240 return(c);
241 }
242
243 return(1);
244 }
245
246
247 #define INEQ_DEFINE(x, ineq, name) \
248 static int \
249 check_##name(CHKARGS) \
250 { \
251 if (n->nchild ineq (x)) \
252 return(1); \
253 man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
254 "line arguments %s %d (have %d)", \
255 #ineq, (x), n->nchild); \
256 return(0); \
257 }
258
259 INEQ_DEFINE(0, ==, eq0)
260 INEQ_DEFINE(1, <=, le1)
261 INEQ_DEFINE(2, >=, ge2)
262 INEQ_DEFINE(5, <=, le5)
263
264
265 static int
266 check_sec(CHKARGS)
267 {
268
269 if (MAN_HEAD == n->type && 0 == n->nchild) {
270 man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
271 return(0);
272 } else if (MAN_BODY == n->type && 0 == n->nchild)
273 return(man_nmsg(m, n, MANDOCERR_NOBODY));
274
275 return(1);
276 }
277
278
279 static int
280 check_part(CHKARGS)
281 {
282
283 if (MAN_BODY == n->type && 0 == n->nchild)
284 return(man_nmsg(m, n, MANDOCERR_NOBODY));
285 return(1);
286 }
287
288
289 static int
290 check_par(CHKARGS)
291 {
292
293 if (MAN_BODY == n->type)
294 switch (n->tok) {
295 case (MAN_IP):
296 /* FALLTHROUGH */
297 case (MAN_HP):
298 /* FALLTHROUGH */
299 case (MAN_TP):
300 /* Body-less lists are ok. */
301 break;
302 default:
303 if (n->nchild)
304 break;
305 return(man_nmsg(m, n, MANDOCERR_NOBODY));
306 }
307 if (MAN_HEAD == n->type)
308 switch (n->tok) {
309 case (MAN_PP):
310 /* FALLTHROUGH */
311 case (MAN_P):
312 /* FALLTHROUGH */
313 case (MAN_LP):
314 if (0 == n->nchild)
315 break;
316 return(man_nmsg(m, n, MANDOCERR_ARGSLOST));
317 default:
318 if (n->nchild)
319 break;
320 return(man_nmsg(m, n, MANDOCERR_NOARGS));
321 }
322
323 return(1);
324 }
325
326
327 static int
328 check_bline(CHKARGS)
329 {
330
331 assert( ! (MAN_ELINE & m->flags));
332 if (MAN_BLINE & m->flags) {
333 man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
334 return(0);
335 }
336
337 return(1);
338 }
339