]> git.cameronkatri.com Git - mandoc.git/blob - man_validate.c
Get rid of HAVE_CONFIG_H, it is always defined; idea from libnbcompat.
[mandoc.git] / man_validate.c
1 /* $Id: man_validate.c,v 1.106 2014/08/10 23:54:41 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "man.h"
32 #include "mandoc.h"
33 #include "mandoc_aux.h"
34 #include "libman.h"
35 #include "libmandoc.h"
36
37 #define CHKARGS struct man *man, struct man_node *n
38
39 typedef int (*v_check)(CHKARGS);
40
41 static int check_eq0(CHKARGS);
42 static int check_eq2(CHKARGS);
43 static int check_le1(CHKARGS);
44 static int check_le5(CHKARGS);
45 static int check_par(CHKARGS);
46 static int check_part(CHKARGS);
47 static int check_root(CHKARGS);
48 static int check_text(CHKARGS);
49
50 static int post_AT(CHKARGS);
51 static int post_IP(CHKARGS);
52 static int post_vs(CHKARGS);
53 static int post_fi(CHKARGS);
54 static int post_ft(CHKARGS);
55 static int post_nf(CHKARGS);
56 static int post_TH(CHKARGS);
57 static int post_UC(CHKARGS);
58 static int post_UR(CHKARGS);
59
60 static v_check man_valids[MAN_MAX] = {
61 post_vs, /* br */
62 post_TH, /* TH */
63 NULL, /* SH */
64 NULL, /* SS */
65 NULL, /* TP */
66 check_par, /* LP */
67 check_par, /* PP */
68 check_par, /* P */
69 post_IP, /* IP */
70 NULL, /* HP */
71 NULL, /* SM */
72 NULL, /* SB */
73 NULL, /* BI */
74 NULL, /* IB */
75 NULL, /* BR */
76 NULL, /* RB */
77 NULL, /* R */
78 NULL, /* B */
79 NULL, /* I */
80 NULL, /* IR */
81 NULL, /* RI */
82 check_eq0, /* na */
83 post_vs, /* sp */
84 post_nf, /* nf */
85 post_fi, /* fi */
86 NULL, /* RE */
87 check_part, /* RS */
88 NULL, /* DT */
89 post_UC, /* UC */
90 check_le1, /* PD */
91 post_AT, /* AT */
92 NULL, /* in */
93 post_ft, /* ft */
94 check_eq2, /* OP */
95 post_nf, /* EX */
96 post_fi, /* EE */
97 post_UR, /* UR */
98 NULL, /* UE */
99 NULL, /* ll */
100 };
101
102
103 int
104 man_valid_post(struct man *man)
105 {
106 struct man_node *n;
107 v_check *cp;
108
109 n = man->last;
110 if (n->flags & MAN_VALID)
111 return(1);
112 n->flags |= MAN_VALID;
113
114 switch (n->type) {
115 case MAN_TEXT:
116 return(check_text(man, n));
117 case MAN_ROOT:
118 return(check_root(man, n));
119 case MAN_EQN:
120 /* FALLTHROUGH */
121 case MAN_TBL:
122 return(1);
123 default:
124 cp = man_valids + n->tok;
125 return(*cp ? (*cp)(man, n) : 1);
126 }
127 }
128
129 static int
130 check_root(CHKARGS)
131 {
132
133 assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
134
135 if (NULL == man->first->child)
136 mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
137 n->line, n->pos, NULL);
138 else
139 man->meta.hasbody = 1;
140
141 if (NULL == man->meta.title) {
142 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
143 n->line, n->pos, NULL);
144
145 /*
146 * If a title hasn't been set, do so now (by
147 * implication, date and section also aren't set).
148 */
149
150 man->meta.title = mandoc_strdup("");
151 man->meta.msec = mandoc_strdup("");
152 man->meta.date = man->quick ? mandoc_strdup("") :
153 mandoc_normdate(man->parse, NULL, n->line, n->pos);
154 }
155
156 return(1);
157 }
158
159 static int
160 check_text(CHKARGS)
161 {
162 char *cp, *p;
163
164 if (MAN_LITERAL & man->flags)
165 return(1);
166
167 cp = n->string;
168 for (p = cp; NULL != (p = strchr(p, '\t')); p++)
169 mandoc_msg(MANDOCERR_FI_TAB, man->parse,
170 n->line, n->pos + (p - cp), NULL);
171 return(1);
172 }
173
174 #define INEQ_DEFINE(x, ineq, name) \
175 static int \
176 check_##name(CHKARGS) \
177 { \
178 if (n->nchild ineq (x)) \
179 return(1); \
180 mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, n->pos, \
181 "line arguments %s %d (have %d)", \
182 #ineq, (x), n->nchild); \
183 return(1); \
184 }
185
186 INEQ_DEFINE(0, ==, eq0)
187 INEQ_DEFINE(2, ==, eq2)
188 INEQ_DEFINE(1, <=, le1)
189 INEQ_DEFINE(5, <=, le5)
190
191 static int
192 post_UR(CHKARGS)
193 {
194
195 if (MAN_HEAD == n->type && 1 != n->nchild)
196 mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
197 n->pos, "line arguments eq 1 (have %d)", n->nchild);
198
199 return(check_part(man, n));
200 }
201
202 static int
203 post_ft(CHKARGS)
204 {
205 char *cp;
206 int ok;
207
208 if (0 == n->nchild)
209 return(1);
210
211 ok = 0;
212 cp = n->child->string;
213 switch (*cp) {
214 case '1':
215 /* FALLTHROUGH */
216 case '2':
217 /* FALLTHROUGH */
218 case '3':
219 /* FALLTHROUGH */
220 case '4':
221 /* FALLTHROUGH */
222 case 'I':
223 /* FALLTHROUGH */
224 case 'P':
225 /* FALLTHROUGH */
226 case 'R':
227 if ('\0' == cp[1])
228 ok = 1;
229 break;
230 case 'B':
231 if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
232 ok = 1;
233 break;
234 case 'C':
235 if ('W' == cp[1] && '\0' == cp[2])
236 ok = 1;
237 break;
238 default:
239 break;
240 }
241
242 if (0 == ok) {
243 mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
244 n->line, n->pos, "ft %s", cp);
245 *cp = '\0';
246 }
247
248 if (1 < n->nchild)
249 mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
250 n->pos, "want one child (have %d)", n->nchild);
251
252 return(1);
253 }
254
255 static int
256 check_part(CHKARGS)
257 {
258
259 if (MAN_BODY == n->type && 0 == n->nchild)
260 mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line,
261 n->pos, "want children (have none)");
262
263 return(1);
264 }
265
266 static int
267 check_par(CHKARGS)
268 {
269
270 switch (n->type) {
271 case MAN_BLOCK:
272 if (0 == n->body->nchild)
273 man_node_delete(man, n);
274 break;
275 case MAN_BODY:
276 if (0 == n->nchild)
277 mandoc_vmsg(MANDOCERR_PAR_SKIP,
278 man->parse, n->line, n->pos,
279 "%s empty", man_macronames[n->tok]);
280 break;
281 case MAN_HEAD:
282 if (n->nchild)
283 mandoc_vmsg(MANDOCERR_ARG_SKIP,
284 man->parse, n->line, n->pos,
285 "%s %s%s", man_macronames[n->tok],
286 n->child->string,
287 n->nchild > 1 ? " ..." : "");
288 break;
289 default:
290 break;
291 }
292
293 return(1);
294 }
295
296 static int
297 post_IP(CHKARGS)
298 {
299
300 switch (n->type) {
301 case MAN_BLOCK:
302 if (0 == n->head->nchild && 0 == n->body->nchild)
303 man_node_delete(man, n);
304 break;
305 case MAN_BODY:
306 if (0 == n->parent->head->nchild && 0 == n->nchild)
307 mandoc_vmsg(MANDOCERR_PAR_SKIP,
308 man->parse, n->line, n->pos,
309 "%s empty", man_macronames[n->tok]);
310 break;
311 default:
312 break;
313 }
314 return(1);
315 }
316
317 static int
318 post_TH(CHKARGS)
319 {
320 struct man_node *nb;
321 const char *p;
322
323 check_le5(man, n);
324
325 free(man->meta.title);
326 free(man->meta.vol);
327 free(man->meta.source);
328 free(man->meta.msec);
329 free(man->meta.date);
330
331 man->meta.title = man->meta.vol = man->meta.date =
332 man->meta.msec = man->meta.source = NULL;
333
334 nb = n;
335
336 /* ->TITLE<- MSEC DATE SOURCE VOL */
337
338 n = n->child;
339 if (n && n->string) {
340 for (p = n->string; '\0' != *p; p++) {
341 /* Only warn about this once... */
342 if (isalpha((unsigned char)*p) &&
343 ! isupper((unsigned char)*p)) {
344 mandoc_vmsg(MANDOCERR_TITLE_CASE,
345 man->parse, n->line,
346 n->pos + (p - n->string),
347 "TH %s", n->string);
348 break;
349 }
350 }
351 man->meta.title = mandoc_strdup(n->string);
352 } else {
353 man->meta.title = mandoc_strdup("");
354 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
355 nb->line, nb->pos, "TH");
356 }
357
358 /* TITLE ->MSEC<- DATE SOURCE VOL */
359
360 if (n)
361 n = n->next;
362 if (n && n->string)
363 man->meta.msec = mandoc_strdup(n->string);
364 else {
365 man->meta.msec = mandoc_strdup("");
366 mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
367 nb->line, nb->pos, "TH %s", man->meta.title);
368 }
369
370 /* TITLE MSEC ->DATE<- SOURCE VOL */
371
372 if (n)
373 n = n->next;
374 if (n && n->string && '\0' != n->string[0]) {
375 man->meta.date = man->quick ?
376 mandoc_strdup(n->string) :
377 mandoc_normdate(man->parse, n->string,
378 n->line, n->pos);
379 } else {
380 man->meta.date = mandoc_strdup("");
381 mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
382 n ? n->line : nb->line,
383 n ? n->pos : nb->pos, "TH");
384 }
385
386 /* TITLE MSEC DATE ->SOURCE<- VOL */
387
388 if (n && (n = n->next))
389 man->meta.source = mandoc_strdup(n->string);
390
391 /* TITLE MSEC DATE SOURCE ->VOL<- */
392 /* If missing, use the default VOL name for MSEC. */
393
394 if (n && (n = n->next))
395 man->meta.vol = mandoc_strdup(n->string);
396 else if ('\0' != man->meta.msec[0] &&
397 (NULL != (p = mandoc_a2msec(man->meta.msec))))
398 man->meta.vol = mandoc_strdup(p);
399
400 /*
401 * Remove the `TH' node after we've processed it for our
402 * meta-data.
403 */
404 man_node_delete(man, man->last);
405 return(1);
406 }
407
408 static int
409 post_nf(CHKARGS)
410 {
411
412 check_eq0(man, n);
413
414 if (MAN_LITERAL & man->flags)
415 mandoc_msg(MANDOCERR_NF_SKIP, man->parse,
416 n->line, n->pos, "nf");
417
418 man->flags |= MAN_LITERAL;
419 return(1);
420 }
421
422 static int
423 post_fi(CHKARGS)
424 {
425
426 check_eq0(man, n);
427
428 if ( ! (MAN_LITERAL & man->flags))
429 mandoc_msg(MANDOCERR_FI_SKIP, man->parse,
430 n->line, n->pos, "fi");
431
432 man->flags &= ~MAN_LITERAL;
433 return(1);
434 }
435
436 static int
437 post_UC(CHKARGS)
438 {
439 static const char * const bsd_versions[] = {
440 "3rd Berkeley Distribution",
441 "4th Berkeley Distribution",
442 "4.2 Berkeley Distribution",
443 "4.3 Berkeley Distribution",
444 "4.4 Berkeley Distribution",
445 };
446
447 const char *p, *s;
448
449 n = n->child;
450
451 if (NULL == n || MAN_TEXT != n->type)
452 p = bsd_versions[0];
453 else {
454 s = n->string;
455 if (0 == strcmp(s, "3"))
456 p = bsd_versions[0];
457 else if (0 == strcmp(s, "4"))
458 p = bsd_versions[1];
459 else if (0 == strcmp(s, "5"))
460 p = bsd_versions[2];
461 else if (0 == strcmp(s, "6"))
462 p = bsd_versions[3];
463 else if (0 == strcmp(s, "7"))
464 p = bsd_versions[4];
465 else
466 p = bsd_versions[0];
467 }
468
469 free(man->meta.source);
470 man->meta.source = mandoc_strdup(p);
471 return(1);
472 }
473
474 static int
475 post_AT(CHKARGS)
476 {
477 static const char * const unix_versions[] = {
478 "7th Edition",
479 "System III",
480 "System V",
481 "System V Release 2",
482 };
483
484 const char *p, *s;
485 struct man_node *nn;
486
487 n = n->child;
488
489 if (NULL == n || MAN_TEXT != n->type)
490 p = unix_versions[0];
491 else {
492 s = n->string;
493 if (0 == strcmp(s, "3"))
494 p = unix_versions[0];
495 else if (0 == strcmp(s, "4"))
496 p = unix_versions[1];
497 else if (0 == strcmp(s, "5")) {
498 nn = n->next;
499 if (nn && MAN_TEXT == nn->type && nn->string[0])
500 p = unix_versions[3];
501 else
502 p = unix_versions[2];
503 } else
504 p = unix_versions[0];
505 }
506
507 free(man->meta.source);
508 man->meta.source = mandoc_strdup(p);
509 return(1);
510 }
511
512 static int
513 post_vs(CHKARGS)
514 {
515
516 if (n->tok == MAN_br)
517 check_eq0(man, n);
518 else
519 check_le1(man, n);
520
521 if (NULL != n->prev)
522 return(1);
523
524 switch (n->parent->tok) {
525 case MAN_SH:
526 /* FALLTHROUGH */
527 case MAN_SS:
528 mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
529 "%s after %s", man_macronames[n->tok],
530 man_macronames[n->parent->tok]);
531 /* FALLTHROUGH */
532 case MAN_MAX:
533 /*
534 * Don't warn about this because it occurs in pod2man
535 * and would cause considerable (unfixable) warnage.
536 */
537 man_node_delete(man, n);
538 break;
539 default:
540 break;
541 }
542
543 return(1);
544 }