1 /* $Vendor-Id: main.c,v 1.100 2010/07/25 11:44:31 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/mman.h> 23 #include <sys/stat.h> 24 25 #include <assert.h> 26 #include <ctype.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <stdint.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "mandoc.h" 35 #include "main.h" 36 #include "mdoc.h" 37 #include "man.h" 38 #include "roff.h" 39 40 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) 41 42 /* FIXME: Intel's compiler? LLVM? pcc? */ 43 44 #if !defined(__GNUC__) || (__GNUC__ < 2) 45 # if !defined(lint) 46 # define __attribute__(x) 47 # endif 48 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 49 50 typedef void (*out_mdoc)(void *, const struct mdoc *); 51 typedef void (*out_man)(void *, const struct man *); 52 typedef void (*out_free)(void *); 53 54 struct buf { 55 char *buf; 56 size_t sz; 57 }; 58 59 enum intt { 60 INTT_AUTO, 61 INTT_MDOC, 62 INTT_MAN 63 }; 64 65 enum outt { 66 OUTT_ASCII = 0, 67 OUTT_TREE, 68 OUTT_HTML, 69 OUTT_XHTML, 70 OUTT_LINT, 71 OUTT_PS, 72 OUTT_PDF 73 }; 74 75 struct curparse { 76 const char *file; /* Current parse. */ 77 int fd; /* Current parse. */ 78 int wflags; 79 /* FIXME: set by max error */ 80 #define WARN_WALL (1 << 0) /* All-warnings mask. */ 81 #define WARN_WERR (1 << 2) /* Warnings->errors. */ 82 int fflags; 83 #define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */ 84 #define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */ 85 #define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */ 86 #define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */ 87 #define FL_STRICT FL_NIGN_ESCAPE | \ 88 FL_NIGN_MACRO /* ignore nothing */ 89 enum intt inttype; /* which parser to use */ 90 struct man *man; /* man parser */ 91 struct mdoc *mdoc; /* mdoc parser */ 92 struct roff *roff; /* roff parser (!NULL) */ 93 struct regset regs; /* roff registers */ 94 enum outt outtype; /* which output to use */ 95 out_mdoc outmdoc; /* mdoc output ptr */ 96 out_man outman; /* man output ptr */ 97 out_free outfree; /* free output ptr */ 98 void *outdata; /* data for output */ 99 char outopts[BUFSIZ]; /* buf of output opts */ 100 }; 101 102 static const char * const mandocerrs[MANDOCERR_MAX] = { 103 "ok", 104 105 "generic warning", 106 107 "text should be uppercase", 108 "sections out of conventional order", 109 "section name repeats", 110 "out of order prologue", 111 "repeated prologue entry", 112 "list type must come first", 113 "bad standard", 114 "bad library", 115 "tab in non-literal context", 116 "bad escape sequence", 117 "unterminated quoted string", 118 "argument requires the width argument", 119 "superfluous width argument", 120 "ignoring argument", 121 "bad date argument", 122 "bad width argument", 123 "unknown manual section", 124 "section not in conventional manual section", 125 "end of line whitespace", 126 "blocks badly nested", 127 "scope open on exit", 128 129 "generic error", 130 131 "NAME section must come first", 132 "bad Boolean value", 133 "child violates parent syntax", 134 "bad AT&T symbol", 135 "list type repeated", 136 "display type repeated", 137 "argument repeated", 138 "manual name not yet set", 139 "obsolete macro ignored", 140 "empty macro ignored", 141 "macro not allowed in body", 142 "macro not allowed in prologue", 143 "bad character", 144 "bad NAME section contents", 145 "no blank lines", 146 "no text in this context", 147 "bad comment style", 148 "unknown macro will be lost", 149 "line scope broken", 150 "argument count wrong", 151 "request scope close w/none open", 152 "scope already open", 153 "macro requires line argument(s)", 154 "macro requires body argument(s)", 155 "macro requires argument(s)", 156 "no title in document", 157 "missing list type", 158 "missing display type", 159 "missing font type", 160 "line argument(s) will be lost", 161 "body argument(s) will be lost", 162 163 "generic fatal error", 164 165 "column syntax is inconsistent", 166 "displays may not be nested", 167 "unsupported display type", 168 "blocks badly nested", 169 "no such block is open", 170 "scope broken, syntax violated", 171 "line scope broken, syntax violated", 172 "argument count wrong, violates syntax", 173 "child violates parent syntax", 174 "argument count wrong, violates syntax", 175 "no document body", 176 "no document prologue", 177 "utsname system call failed", 178 "memory exhausted", 179 }; 180 181 static void fdesc(struct curparse *); 182 static void ffile(const char *, struct curparse *); 183 static int foptions(int *, char *); 184 static struct man *man_init(struct curparse *); 185 static struct mdoc *mdoc_init(struct curparse *); 186 static struct roff *roff_init(struct curparse *); 187 static int moptions(enum intt *, char *); 188 static int mmsg(enum mandocerr, void *, 189 int, int, const char *); 190 static int pset(const char *, int, struct curparse *, 191 struct man **, struct mdoc **); 192 static int toptions(struct curparse *, char *); 193 static void usage(void) __attribute__((noreturn)); 194 static void version(void) __attribute__((noreturn)); 195 static int woptions(int *, char *); 196 197 static const char *progname; 198 static int with_fatal; 199 static int with_error; 200 201 int 202 main(int argc, char *argv[]) 203 { 204 int c; 205 struct curparse curp; 206 207 progname = strrchr(argv[0], '/'); 208 if (progname == NULL) 209 progname = argv[0]; 210 else 211 ++progname; 212 213 memset(&curp, 0, sizeof(struct curparse)); 214 215 curp.inttype = INTT_AUTO; 216 curp.outtype = OUTT_ASCII; 217 218 /* LINTED */ 219 while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:"))) 220 switch (c) { 221 case ('f'): 222 if ( ! foptions(&curp.fflags, optarg)) 223 return(EXIT_FAILURE); 224 break; 225 case ('m'): 226 if ( ! moptions(&curp.inttype, optarg)) 227 return(EXIT_FAILURE); 228 break; 229 case ('O'): 230 (void)strlcat(curp.outopts, optarg, BUFSIZ); 231 (void)strlcat(curp.outopts, ",", BUFSIZ); 232 break; 233 case ('T'): 234 if ( ! toptions(&curp, optarg)) 235 return(EXIT_FAILURE); 236 break; 237 case ('W'): 238 if ( ! woptions(&curp.wflags, optarg)) 239 return(EXIT_FAILURE); 240 break; 241 case ('V'): 242 version(); 243 /* NOTREACHED */ 244 default: 245 usage(); 246 /* NOTREACHED */ 247 } 248 249 argc -= optind; 250 argv += optind; 251 252 if (NULL == *argv) { 253 curp.file = "<stdin>"; 254 curp.fd = STDIN_FILENO; 255 256 fdesc(&curp); 257 } 258 259 while (*argv) { 260 ffile(*argv, &curp); 261 262 if (with_fatal && !(curp.fflags & FL_IGN_ERRORS)) 263 break; 264 ++argv; 265 } 266 267 if (curp.outfree) 268 (*curp.outfree)(curp.outdata); 269 if (curp.mdoc) 270 mdoc_free(curp.mdoc); 271 if (curp.man) 272 man_free(curp.man); 273 if (curp.roff) 274 roff_free(curp.roff); 275 276 return((with_fatal || with_error) ? 277 EXIT_FAILURE : EXIT_SUCCESS); 278 } 279 280 281 static void 282 version(void) 283 { 284 285 (void)printf("%s %s\n", progname, VERSION); 286 exit(EXIT_SUCCESS); 287 } 288 289 290 static void 291 usage(void) 292 { 293 294 (void)fprintf(stderr, "usage: %s [-V] [-foption] " 295 "[-mformat] [-Ooption] [-Toutput] " 296 "[-Werr] [file...]\n", progname); 297 exit(EXIT_FAILURE); 298 } 299 300 301 static struct man * 302 man_init(struct curparse *curp) 303 { 304 int pflags; 305 306 /* Defaults from mandoc.1. */ 307 308 pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE; 309 310 if (curp->fflags & FL_NIGN_MACRO) 311 pflags &= ~MAN_IGN_MACRO; 312 if (curp->fflags & FL_NIGN_ESCAPE) 313 pflags &= ~MAN_IGN_ESCAPE; 314 315 return(man_alloc(&curp->regs, curp, pflags, mmsg)); 316 } 317 318 319 static struct roff * 320 roff_init(struct curparse *curp) 321 { 322 323 return(roff_alloc(&curp->regs, mmsg, curp)); 324 } 325 326 327 static struct mdoc * 328 mdoc_init(struct curparse *curp) 329 { 330 int pflags; 331 332 /* Defaults from mandoc.1. */ 333 334 pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE; 335 336 if (curp->fflags & FL_IGN_SCOPE) 337 pflags |= MDOC_IGN_SCOPE; 338 if (curp->fflags & FL_NIGN_ESCAPE) 339 pflags &= ~MDOC_IGN_ESCAPE; 340 if (curp->fflags & FL_NIGN_MACRO) 341 pflags &= ~MDOC_IGN_MACRO; 342 343 return(mdoc_alloc(&curp->regs, curp, pflags, mmsg)); 344 } 345 346 347 static void 348 ffile(const char *file, struct curparse *curp) 349 { 350 351 curp->file = file; 352 if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { 353 perror(curp->file); 354 with_fatal = 1; 355 return; 356 } 357 358 fdesc(curp); 359 360 if (-1 == close(curp->fd)) 361 perror(curp->file); 362 } 363 364 365 static int 366 resize_buf(struct buf *buf, size_t initial) 367 { 368 void *tmp; 369 size_t sz; 370 371 if (buf->sz == 0) 372 sz = initial; 373 else 374 sz = 2 * buf->sz; 375 tmp = realloc(buf->buf, sz); 376 if (NULL == tmp) { 377 perror(NULL); 378 return(0); 379 } 380 buf->buf = tmp; 381 buf->sz = sz; 382 return(1); 383 } 384 385 386 static int 387 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap) 388 { 389 struct stat st; 390 size_t off; 391 ssize_t ssz; 392 393 if (-1 == fstat(curp->fd, &st)) { 394 perror(curp->file); 395 with_fatal = 1; 396 return(0); 397 } 398 399 /* 400 * If we're a regular file, try just reading in the whole entry 401 * via mmap(). This is faster than reading it into blocks, and 402 * since each file is only a few bytes to begin with, I'm not 403 * concerned that this is going to tank any machines. 404 */ 405 406 if (S_ISREG(st.st_mode)) { 407 if (st.st_size >= (1U << 31)) { 408 fprintf(stderr, "%s: input too large\n", 409 curp->file); 410 with_fatal = 1; 411 return(0); 412 } 413 *with_mmap = 1; 414 fb->sz = (size_t)st.st_size; 415 fb->buf = mmap(NULL, fb->sz, PROT_READ, 416 MAP_FILE|MAP_SHARED, curp->fd, 0); 417 if (fb->buf != MAP_FAILED) 418 return(1); 419 } 420 421 /* 422 * If this isn't a regular file (like, say, stdin), then we must 423 * go the old way and just read things in bit by bit. 424 */ 425 426 *with_mmap = 0; 427 off = 0; 428 fb->sz = 0; 429 fb->buf = NULL; 430 for (;;) { 431 if (off == fb->sz) { 432 if (fb->sz == (1U << 31)) { 433 fprintf(stderr, "%s: input too large\n", 434 curp->file); 435 break; 436 } 437 if (! resize_buf(fb, 65536)) 438 break; 439 } 440 ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off); 441 if (ssz == 0) { 442 fb->sz = off; 443 return(1); 444 } 445 if (ssz == -1) { 446 perror(curp->file); 447 break; 448 } 449 off += (size_t)ssz; 450 } 451 452 free(fb->buf); 453 fb->buf = NULL; 454 with_fatal = 1; 455 return(0); 456 } 457 458 459 static void 460 fdesc(struct curparse *curp) 461 { 462 struct buf ln, blk; 463 int i, pos, lnn, lnn_start, with_mmap, of; 464 enum rofferr re; 465 struct man *man; 466 struct mdoc *mdoc; 467 struct roff *roff; 468 469 man = NULL; 470 mdoc = NULL; 471 roff = NULL; 472 473 memset(&ln, 0, sizeof(struct buf)); 474 475 /* 476 * Two buffers: ln and buf. buf is the input file and may be 477 * memory mapped. ln is a line buffer and grows on-demand. 478 */ 479 480 if ( ! read_whole_file(curp, &blk, &with_mmap)) 481 return; 482 483 if (NULL == curp->roff) 484 curp->roff = roff_init(curp); 485 if (NULL == (roff = curp->roff)) 486 goto bailout; 487 488 for (i = 0, lnn = 1; i < (int)blk.sz;) { 489 pos = 0; 490 lnn_start = lnn; 491 while (i < (int)blk.sz) { 492 if ('\n' == blk.buf[i]) { 493 ++i; 494 ++lnn; 495 break; 496 } 497 498 /* 499 * Warn about bogus characters. If you're using 500 * non-ASCII encoding, you're screwing your 501 * readers. Since I'd rather this not happen, 502 * I'll be helpful and drop these characters so 503 * we don't display gibberish. Note to manual 504 * writers: use special characters. 505 */ 506 507 if ( ! isgraph((u_char)blk.buf[i]) && 508 ! isblank((u_char)blk.buf[i])) { 509 if ( ! mmsg(MANDOCERR_BADCHAR, curp, 510 lnn_start, pos, 511 "ignoring byte")) 512 goto bailout; 513 i++; 514 continue; 515 } 516 517 /* Trailing backslash is like a plain character. */ 518 if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { 519 if (pos >= (int)ln.sz) 520 if (! resize_buf(&ln, 256)) 521 goto bailout; 522 ln.buf[pos++] = blk.buf[i++]; 523 continue; 524 } 525 /* Found an escape and at least one other character. */ 526 if ('\n' == blk.buf[i + 1]) { 527 /* Escaped newlines are skipped over */ 528 i += 2; 529 ++lnn; 530 continue; 531 } 532 if ('"' == blk.buf[i + 1]) { 533 i += 2; 534 /* Comment, skip to end of line */ 535 for (; i < (int)blk.sz; ++i) { 536 if ('\n' == blk.buf[i]) { 537 ++i; 538 ++lnn; 539 break; 540 } 541 } 542 /* Backout trailing whitespaces */ 543 for (; pos > 0; --pos) { 544 if (ln.buf[pos - 1] != ' ') 545 break; 546 if (pos > 2 && ln.buf[pos - 2] == '\\') 547 break; 548 } 549 break; 550 } 551 /* Some other escape sequence, copy and continue. */ 552 if (pos + 1 >= (int)ln.sz) 553 if (! resize_buf(&ln, 256)) 554 goto bailout; 555 556 ln.buf[pos++] = blk.buf[i++]; 557 ln.buf[pos++] = blk.buf[i++]; 558 } 559 560 if (pos >= (int)ln.sz) 561 if (! resize_buf(&ln, 256)) 562 goto bailout; 563 ln.buf[pos] = '\0'; 564 565 /* 566 * A significant amount of complexity is contained by 567 * the roff preprocessor. It's line-oriented but can be 568 * expressed on one line, so we need at times to 569 * readjust our starting point and re-run it. The roff 570 * preprocessor can also readjust the buffers with new 571 * data, so we pass them in wholesale. 572 */ 573 574 of = 0; 575 do { 576 re = roff_parseln(roff, lnn_start, 577 &ln.buf, &ln.sz, of, &of); 578 } while (ROFF_RERUN == re); 579 580 if (ROFF_IGN == re) 581 continue; 582 else if (ROFF_ERR == re) 583 goto bailout; 584 585 /* 586 * If input parsers have not been allocated, do so now. 587 * We keep these instanced betwen parsers, but set them 588 * locally per parse routine since we can use different 589 * parsers with each one. 590 */ 591 592 if ( ! (man || mdoc)) 593 if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc)) 594 goto bailout; 595 596 /* Lastly, push down into the parsers themselves. */ 597 598 if (man && ! man_parseln(man, lnn_start, ln.buf, of)) 599 goto bailout; 600 if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of)) 601 goto bailout; 602 } 603 604 /* NOTE a parser may not have been assigned, yet. */ 605 606 if ( ! (man || mdoc)) { 607 fprintf(stderr, "%s: Not a manual\n", curp->file); 608 goto bailout; 609 } 610 611 /* Clean up the parse routine ASTs. */ 612 613 if (mdoc && ! mdoc_endparse(mdoc)) 614 goto bailout; 615 if (man && ! man_endparse(man)) 616 goto bailout; 617 if (roff && ! roff_endparse(roff)) 618 goto bailout; 619 620 /* If unset, allocate output dev now (if applicable). */ 621 622 if ( ! (curp->outman && curp->outmdoc)) { 623 switch (curp->outtype) { 624 case (OUTT_XHTML): 625 curp->outdata = xhtml_alloc(curp->outopts); 626 break; 627 case (OUTT_HTML): 628 curp->outdata = html_alloc(curp->outopts); 629 break; 630 case (OUTT_ASCII): 631 curp->outdata = ascii_alloc(curp->outopts); 632 curp->outfree = ascii_free; 633 break; 634 case (OUTT_PDF): 635 curp->outdata = pdf_alloc(curp->outopts); 636 curp->outfree = pspdf_free; 637 break; 638 case (OUTT_PS): 639 curp->outdata = ps_alloc(curp->outopts); 640 curp->outfree = pspdf_free; 641 break; 642 default: 643 break; 644 } 645 646 switch (curp->outtype) { 647 case (OUTT_HTML): 648 /* FALLTHROUGH */ 649 case (OUTT_XHTML): 650 curp->outman = html_man; 651 curp->outmdoc = html_mdoc; 652 curp->outfree = html_free; 653 break; 654 case (OUTT_TREE): 655 curp->outman = tree_man; 656 curp->outmdoc = tree_mdoc; 657 break; 658 case (OUTT_PDF): 659 /* FALLTHROUGH */ 660 case (OUTT_ASCII): 661 /* FALLTHROUGH */ 662 case (OUTT_PS): 663 curp->outman = terminal_man; 664 curp->outmdoc = terminal_mdoc; 665 break; 666 default: 667 break; 668 } 669 } 670 671 /* Execute the out device, if it exists. */ 672 673 if (man && curp->outman) 674 (*curp->outman)(curp->outdata, man); 675 if (mdoc && curp->outmdoc) 676 (*curp->outmdoc)(curp->outdata, mdoc); 677 678 cleanup: 679 memset(&curp->regs, 0, sizeof(struct regset)); 680 if (mdoc) 681 mdoc_reset(mdoc); 682 if (man) 683 man_reset(man); 684 if (roff) 685 roff_reset(roff); 686 if (ln.buf) 687 free(ln.buf); 688 if (with_mmap) 689 munmap(blk.buf, blk.sz); 690 else 691 free(blk.buf); 692 693 return; 694 695 bailout: 696 with_fatal = 1; 697 goto cleanup; 698 } 699 700 701 static int 702 pset(const char *buf, int pos, struct curparse *curp, 703 struct man **man, struct mdoc **mdoc) 704 { 705 int i; 706 707 /* 708 * Try to intuit which kind of manual parser should be used. If 709 * passed in by command-line (-man, -mdoc), then use that 710 * explicitly. If passed as -mandoc, then try to guess from the 711 * line: either skip dot-lines, use -mdoc when finding `.Dt', or 712 * default to -man, which is more lenient. 713 */ 714 715 if ('.' == buf[0] || '\'' == buf[0]) { 716 for (i = 1; buf[i]; i++) 717 if (' ' != buf[i] && '\t' != buf[i]) 718 break; 719 if (0 == buf[i]) 720 return(1); 721 } 722 723 switch (curp->inttype) { 724 case (INTT_MDOC): 725 if (NULL == curp->mdoc) 726 curp->mdoc = mdoc_init(curp); 727 if (NULL == (*mdoc = curp->mdoc)) 728 return(0); 729 return(1); 730 case (INTT_MAN): 731 if (NULL == curp->man) 732 curp->man = man_init(curp); 733 if (NULL == (*man = curp->man)) 734 return(0); 735 return(1); 736 default: 737 break; 738 } 739 740 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { 741 if (NULL == curp->mdoc) 742 curp->mdoc = mdoc_init(curp); 743 if (NULL == (*mdoc = curp->mdoc)) 744 return(0); 745 return(1); 746 } 747 748 if (NULL == curp->man) 749 curp->man = man_init(curp); 750 if (NULL == (*man = curp->man)) 751 return(0); 752 return(1); 753 } 754 755 756 static int 757 moptions(enum intt *tflags, char *arg) 758 { 759 760 if (0 == strcmp(arg, "doc")) 761 *tflags = INTT_MDOC; 762 else if (0 == strcmp(arg, "andoc")) 763 *tflags = INTT_AUTO; 764 else if (0 == strcmp(arg, "an")) 765 *tflags = INTT_MAN; 766 else { 767 fprintf(stderr, "%s: Bad argument\n", arg); 768 return(0); 769 } 770 771 return(1); 772 } 773 774 775 static int 776 toptions(struct curparse *curp, char *arg) 777 { 778 779 if (0 == strcmp(arg, "ascii")) 780 curp->outtype = OUTT_ASCII; 781 else if (0 == strcmp(arg, "lint")) { 782 curp->outtype = OUTT_LINT; 783 curp->wflags |= WARN_WALL; 784 curp->fflags |= FL_STRICT; 785 } 786 else if (0 == strcmp(arg, "tree")) 787 curp->outtype = OUTT_TREE; 788 else if (0 == strcmp(arg, "html")) 789 curp->outtype = OUTT_HTML; 790 else if (0 == strcmp(arg, "xhtml")) 791 curp->outtype = OUTT_XHTML; 792 else if (0 == strcmp(arg, "ps")) 793 curp->outtype = OUTT_PS; 794 else if (0 == strcmp(arg, "pdf")) 795 curp->outtype = OUTT_PDF; 796 else { 797 fprintf(stderr, "%s: Bad argument\n", arg); 798 return(0); 799 } 800 801 return(1); 802 } 803 804 805 static int 806 foptions(int *fflags, char *arg) 807 { 808 char *v, *o; 809 const char *toks[8]; 810 811 toks[0] = "ign-scope"; 812 toks[1] = "no-ign-escape"; 813 toks[2] = "no-ign-macro"; 814 toks[3] = "ign-errors"; 815 toks[4] = "strict"; 816 toks[5] = "ign-escape"; 817 toks[6] = NULL; 818 819 while (*arg) { 820 o = arg; 821 switch (getsubopt(&arg, UNCONST(toks), &v)) { 822 case (0): 823 *fflags |= FL_IGN_SCOPE; 824 break; 825 case (1): 826 *fflags |= FL_NIGN_ESCAPE; 827 break; 828 case (2): 829 *fflags |= FL_NIGN_MACRO; 830 break; 831 case (3): 832 *fflags |= FL_IGN_ERRORS; 833 break; 834 case (4): 835 *fflags |= FL_STRICT; 836 break; 837 case (5): 838 *fflags &= ~FL_NIGN_ESCAPE; 839 break; 840 default: 841 fprintf(stderr, "%s: Bad argument\n", o); 842 return(0); 843 } 844 } 845 846 return(1); 847 } 848 849 850 static int 851 woptions(int *wflags, char *arg) 852 { 853 char *v, *o; 854 const char *toks[3]; 855 856 toks[0] = "all"; 857 toks[1] = "error"; 858 toks[2] = NULL; 859 860 while (*arg) { 861 o = arg; 862 switch (getsubopt(&arg, UNCONST(toks), &v)) { 863 case (0): 864 *wflags |= WARN_WALL; 865 break; 866 case (1): 867 *wflags |= WARN_WERR; 868 break; 869 default: 870 fprintf(stderr, "%s: Bad argument\n", o); 871 return(0); 872 } 873 } 874 875 return(1); 876 } 877 878 879 static int 880 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg) 881 { 882 struct curparse *cp; 883 const char *level; 884 int rc; 885 886 cp = (struct curparse *)arg; 887 level = NULL; 888 rc = 1; 889 890 if (t >= MANDOCERR_FATAL) { 891 with_fatal = 1; 892 level = "FATAL"; 893 rc = 0; 894 } else { 895 if ( ! (WARN_WALL & cp->wflags)) 896 return(1); 897 if (t >= MANDOCERR_ERROR) { 898 with_error = 1; 899 level = "ERROR"; 900 } 901 if (WARN_WERR & cp->wflags) { 902 with_fatal = 1; 903 rc = 0; 904 } 905 } 906 907 fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1); 908 if (level) 909 fprintf(stderr, " %s:", level); 910 fprintf(stderr, " %s", mandocerrs[t]); 911 if (msg) 912 fprintf(stderr, ": %s", msg); 913 fputc('\n', stderr); 914 915 return(rc); 916 } 917