1 /* $Id: man_validate.c,v 1.61 2014/03/21 22:17:01 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 <sys/types.h> 19 20 #include <assert.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <limits.h> 24 #include <stdarg.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <time.h> 28 29 #include "man.h" 30 #include "mandoc.h" 31 #include "mandoc_aux.h" 32 #include "libman.h" 33 #include "libmandoc.h" 34 35 #define CHKARGS struct man *man, 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_eq0(CHKARGS); 45 static int check_eq2(CHKARGS); 46 static int check_le1(CHKARGS); 47 static int check_ge2(CHKARGS); 48 static int check_le5(CHKARGS); 49 static int check_head1(CHKARGS); 50 static int check_par(CHKARGS); 51 static int check_part(CHKARGS); 52 static int check_root(CHKARGS); 53 static void check_text(CHKARGS); 54 55 static int post_AT(CHKARGS); 56 static int post_IP(CHKARGS); 57 static int post_vs(CHKARGS); 58 static int post_fi(CHKARGS); 59 static int post_ft(CHKARGS); 60 static int post_nf(CHKARGS); 61 static int post_sec(CHKARGS); 62 static int post_TH(CHKARGS); 63 static int post_UC(CHKARGS); 64 static int pre_sec(CHKARGS); 65 66 static v_check posts_at[] = { post_AT, NULL }; 67 static v_check posts_br[] = { post_vs, check_eq0, NULL }; 68 static v_check posts_eq0[] = { check_eq0, NULL }; 69 static v_check posts_eq2[] = { check_eq2, NULL }; 70 static v_check posts_fi[] = { check_eq0, post_fi, NULL }; 71 static v_check posts_ft[] = { post_ft, NULL }; 72 static v_check posts_ip[] = { post_IP, NULL }; 73 static v_check posts_le1[] = { check_le1, NULL }; 74 static v_check posts_nf[] = { check_eq0, post_nf, NULL }; 75 static v_check posts_par[] = { check_par, NULL }; 76 static v_check posts_part[] = { check_part, NULL }; 77 static v_check posts_sec[] = { post_sec, NULL }; 78 static v_check posts_sp[] = { post_vs, check_le1, NULL }; 79 static v_check posts_th[] = { check_ge2, check_le5, post_TH, NULL }; 80 static v_check posts_uc[] = { post_UC, NULL }; 81 static v_check posts_ur[] = { check_head1, check_part, NULL }; 82 static v_check pres_sec[] = { pre_sec, NULL }; 83 84 static const struct man_valid man_valids[MAN_MAX] = { 85 { NULL, posts_br }, /* br */ 86 { NULL, posts_th }, /* TH */ 87 { pres_sec, posts_sec }, /* SH */ 88 { pres_sec, posts_sec }, /* SS */ 89 { NULL, NULL }, /* TP */ 90 { NULL, posts_par }, /* LP */ 91 { NULL, posts_par }, /* PP */ 92 { NULL, posts_par }, /* P */ 93 { NULL, posts_ip }, /* IP */ 94 { NULL, NULL }, /* HP */ 95 { NULL, NULL }, /* SM */ 96 { NULL, NULL }, /* SB */ 97 { NULL, NULL }, /* BI */ 98 { NULL, NULL }, /* IB */ 99 { NULL, NULL }, /* BR */ 100 { NULL, NULL }, /* RB */ 101 { NULL, NULL }, /* R */ 102 { NULL, NULL }, /* B */ 103 { NULL, NULL }, /* I */ 104 { NULL, NULL }, /* IR */ 105 { NULL, NULL }, /* RI */ 106 { NULL, posts_eq0 }, /* na */ 107 { NULL, posts_sp }, /* sp */ 108 { NULL, posts_nf }, /* nf */ 109 { NULL, posts_fi }, /* fi */ 110 { NULL, NULL }, /* RE */ 111 { NULL, posts_part }, /* RS */ 112 { NULL, NULL }, /* DT */ 113 { NULL, posts_uc }, /* UC */ 114 { NULL, posts_le1 }, /* PD */ 115 { NULL, posts_at }, /* AT */ 116 { NULL, NULL }, /* in */ 117 { NULL, posts_ft }, /* ft */ 118 { NULL, posts_eq2 }, /* OP */ 119 { NULL, posts_nf }, /* EX */ 120 { NULL, posts_fi }, /* EE */ 121 { NULL, posts_ur }, /* UR */ 122 { NULL, NULL }, /* UE */ 123 }; 124 125 126 int 127 man_valid_pre(struct man *man, struct man_node *n) 128 { 129 v_check *cp; 130 131 switch (n->type) { 132 case (MAN_TEXT): 133 /* FALLTHROUGH */ 134 case (MAN_ROOT): 135 /* FALLTHROUGH */ 136 case (MAN_EQN): 137 /* FALLTHROUGH */ 138 case (MAN_TBL): 139 return(1); 140 default: 141 break; 142 } 143 144 if (NULL == (cp = man_valids[n->tok].pres)) 145 return(1); 146 for ( ; *cp; cp++) 147 if ( ! (*cp)(man, n)) 148 return(0); 149 return(1); 150 } 151 152 153 int 154 man_valid_post(struct man *man) 155 { 156 v_check *cp; 157 158 if (MAN_VALID & man->last->flags) 159 return(1); 160 man->last->flags |= MAN_VALID; 161 162 switch (man->last->type) { 163 case (MAN_TEXT): 164 check_text(man, man->last); 165 return(1); 166 case (MAN_ROOT): 167 return(check_root(man, man->last)); 168 case (MAN_EQN): 169 /* FALLTHROUGH */ 170 case (MAN_TBL): 171 return(1); 172 default: 173 break; 174 } 175 176 if (NULL == (cp = man_valids[man->last->tok].posts)) 177 return(1); 178 for ( ; *cp; cp++) 179 if ( ! (*cp)(man, man->last)) 180 return(0); 181 182 return(1); 183 } 184 185 186 static int 187 check_root(CHKARGS) 188 { 189 190 if (MAN_BLINE & man->flags) 191 man_nmsg(man, n, MANDOCERR_SCOPEEXIT); 192 else if (MAN_ELINE & man->flags) 193 man_nmsg(man, n, MANDOCERR_SCOPEEXIT); 194 195 man->flags &= ~MAN_BLINE; 196 man->flags &= ~MAN_ELINE; 197 198 if (NULL == man->first->child) { 199 man_nmsg(man, n, MANDOCERR_NODOCBODY); 200 return(0); 201 } else if (NULL == man->meta.title) { 202 man_nmsg(man, n, MANDOCERR_NOTITLE); 203 204 /* 205 * If a title hasn't been set, do so now (by 206 * implication, date and section also aren't set). 207 */ 208 209 man->meta.title = mandoc_strdup("unknown"); 210 man->meta.msec = mandoc_strdup("1"); 211 man->meta.date = man->quick ? mandoc_strdup("") : 212 mandoc_normdate(man->parse, NULL, n->line, n->pos); 213 } 214 215 return(1); 216 } 217 218 static void 219 check_text(CHKARGS) 220 { 221 char *cp, *p; 222 223 if (MAN_LITERAL & man->flags) 224 return; 225 226 cp = n->string; 227 for (p = cp; NULL != (p = strchr(p, '\t')); p++) 228 man_pmsg(man, n->line, (int)(p - cp), MANDOCERR_BADTAB); 229 } 230 231 #define INEQ_DEFINE(x, ineq, name) \ 232 static int \ 233 check_##name(CHKARGS) \ 234 { \ 235 if (n->nchild ineq (x)) \ 236 return(1); \ 237 mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, n->pos, \ 238 "line arguments %s %d (have %d)", \ 239 #ineq, (x), n->nchild); \ 240 return(1); \ 241 } 242 243 INEQ_DEFINE(0, ==, eq0) 244 INEQ_DEFINE(2, ==, eq2) 245 INEQ_DEFINE(1, <=, le1) 246 INEQ_DEFINE(2, >=, ge2) 247 INEQ_DEFINE(5, <=, le5) 248 249 static int 250 check_head1(CHKARGS) 251 { 252 253 if (MAN_HEAD == n->type && 1 != n->nchild) 254 mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, 255 n->pos, "line arguments eq 1 (have %d)", n->nchild); 256 257 return(1); 258 } 259 260 static int 261 post_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 mandoc_vmsg 302 (MANDOCERR_BADFONT, man->parse, 303 n->line, n->pos, "%s", cp); 304 *cp = '\0'; 305 } 306 307 if (1 < n->nchild) 308 mandoc_vmsg 309 (MANDOCERR_ARGCOUNT, man->parse, n->line, 310 n->pos, "want one child (have %d)", 311 n->nchild); 312 313 return(1); 314 } 315 316 static int 317 pre_sec(CHKARGS) 318 { 319 320 if (MAN_BLOCK == n->type) 321 man->flags &= ~MAN_LITERAL; 322 return(1); 323 } 324 325 static int 326 post_sec(CHKARGS) 327 { 328 329 if ( ! (MAN_HEAD == n->type && 0 == n->nchild)) 330 return(1); 331 332 man_nmsg(man, n, MANDOCERR_SYNTARGCOUNT); 333 return(0); 334 } 335 336 static int 337 check_part(CHKARGS) 338 { 339 340 if (MAN_BODY == n->type && 0 == n->nchild) 341 mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line, 342 n->pos, "want children (have none)"); 343 344 return(1); 345 } 346 347 348 static int 349 check_par(CHKARGS) 350 { 351 352 switch (n->type) { 353 case (MAN_BLOCK): 354 if (0 == n->body->nchild) 355 man_node_delete(man, n); 356 break; 357 case (MAN_BODY): 358 if (0 == n->nchild) 359 man_nmsg(man, n, MANDOCERR_IGNPAR); 360 break; 361 case (MAN_HEAD): 362 if (n->nchild) 363 man_nmsg(man, n, MANDOCERR_ARGSLOST); 364 break; 365 default: 366 break; 367 } 368 369 return(1); 370 } 371 372 static int 373 post_IP(CHKARGS) 374 { 375 376 switch (n->type) { 377 case (MAN_BLOCK): 378 if (0 == n->head->nchild && 0 == n->body->nchild) 379 man_node_delete(man, n); 380 break; 381 case (MAN_BODY): 382 if (0 == n->parent->head->nchild && 0 == n->nchild) 383 man_nmsg(man, n, MANDOCERR_IGNPAR); 384 break; 385 default: 386 break; 387 } 388 return(1); 389 } 390 391 static int 392 post_TH(CHKARGS) 393 { 394 const char *p; 395 396 free(man->meta.title); 397 free(man->meta.vol); 398 free(man->meta.source); 399 free(man->meta.msec); 400 free(man->meta.date); 401 402 man->meta.title = man->meta.vol = man->meta.date = 403 man->meta.msec = man->meta.source = NULL; 404 405 /* ->TITLE<- MSEC DATE SOURCE VOL */ 406 407 n = n->child; 408 if (n && n->string) { 409 for (p = n->string; '\0' != *p; p++) { 410 /* Only warn about this once... */ 411 if (isalpha((unsigned char)*p) && 412 ! isupper((unsigned char)*p)) { 413 man_nmsg(man, n, MANDOCERR_UPPERCASE); 414 break; 415 } 416 } 417 man->meta.title = mandoc_strdup(n->string); 418 } else 419 man->meta.title = mandoc_strdup(""); 420 421 /* TITLE ->MSEC<- DATE SOURCE VOL */ 422 423 if (n) 424 n = n->next; 425 if (n && n->string) 426 man->meta.msec = mandoc_strdup(n->string); 427 else 428 man->meta.msec = mandoc_strdup(""); 429 430 /* TITLE MSEC ->DATE<- SOURCE VOL */ 431 432 if (n) 433 n = n->next; 434 if (n && n->string && '\0' != n->string[0]) { 435 man->meta.date = man->quick ? 436 mandoc_strdup(n->string) : 437 mandoc_normdate(man->parse, n->string, 438 n->line, n->pos); 439 } else 440 man->meta.date = mandoc_strdup(""); 441 442 /* TITLE MSEC DATE ->SOURCE<- VOL */ 443 444 if (n && (n = n->next)) 445 man->meta.source = mandoc_strdup(n->string); 446 447 /* TITLE MSEC DATE SOURCE ->VOL<- */ 448 /* If missing, use the default VOL name for MSEC. */ 449 450 if (n && (n = n->next)) 451 man->meta.vol = mandoc_strdup(n->string); 452 else if ('\0' != man->meta.msec[0] && 453 (NULL != (p = mandoc_a2msec(man->meta.msec)))) 454 man->meta.vol = mandoc_strdup(p); 455 456 /* 457 * Remove the `TH' node after we've processed it for our 458 * meta-data. 459 */ 460 man_node_delete(man, man->last); 461 return(1); 462 } 463 464 static int 465 post_nf(CHKARGS) 466 { 467 468 if (MAN_LITERAL & man->flags) 469 man_nmsg(man, n, MANDOCERR_SCOPEREP); 470 471 man->flags |= MAN_LITERAL; 472 return(1); 473 } 474 475 static int 476 post_fi(CHKARGS) 477 { 478 479 if ( ! (MAN_LITERAL & man->flags)) 480 man_nmsg(man, n, MANDOCERR_WNOSCOPE); 481 482 man->flags &= ~MAN_LITERAL; 483 return(1); 484 } 485 486 static int 487 post_UC(CHKARGS) 488 { 489 static const char * const bsd_versions[] = { 490 "3rd Berkeley Distribution", 491 "4th Berkeley Distribution", 492 "4.2 Berkeley Distribution", 493 "4.3 Berkeley Distribution", 494 "4.4 Berkeley Distribution", 495 }; 496 497 const char *p, *s; 498 499 n = n->child; 500 501 if (NULL == n || MAN_TEXT != n->type) 502 p = bsd_versions[0]; 503 else { 504 s = n->string; 505 if (0 == strcmp(s, "3")) 506 p = bsd_versions[0]; 507 else if (0 == strcmp(s, "4")) 508 p = bsd_versions[1]; 509 else if (0 == strcmp(s, "5")) 510 p = bsd_versions[2]; 511 else if (0 == strcmp(s, "6")) 512 p = bsd_versions[3]; 513 else if (0 == strcmp(s, "7")) 514 p = bsd_versions[4]; 515 else 516 p = bsd_versions[0]; 517 } 518 519 free(man->meta.source); 520 man->meta.source = mandoc_strdup(p); 521 return(1); 522 } 523 524 static int 525 post_AT(CHKARGS) 526 { 527 static const char * const unix_versions[] = { 528 "7th Edition", 529 "System III", 530 "System V", 531 "System V Release 2", 532 }; 533 534 const char *p, *s; 535 struct man_node *nn; 536 537 n = n->child; 538 539 if (NULL == n || MAN_TEXT != n->type) 540 p = unix_versions[0]; 541 else { 542 s = n->string; 543 if (0 == strcmp(s, "3")) 544 p = unix_versions[0]; 545 else if (0 == strcmp(s, "4")) 546 p = unix_versions[1]; 547 else if (0 == strcmp(s, "5")) { 548 nn = n->next; 549 if (nn && MAN_TEXT == nn->type && nn->string[0]) 550 p = unix_versions[3]; 551 else 552 p = unix_versions[2]; 553 } else 554 p = unix_versions[0]; 555 } 556 557 free(man->meta.source); 558 man->meta.source = mandoc_strdup(p); 559 return(1); 560 } 561 562 static int 563 post_vs(CHKARGS) 564 { 565 566 if (NULL != n->prev) 567 return(1); 568 569 switch (n->parent->tok) { 570 case (MAN_SH): 571 /* FALLTHROUGH */ 572 case (MAN_SS): 573 man_nmsg(man, n, MANDOCERR_IGNPAR); 574 /* FALLTHROUGH */ 575 case (MAN_MAX): 576 /* 577 * Don't warn about this because it occurs in pod2man 578 * and would cause considerable (unfixable) warnage. 579 */ 580 man_node_delete(man, n); 581 break; 582 default: 583 break; 584 } 585 586 return(1); 587 } 588