]> git.cameronkatri.com Git - mandoc.git/blob - man_validate.c
Allow -man to process EQN as well. Also fix a segfault in missing case
[mandoc.git] / man_validate.c
1 /* $Id: man_validate.c,v 1.62 2011/02/09 09:18:15 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010 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 #include <time.h>
31
32 #include "mandoc.h"
33 #include "libman.h"
34 #include "libmandoc.h"
35
36 #define CHKARGS struct man *m, struct man_node *n
37
38 typedef int (*v_check)(CHKARGS);
39
40 struct man_valid {
41 v_check *pres;
42 v_check *posts;
43 };
44
45 static int check_bline(CHKARGS);
46 static int check_eq0(CHKARGS);
47 static int check_ft(CHKARGS);
48 static int check_le1(CHKARGS);
49 static int check_ge2(CHKARGS);
50 static int check_le5(CHKARGS);
51 static int check_par(CHKARGS);
52 static int check_part(CHKARGS);
53 static int check_root(CHKARGS);
54 static int check_sec(CHKARGS);
55 static int check_text(CHKARGS);
56
57 static int post_AT(CHKARGS);
58 static int post_fi(CHKARGS);
59 static int post_nf(CHKARGS);
60 static int post_TH(CHKARGS);
61 static int post_UC(CHKARGS);
62
63 static v_check posts_at[] = { post_AT, NULL };
64 static v_check posts_eq0[] = { check_eq0, NULL };
65 static v_check posts_fi[] = { check_eq0, post_fi, NULL };
66 static v_check posts_le1[] = { check_le1, NULL };
67 static v_check posts_ft[] = { check_ft, NULL };
68 static v_check posts_nf[] = { check_eq0, post_nf, NULL };
69 static v_check posts_par[] = { check_par, NULL };
70 static v_check posts_part[] = { check_part, NULL };
71 static v_check posts_sec[] = { check_sec, NULL };
72 static v_check posts_th[] = { check_ge2, check_le5, post_TH, NULL };
73 static v_check posts_uc[] = { post_UC, NULL };
74 static v_check pres_bline[] = { check_bline, NULL };
75
76
77 static const struct man_valid man_valids[MAN_MAX] = {
78 { NULL, posts_eq0 }, /* br */
79 { pres_bline, posts_th }, /* TH */
80 { pres_bline, posts_sec }, /* SH */
81 { pres_bline, posts_sec }, /* SS */
82 { pres_bline, NULL }, /* TP */
83 { pres_bline, posts_par }, /* LP */
84 { pres_bline, posts_par }, /* PP */
85 { pres_bline, posts_par }, /* P */
86 { pres_bline, NULL }, /* IP */
87 { pres_bline, NULL }, /* HP */
88 { NULL, NULL }, /* SM */
89 { NULL, NULL }, /* SB */
90 { NULL, NULL }, /* BI */
91 { NULL, NULL }, /* IB */
92 { NULL, NULL }, /* BR */
93 { NULL, NULL }, /* RB */
94 { NULL, NULL }, /* R */
95 { NULL, NULL }, /* B */
96 { NULL, NULL }, /* I */
97 { NULL, NULL }, /* IR */
98 { NULL, NULL }, /* RI */
99 { NULL, posts_eq0 }, /* na */ /* FIXME: should warn only. */
100 { NULL, posts_le1 }, /* sp */ /* FIXME: should warn only. */
101 { pres_bline, posts_nf }, /* nf */
102 { pres_bline, posts_fi }, /* fi */
103 { NULL, NULL }, /* RE */
104 { NULL, posts_part }, /* RS */
105 { NULL, NULL }, /* DT */
106 { NULL, posts_uc }, /* UC */
107 { NULL, NULL }, /* PD */
108 { NULL, posts_at }, /* AT */
109 { NULL, NULL }, /* in */
110 { NULL, posts_ft }, /* ft */
111 };
112
113
114 int
115 man_valid_pre(struct man *m, struct man_node *n)
116 {
117 v_check *cp;
118
119 switch (n->type) {
120 case (MAN_TEXT):
121 /* FALLTHROUGH */
122 case (MAN_ROOT):
123 /* FALLTHROUGH */
124 case (MAN_EQN):
125 /* FALLTHROUGH */
126 case (MAN_TBL):
127 return(1);
128 default:
129 break;
130 }
131
132 if (NULL == (cp = man_valids[n->tok].pres))
133 return(1);
134 for ( ; *cp; cp++)
135 if ( ! (*cp)(m, n))
136 return(0);
137 return(1);
138 }
139
140
141 int
142 man_valid_post(struct man *m)
143 {
144 v_check *cp;
145
146 if (MAN_VALID & m->last->flags)
147 return(1);
148 m->last->flags |= MAN_VALID;
149
150 switch (m->last->type) {
151 case (MAN_TEXT):
152 return(check_text(m, m->last));
153 case (MAN_ROOT):
154 return(check_root(m, m->last));
155 case (MAN_EQN):
156 /* FALLTHROUGH */
157 case (MAN_TBL):
158 return(1);
159 default:
160 break;
161 }
162
163 if (NULL == (cp = man_valids[m->last->tok].posts))
164 return(1);
165 for ( ; *cp; cp++)
166 if ( ! (*cp)(m, m->last))
167 return(0);
168
169 return(1);
170 }
171
172
173 static int
174 check_root(CHKARGS)
175 {
176
177 if (MAN_BLINE & m->flags)
178 man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
179 else if (MAN_ELINE & m->flags)
180 man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
181
182 m->flags &= ~MAN_BLINE;
183 m->flags &= ~MAN_ELINE;
184
185 if (NULL == m->first->child) {
186 man_nmsg(m, n, MANDOCERR_NODOCBODY);
187 return(0);
188 } else if (NULL == m->meta.title) {
189 man_nmsg(m, n, MANDOCERR_NOTITLE);
190
191 /*
192 * If a title hasn't been set, do so now (by
193 * implication, date and section also aren't set).
194 */
195
196 m->meta.title = mandoc_strdup("unknown");
197 m->meta.date = time(NULL);
198 m->meta.msec = mandoc_strdup("1");
199 }
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 } else
236 man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
237 }
238
239 return(1);
240 }
241
242
243 #define INEQ_DEFINE(x, ineq, name) \
244 static int \
245 check_##name(CHKARGS) \
246 { \
247 if (n->nchild ineq (x)) \
248 return(1); \
249 man_vmsg(m, MANDOCERR_ARGCOUNT, n->line, n->pos, \
250 "line arguments %s %d (have %d)", \
251 #ineq, (x), n->nchild); \
252 return(1); \
253 }
254
255 INEQ_DEFINE(0, ==, eq0)
256 INEQ_DEFINE(1, <=, le1)
257 INEQ_DEFINE(2, >=, ge2)
258 INEQ_DEFINE(5, <=, le5)
259
260 static int
261 check_ft(CHKARGS)
262 {
263 char *cp;
264 int ok;
265
266 if (0 == n->nchild)
267 return(1);
268
269 ok = 0;
270 cp = n->child->string;
271 switch (*cp) {
272 case ('1'):
273 /* FALLTHROUGH */
274 case ('2'):
275 /* FALLTHROUGH */
276 case ('3'):
277 /* FALLTHROUGH */
278 case ('4'):
279 /* FALLTHROUGH */
280 case ('I'):
281 /* FALLTHROUGH */
282 case ('P'):
283 /* FALLTHROUGH */
284 case ('R'):
285 if ('\0' == cp[1])
286 ok = 1;
287 break;
288 case ('B'):
289 if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
290 ok = 1;
291 break;
292 case ('C'):
293 if ('W' == cp[1] && '\0' == cp[2])
294 ok = 1;
295 break;
296 default:
297 break;
298 }
299
300 if (0 == ok) {
301 man_vmsg(m, MANDOCERR_BADFONT,
302 n->line, n->pos, "%s", cp);
303 *cp = '\0';
304 }
305
306 if (1 < n->nchild)
307 man_vmsg(m, MANDOCERR_ARGCOUNT, n->line, n->pos,
308 "want one child (have %d)", n->nchild);
309
310 return(1);
311 }
312
313 static int
314 check_sec(CHKARGS)
315 {
316
317 if (MAN_HEAD == n->type && 0 == n->nchild) {
318 man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
319 return(0);
320 } else if (MAN_BODY == n->type && 0 == n->nchild)
321 man_nmsg(m, n, MANDOCERR_NOBODY);
322
323 return(1);
324 }
325
326
327 static int
328 check_part(CHKARGS)
329 {
330
331 if (MAN_BODY == n->type && 0 == n->nchild)
332 man_nmsg(m, n, MANDOCERR_NOBODY);
333
334 return(1);
335 }
336
337
338 static int
339 check_par(CHKARGS)
340 {
341
342 switch (n->type) {
343 case (MAN_BLOCK):
344 if (0 == n->body->nchild)
345 man_node_delete(m, n);
346 break;
347 case (MAN_BODY):
348 if (0 == n->nchild)
349 man_nmsg(m, n, MANDOCERR_IGNPAR);
350 break;
351 case (MAN_HEAD):
352 if (n->nchild)
353 man_nmsg(m, n, MANDOCERR_ARGSLOST);
354 break;
355 default:
356 break;
357 }
358
359 return(1);
360 }
361
362
363 static int
364 check_bline(CHKARGS)
365 {
366
367 assert( ! (MAN_ELINE & m->flags));
368 if (MAN_BLINE & m->flags) {
369 man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
370 return(0);
371 }
372
373 return(1);
374 }
375
376 static int
377 post_TH(CHKARGS)
378 {
379 const char *p;
380
381 if (m->meta.title)
382 free(m->meta.title);
383 if (m->meta.vol)
384 free(m->meta.vol);
385 if (m->meta.source)
386 free(m->meta.source);
387 if (m->meta.msec)
388 free(m->meta.msec);
389 if (m->meta.rawdate)
390 free(m->meta.rawdate);
391
392 m->meta.title = m->meta.vol = m->meta.rawdate =
393 m->meta.msec = m->meta.source = NULL;
394 m->meta.date = 0;
395
396 /* ->TITLE<- MSEC DATE SOURCE VOL */
397
398 n = n->child;
399 if (n && n->string) {
400 for (p = n->string; '\0' != *p; p++) {
401 /* Only warn about this once... */
402 if (isalpha((u_char)*p) && ! isupper((u_char)*p)) {
403 man_nmsg(m, n, MANDOCERR_UPPERCASE);
404 break;
405 }
406 }
407 m->meta.title = mandoc_strdup(n->string);
408 } else
409 m->meta.title = mandoc_strdup("");
410
411 /* TITLE ->MSEC<- DATE SOURCE VOL */
412
413 if (n)
414 n = n->next;
415 if (n && n->string)
416 m->meta.msec = mandoc_strdup(n->string);
417 else
418 m->meta.msec = mandoc_strdup("");
419
420 /* TITLE MSEC ->DATE<- SOURCE VOL */
421
422 /*
423 * Try to parse the date. If this works, stash the epoch (this
424 * is optimal because we can reformat it in the canonical form).
425 * If it doesn't parse, isn't specified at all, or is an empty
426 * string, then use the current date.
427 */
428
429 if (n)
430 n = n->next;
431 if (n && n->string && *n->string) {
432 m->meta.date = mandoc_a2time
433 (MTIME_ISO_8601, n->string);
434 if (0 == m->meta.date) {
435 man_nmsg(m, n, MANDOCERR_BADDATE);
436 m->meta.rawdate = mandoc_strdup(n->string);
437 }
438 } else
439 m->meta.date = time(NULL);
440
441 /* TITLE MSEC DATE ->SOURCE<- VOL */
442
443 if (n && (n = n->next))
444 m->meta.source = mandoc_strdup(n->string);
445
446 /* TITLE MSEC DATE SOURCE ->VOL<- */
447
448 if (n && (n = n->next))
449 m->meta.vol = mandoc_strdup(n->string);
450
451 /*
452 * Remove the `TH' node after we've processed it for our
453 * meta-data.
454 */
455 man_node_delete(m, m->last);
456 return(1);
457 }
458
459 static int
460 post_nf(CHKARGS)
461 {
462
463 if (MAN_LITERAL & m->flags)
464 man_nmsg(m, n, MANDOCERR_SCOPEREP);
465
466 m->flags |= MAN_LITERAL;
467 return(1);
468 }
469
470 static int
471 post_fi(CHKARGS)
472 {
473
474 if ( ! (MAN_LITERAL & m->flags))
475 man_nmsg(m, n, MANDOCERR_WNOSCOPE);
476
477 m->flags &= ~MAN_LITERAL;
478 return(1);
479 }
480
481 static int
482 post_UC(CHKARGS)
483 {
484 static const char * const bsd_versions[] = {
485 "3rd Berkeley Distribution",
486 "4th Berkeley Distribution",
487 "4.2 Berkeley Distribution",
488 "4.3 Berkeley Distribution",
489 "4.4 Berkeley Distribution",
490 };
491
492 const char *p, *s;
493
494 n = n->child;
495 n = m->last->child;
496
497 if (NULL == n || MAN_TEXT != n->type)
498 p = bsd_versions[0];
499 else {
500 s = n->string;
501 if (0 == strcmp(s, "3"))
502 p = bsd_versions[0];
503 else if (0 == strcmp(s, "4"))
504 p = bsd_versions[1];
505 else if (0 == strcmp(s, "5"))
506 p = bsd_versions[2];
507 else if (0 == strcmp(s, "6"))
508 p = bsd_versions[3];
509 else if (0 == strcmp(s, "7"))
510 p = bsd_versions[4];
511 else
512 p = bsd_versions[0];
513 }
514
515 if (m->meta.source)
516 free(m->meta.source);
517
518 m->meta.source = mandoc_strdup(p);
519 return(1);
520 }
521
522 static int
523 post_AT(CHKARGS)
524 {
525 static const char * const unix_versions[] = {
526 "7th Edition",
527 "System III",
528 "System V",
529 "System V Release 2",
530 };
531
532 const char *p, *s;
533 struct man_node *nn;
534
535 n = n->child;
536
537 if (NULL == n || MAN_TEXT != n->type)
538 p = unix_versions[0];
539 else {
540 s = n->string;
541 if (0 == strcmp(s, "3"))
542 p = unix_versions[0];
543 else if (0 == strcmp(s, "4"))
544 p = unix_versions[1];
545 else if (0 == strcmp(s, "5")) {
546 nn = n->next;
547 if (nn && MAN_TEXT == nn->type && nn->string[0])
548 p = unix_versions[3];
549 else
550 p = unix_versions[2];
551 } else
552 p = unix_versions[0];
553 }
554
555 if (m->meta.source)
556 free(m->meta.source);
557
558 m->meta.source = mandoc_strdup(p);
559 return(1);
560 }