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