1 /* $Id: main.c,v 1.135 2011/01/04 15:02:00 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 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 #ifndef MAP_FILE 41 #define MAP_FILE 0 42 #endif 43 44 #define REPARSE_LIMIT 1000 45 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) 46 47 /* FIXME: Intel's compiler? LLVM? pcc? */ 48 49 #if !defined(__GNUC__) || (__GNUC__ < 2) 50 # if !defined(lint) 51 # define __attribute__(x) 52 # endif 53 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 54 55 typedef void (*out_mdoc)(void *, const struct mdoc *); 56 typedef void (*out_man)(void *, const struct man *); 57 typedef void (*out_free)(void *); 58 59 struct buf { 60 char *buf; 61 size_t sz; 62 }; 63 64 enum intt { 65 INTT_AUTO, 66 INTT_MDOC, 67 INTT_MAN 68 }; 69 70 enum outt { 71 OUTT_ASCII = 0, 72 OUTT_TREE, 73 OUTT_HTML, 74 OUTT_XHTML, 75 OUTT_LINT, 76 OUTT_PS, 77 OUTT_PDF 78 }; 79 80 struct curparse { 81 const char *file; /* Current parse. */ 82 int fd; /* Current parse. */ 83 int line; /* Line number in the file. */ 84 enum mandoclevel wlevel; /* Ignore messages below this. */ 85 int wstop; /* Stop after a file with a warning. */ 86 enum intt inttype; /* which parser to use */ 87 struct man *pman; /* persistent man parser */ 88 struct mdoc *pmdoc; /* persistent mdoc parser */ 89 struct man *man; /* man parser */ 90 struct mdoc *mdoc; /* mdoc parser */ 91 struct roff *roff; /* roff parser (!NULL) */ 92 struct regset regs; /* roff registers */ 93 int reparse_count; /* finite interpolation stack */ 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 mandoclevels[MANDOCLEVEL_MAX] = { 103 "SUCCESS", 104 "RESERVED", 105 "WARNING", 106 "ERROR", 107 "FATAL", 108 "BADARG", 109 "SYSERR" 110 }; 111 112 static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { 113 MANDOCERR_OK, 114 MANDOCERR_WARNING, 115 MANDOCERR_WARNING, 116 MANDOCERR_ERROR, 117 MANDOCERR_FATAL, 118 MANDOCERR_MAX, 119 MANDOCERR_MAX 120 }; 121 122 static const char * const mandocerrs[MANDOCERR_MAX] = { 123 "ok", 124 125 "generic warning", 126 127 /* related to the prologue */ 128 "no title in document", 129 "document title should be all caps", 130 "unknown manual section", 131 "cannot parse date argument", 132 "prologue macros out of order", 133 "duplicate prologue macro", 134 "macro not allowed in prologue", 135 "macro not allowed in body", 136 137 /* related to document structure */ 138 ".so is fragile, better use ln(1)", 139 "NAME section must come first", 140 "bad NAME section contents", 141 "manual name not yet set", 142 "sections out of conventional order", 143 "duplicate section name", 144 "section not in conventional manual section", 145 146 /* related to macros and nesting */ 147 "skipping obsolete macro", 148 "skipping paragraph macro", 149 "blocks badly nested", 150 "child violates parent syntax", 151 "nested displays are not portable", 152 "already in literal mode", 153 154 /* related to missing macro arguments */ 155 "skipping empty macro", 156 "argument count wrong", 157 "missing display type", 158 "list type must come first", 159 "tag lists require a width argument", 160 "missing font type", 161 162 /* related to bad macro arguments */ 163 "skipping argument", 164 "duplicate argument", 165 "duplicate display type", 166 "duplicate list type", 167 "unknown AT&T UNIX version", 168 "bad Boolean value", 169 "unknown font", 170 "unknown standard specifier", 171 "bad width argument", 172 173 /* related to plain text */ 174 "blank line in non-literal context", 175 "tab in non-literal context", 176 "end of line whitespace", 177 "bad comment style", 178 "unknown escape sequence", 179 "unterminated quoted string", 180 181 /* related to tables */ 182 "extra data cells", 183 184 "generic error", 185 186 /* related to tables */ 187 "bad table syntax", 188 "bad table option", 189 "bad table layout", 190 "no table layout cells specified", 191 "no table data cells specified", 192 "ignore data in cell", 193 "data block still open", 194 195 "input stack limit exceeded, infinite loop?", 196 "skipping bad character", 197 "skipping text before the first section header", 198 "skipping unknown macro", 199 "NOT IMPLEMENTED: skipping request", 200 "line scope broken", 201 "argument count wrong", 202 "skipping end of block that is not open", 203 "missing end of block", 204 "scope open on exit", 205 "uname(3) system call failed", 206 "macro requires line argument(s)", 207 "macro requires body argument(s)", 208 "macro requires argument(s)", 209 "missing list type", 210 "line argument(s) will be lost", 211 "body argument(s) will be lost", 212 213 "generic fatal error", 214 215 "column syntax is inconsistent", 216 "NOT IMPLEMENTED: .Bd -file", 217 "line scope broken, syntax violated", 218 "argument count wrong, violates syntax", 219 "child violates parent syntax", 220 "argument count wrong, violates syntax", 221 "NOT IMPLEMENTED: .so with absolute path or \"..\"", 222 "no document body", 223 "no document prologue", 224 "static buffer exhausted", 225 }; 226 227 static void parsebuf(struct curparse *, struct buf, int); 228 static void pdesc(struct curparse *); 229 static void fdesc(struct curparse *); 230 static void ffile(const char *, struct curparse *); 231 static int pfile(const char *, struct curparse *); 232 static int moptions(enum intt *, char *); 233 static int mmsg(enum mandocerr, void *, 234 int, int, const char *); 235 static void pset(const char *, int, struct curparse *); 236 static int toptions(struct curparse *, char *); 237 static void usage(void) __attribute__((noreturn)); 238 static void version(void) __attribute__((noreturn)); 239 static int woptions(struct curparse *, char *); 240 241 static const char *progname; 242 static enum mandoclevel file_status = MANDOCLEVEL_OK; 243 static enum mandoclevel exit_status = MANDOCLEVEL_OK; 244 245 int 246 main(int argc, char *argv[]) 247 { 248 int c; 249 struct curparse curp; 250 251 progname = strrchr(argv[0], '/'); 252 if (progname == NULL) 253 progname = argv[0]; 254 else 255 ++progname; 256 257 memset(&curp, 0, sizeof(struct curparse)); 258 259 curp.inttype = INTT_AUTO; 260 curp.outtype = OUTT_ASCII; 261 curp.wlevel = MANDOCLEVEL_FATAL; 262 263 /* LINTED */ 264 while (-1 != (c = getopt(argc, argv, "m:O:T:VW:"))) 265 switch (c) { 266 case ('m'): 267 if ( ! moptions(&curp.inttype, optarg)) 268 return((int)MANDOCLEVEL_BADARG); 269 break; 270 case ('O'): 271 (void)strlcat(curp.outopts, optarg, BUFSIZ); 272 (void)strlcat(curp.outopts, ",", BUFSIZ); 273 break; 274 case ('T'): 275 if ( ! toptions(&curp, optarg)) 276 return((int)MANDOCLEVEL_BADARG); 277 break; 278 case ('W'): 279 if ( ! woptions(&curp, optarg)) 280 return((int)MANDOCLEVEL_BADARG); 281 break; 282 case ('V'): 283 version(); 284 /* NOTREACHED */ 285 default: 286 usage(); 287 /* NOTREACHED */ 288 } 289 290 argc -= optind; 291 argv += optind; 292 293 if (NULL == *argv) { 294 curp.file = "<stdin>"; 295 curp.fd = STDIN_FILENO; 296 297 fdesc(&curp); 298 } 299 300 while (*argv) { 301 ffile(*argv, &curp); 302 if (MANDOCLEVEL_OK != exit_status && curp.wstop) 303 break; 304 ++argv; 305 } 306 307 if (curp.outfree) 308 (*curp.outfree)(curp.outdata); 309 if (curp.pmdoc) 310 mdoc_free(curp.pmdoc); 311 if (curp.pman) 312 man_free(curp.pman); 313 if (curp.roff) 314 roff_free(curp.roff); 315 316 return((int)exit_status); 317 } 318 319 320 static void 321 version(void) 322 { 323 324 (void)printf("%s %s\n", progname, VERSION); 325 exit((int)MANDOCLEVEL_OK); 326 } 327 328 329 static void 330 usage(void) 331 { 332 333 (void)fprintf(stderr, "usage: %s " 334 "[-V] " 335 "[-foption] " 336 "[-mformat] " 337 "[-Ooption] " 338 "[-Toutput] " 339 "[-Werr] " 340 "[file...]\n", 341 progname); 342 343 exit((int)MANDOCLEVEL_BADARG); 344 } 345 346 static void 347 ffile(const char *file, struct curparse *curp) 348 { 349 350 /* 351 * Called once per input file. Get the file ready for reading, 352 * pass it through to the parser-driver, then close it out. 353 * XXX: don't do anything special as this is only called for 354 * files; stdin goes directly to fdesc(). 355 */ 356 357 curp->file = file; 358 359 if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { 360 perror(curp->file); 361 exit_status = MANDOCLEVEL_SYSERR; 362 return; 363 } 364 365 fdesc(curp); 366 367 if (-1 == close(curp->fd)) 368 perror(curp->file); 369 } 370 371 static int 372 pfile(const char *file, struct curparse *curp) 373 { 374 const char *savefile; 375 int fd, savefd; 376 377 if (-1 == (fd = open(file, O_RDONLY, 0))) { 378 perror(file); 379 file_status = MANDOCLEVEL_SYSERR; 380 return(0); 381 } 382 383 savefile = curp->file; 384 savefd = curp->fd; 385 386 curp->file = file; 387 curp->fd = fd; 388 389 pdesc(curp); 390 391 curp->file = savefile; 392 curp->fd = savefd; 393 394 if (-1 == close(fd)) 395 perror(file); 396 397 return(MANDOCLEVEL_FATAL > file_status ? 1 : 0); 398 } 399 400 401 static void 402 resize_buf(struct buf *buf, size_t initial) 403 { 404 405 buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial; 406 buf->buf = realloc(buf->buf, buf->sz); 407 if (NULL == buf->buf) { 408 perror(NULL); 409 exit((int)MANDOCLEVEL_SYSERR); 410 } 411 } 412 413 414 static int 415 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap) 416 { 417 struct stat st; 418 size_t off; 419 ssize_t ssz; 420 421 if (-1 == fstat(curp->fd, &st)) { 422 perror(curp->file); 423 return(0); 424 } 425 426 /* 427 * If we're a regular file, try just reading in the whole entry 428 * via mmap(). This is faster than reading it into blocks, and 429 * since each file is only a few bytes to begin with, I'm not 430 * concerned that this is going to tank any machines. 431 */ 432 433 if (S_ISREG(st.st_mode)) { 434 if (st.st_size >= (1U << 31)) { 435 fprintf(stderr, "%s: input too large\n", 436 curp->file); 437 return(0); 438 } 439 *with_mmap = 1; 440 fb->sz = (size_t)st.st_size; 441 fb->buf = mmap(NULL, fb->sz, PROT_READ, 442 MAP_FILE|MAP_SHARED, curp->fd, 0); 443 if (fb->buf != MAP_FAILED) 444 return(1); 445 } 446 447 /* 448 * If this isn't a regular file (like, say, stdin), then we must 449 * go the old way and just read things in bit by bit. 450 */ 451 452 *with_mmap = 0; 453 off = 0; 454 fb->sz = 0; 455 fb->buf = NULL; 456 for (;;) { 457 if (off == fb->sz) { 458 if (fb->sz == (1U << 31)) { 459 fprintf(stderr, "%s: input too large\n", 460 curp->file); 461 break; 462 } 463 resize_buf(fb, 65536); 464 } 465 ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off); 466 if (ssz == 0) { 467 fb->sz = off; 468 return(1); 469 } 470 if (ssz == -1) { 471 perror(curp->file); 472 break; 473 } 474 off += (size_t)ssz; 475 } 476 477 free(fb->buf); 478 fb->buf = NULL; 479 return(0); 480 } 481 482 483 static void 484 fdesc(struct curparse *curp) 485 { 486 487 /* 488 * Called once per file with an opened file descriptor. All 489 * pre-file-parse operations (whether stdin or a file) should go 490 * here. 491 * 492 * This calls down into the nested parser, which drills down and 493 * fully parses a file and all its dependences (i.e., `so'). It 494 * then runs the cleanup validators and pushes to output. 495 */ 496 497 /* Zero the parse type. */ 498 499 curp->mdoc = NULL; 500 curp->man = NULL; 501 file_status = MANDOCLEVEL_OK; 502 503 /* Make sure the mandotory roff parser is initialised. */ 504 505 if (NULL == curp->roff) { 506 curp->roff = roff_alloc(&curp->regs, curp, mmsg); 507 assert(curp->roff); 508 } 509 510 /* Fully parse the file. */ 511 512 pdesc(curp); 513 514 if (MANDOCLEVEL_FATAL <= file_status) 515 goto cleanup; 516 517 /* NOTE a parser may not have been assigned, yet. */ 518 519 if ( ! (curp->man || curp->mdoc)) { 520 fprintf(stderr, "%s: Not a manual\n", curp->file); 521 file_status = MANDOCLEVEL_FATAL; 522 goto cleanup; 523 } 524 525 /* Clean up the parse routine ASTs. */ 526 527 if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) { 528 assert(MANDOCLEVEL_FATAL <= file_status); 529 goto cleanup; 530 } 531 532 if (curp->man && ! man_endparse(curp->man)) { 533 assert(MANDOCLEVEL_FATAL <= file_status); 534 goto cleanup; 535 } 536 537 assert(curp->roff); 538 roff_endparse(curp->roff); 539 540 /* 541 * With -Wstop and warnings or errors of at least 542 * the requested level, do not produce output. 543 */ 544 545 if (MANDOCLEVEL_OK != file_status && curp->wstop) 546 goto cleanup; 547 548 /* If unset, allocate output dev now (if applicable). */ 549 550 if ( ! (curp->outman && curp->outmdoc)) { 551 switch (curp->outtype) { 552 case (OUTT_XHTML): 553 curp->outdata = xhtml_alloc(curp->outopts); 554 break; 555 case (OUTT_HTML): 556 curp->outdata = html_alloc(curp->outopts); 557 break; 558 case (OUTT_ASCII): 559 curp->outdata = ascii_alloc(curp->outopts); 560 curp->outfree = ascii_free; 561 break; 562 case (OUTT_PDF): 563 curp->outdata = pdf_alloc(curp->outopts); 564 curp->outfree = pspdf_free; 565 break; 566 case (OUTT_PS): 567 curp->outdata = ps_alloc(curp->outopts); 568 curp->outfree = pspdf_free; 569 break; 570 default: 571 break; 572 } 573 574 switch (curp->outtype) { 575 case (OUTT_HTML): 576 /* FALLTHROUGH */ 577 case (OUTT_XHTML): 578 curp->outman = html_man; 579 curp->outmdoc = html_mdoc; 580 curp->outfree = html_free; 581 break; 582 case (OUTT_TREE): 583 curp->outman = tree_man; 584 curp->outmdoc = tree_mdoc; 585 break; 586 case (OUTT_PDF): 587 /* FALLTHROUGH */ 588 case (OUTT_ASCII): 589 /* FALLTHROUGH */ 590 case (OUTT_PS): 591 curp->outman = terminal_man; 592 curp->outmdoc = terminal_mdoc; 593 break; 594 default: 595 break; 596 } 597 } 598 599 /* Execute the out device, if it exists. */ 600 601 if (curp->man && curp->outman) 602 (*curp->outman)(curp->outdata, curp->man); 603 if (curp->mdoc && curp->outmdoc) 604 (*curp->outmdoc)(curp->outdata, curp->mdoc); 605 606 cleanup: 607 608 memset(&curp->regs, 0, sizeof(struct regset)); 609 610 /* Reset the current-parse compilers. */ 611 612 if (curp->mdoc) 613 mdoc_reset(curp->mdoc); 614 if (curp->man) 615 man_reset(curp->man); 616 617 assert(curp->roff); 618 roff_reset(curp->roff); 619 620 if (exit_status < file_status) 621 exit_status = file_status; 622 623 return; 624 } 625 626 static void 627 pdesc(struct curparse *curp) 628 { 629 struct buf blk; 630 int with_mmap; 631 632 /* 633 * Run for each opened file; may be called more than once for 634 * each full parse sequence if the opened file is nested (i.e., 635 * from `so'). Simply sucks in the whole file and moves into 636 * the parse phase for the file. 637 */ 638 639 if ( ! read_whole_file(curp, &blk, &with_mmap)) { 640 file_status = MANDOCLEVEL_SYSERR; 641 return; 642 } 643 644 /* Line number is per-file. */ 645 646 curp->line = 1; 647 648 parsebuf(curp, blk, 1); 649 650 if (with_mmap) 651 munmap(blk.buf, blk.sz); 652 else 653 free(blk.buf); 654 } 655 656 static void 657 parsebuf(struct curparse *curp, struct buf blk, int start) 658 { 659 struct buf ln; 660 enum rofferr rr; 661 int i, of, rc; 662 int pos; /* byte number in the ln buffer */ 663 int lnn; /* line number in the real file */ 664 unsigned char c; 665 666 /* 667 * Main parse routine for an opened file. This is called for 668 * each opened file and simply loops around the full input file, 669 * possibly nesting (i.e., with `so'). 670 */ 671 672 memset(&ln, 0, sizeof(struct buf)); 673 674 lnn = curp->line; 675 pos = 0; 676 677 for (i = 0; i < (int)blk.sz; ) { 678 if (0 == pos && '\0' == blk.buf[i]) 679 break; 680 681 if (start) { 682 curp->line = lnn; 683 curp->reparse_count = 0; 684 } 685 686 while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) { 687 if ('\n' == blk.buf[i]) { 688 ++i; 689 ++lnn; 690 break; 691 } 692 693 /* 694 * Warn about bogus characters. If you're using 695 * non-ASCII encoding, you're screwing your 696 * readers. Since I'd rather this not happen, 697 * I'll be helpful and drop these characters so 698 * we don't display gibberish. Note to manual 699 * writers: use special characters. 700 */ 701 702 c = (unsigned char) blk.buf[i]; 703 704 if ( ! (isascii(c) && 705 (isgraph(c) || isblank(c)))) { 706 mmsg(MANDOCERR_BADCHAR, curp, 707 curp->line, pos, "ignoring byte"); 708 i++; 709 continue; 710 } 711 712 /* Trailing backslash = a plain char. */ 713 714 if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { 715 if (pos >= (int)ln.sz) 716 resize_buf(&ln, 256); 717 ln.buf[pos++] = blk.buf[i++]; 718 continue; 719 } 720 721 /* Found escape & at least one other char. */ 722 723 if ('\n' == blk.buf[i + 1]) { 724 i += 2; 725 /* Escaped newlines are skipped over */ 726 ++lnn; 727 continue; 728 } 729 730 if ('"' == blk.buf[i + 1]) { 731 i += 2; 732 /* Comment, skip to end of line */ 733 for (; i < (int)blk.sz; ++i) { 734 if ('\n' == blk.buf[i]) { 735 ++i; 736 ++lnn; 737 break; 738 } 739 } 740 741 /* Backout trailing whitespaces */ 742 for (; pos > 0; --pos) { 743 if (ln.buf[pos - 1] != ' ') 744 break; 745 if (pos > 2 && ln.buf[pos - 2] == '\\') 746 break; 747 } 748 break; 749 } 750 751 /* Some other escape sequence, copy & cont. */ 752 753 if (pos + 1 >= (int)ln.sz) 754 resize_buf(&ln, 256); 755 756 ln.buf[pos++] = blk.buf[i++]; 757 ln.buf[pos++] = blk.buf[i++]; 758 } 759 760 if (pos >= (int)ln.sz) 761 resize_buf(&ln, 256); 762 763 ln.buf[pos] = '\0'; 764 765 /* 766 * A significant amount of complexity is contained by 767 * the roff preprocessor. It's line-oriented but can be 768 * expressed on one line, so we need at times to 769 * readjust our starting point and re-run it. The roff 770 * preprocessor can also readjust the buffers with new 771 * data, so we pass them in wholesale. 772 */ 773 774 of = 0; 775 776 rerun: 777 rr = roff_parseln 778 (curp->roff, curp->line, 779 &ln.buf, &ln.sz, of, &of); 780 781 switch (rr) { 782 case (ROFF_REPARSE): 783 if (REPARSE_LIMIT >= ++curp->reparse_count) 784 parsebuf(curp, ln, 0); 785 else 786 mmsg(MANDOCERR_ROFFLOOP, curp, 787 curp->line, pos, NULL); 788 pos = 0; 789 continue; 790 case (ROFF_APPEND): 791 pos = strlen(ln.buf); 792 continue; 793 case (ROFF_RERUN): 794 goto rerun; 795 case (ROFF_IGN): 796 pos = 0; 797 continue; 798 case (ROFF_ERR): 799 assert(MANDOCLEVEL_FATAL <= file_status); 800 break; 801 case (ROFF_SO): 802 if (pfile(ln.buf + of, curp)) { 803 pos = 0; 804 continue; 805 } else 806 break; 807 default: 808 break; 809 } 810 811 /* 812 * If we encounter errors in the recursive parsebuf() 813 * call, make sure we don't continue parsing. 814 */ 815 816 if (MANDOCLEVEL_FATAL <= file_status) 817 break; 818 819 /* 820 * If input parsers have not been allocated, do so now. 821 * We keep these instanced betwen parsers, but set them 822 * locally per parse routine since we can use different 823 * parsers with each one. 824 */ 825 826 if ( ! (curp->man || curp->mdoc)) 827 pset(ln.buf + of, pos - of, curp); 828 829 /* 830 * Lastly, push down into the parsers themselves. One 831 * of these will have already been set in the pset() 832 * routine. 833 * If libroff returns ROFF_TBL, then add it to the 834 * currently open parse. Since we only get here if 835 * there does exist data (see tbl_data.c), we're 836 * guaranteed that something's been allocated. 837 */ 838 839 if (ROFF_TBL == rr) { 840 assert(curp->man || curp->mdoc); 841 if (curp->man) 842 man_addspan(curp->man, roff_span(curp->roff)); 843 else 844 mdoc_addspan(curp->mdoc, roff_span(curp->roff)); 845 846 } else if (curp->man || curp->mdoc) { 847 rc = curp->man ? 848 man_parseln(curp->man, 849 curp->line, ln.buf, of) : 850 mdoc_parseln(curp->mdoc, 851 curp->line, ln.buf, of); 852 853 if ( ! rc) { 854 assert(MANDOCLEVEL_FATAL <= file_status); 855 break; 856 } 857 } 858 859 /* Temporary buffers typically are not full. */ 860 861 if (0 == start && '\0' == blk.buf[i]) 862 break; 863 864 /* Start the next input line. */ 865 866 pos = 0; 867 } 868 869 free(ln.buf); 870 } 871 872 static void 873 pset(const char *buf, int pos, struct curparse *curp) 874 { 875 int i; 876 877 /* 878 * Try to intuit which kind of manual parser should be used. If 879 * passed in by command-line (-man, -mdoc), then use that 880 * explicitly. If passed as -mandoc, then try to guess from the 881 * line: either skip dot-lines, use -mdoc when finding `.Dt', or 882 * default to -man, which is more lenient. 883 * 884 * Separate out pmdoc/pman from mdoc/man: the first persists 885 * through all parsers, while the latter is used per-parse. 886 */ 887 888 if ('.' == buf[0] || '\'' == buf[0]) { 889 for (i = 1; buf[i]; i++) 890 if (' ' != buf[i] && '\t' != buf[i]) 891 break; 892 if ('\0' == buf[i]) 893 return; 894 } 895 896 switch (curp->inttype) { 897 case (INTT_MDOC): 898 if (NULL == curp->pmdoc) 899 curp->pmdoc = mdoc_alloc 900 (&curp->regs, curp, mmsg); 901 assert(curp->pmdoc); 902 curp->mdoc = curp->pmdoc; 903 return; 904 case (INTT_MAN): 905 if (NULL == curp->pman) 906 curp->pman = man_alloc 907 (&curp->regs, curp, mmsg); 908 assert(curp->pman); 909 curp->man = curp->pman; 910 return; 911 default: 912 break; 913 } 914 915 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { 916 if (NULL == curp->pmdoc) 917 curp->pmdoc = mdoc_alloc 918 (&curp->regs, curp, mmsg); 919 assert(curp->pmdoc); 920 curp->mdoc = curp->pmdoc; 921 return; 922 } 923 924 if (NULL == curp->pman) 925 curp->pman = man_alloc(&curp->regs, curp, mmsg); 926 assert(curp->pman); 927 curp->man = curp->pman; 928 } 929 930 static int 931 moptions(enum intt *tflags, char *arg) 932 { 933 934 if (0 == strcmp(arg, "doc")) 935 *tflags = INTT_MDOC; 936 else if (0 == strcmp(arg, "andoc")) 937 *tflags = INTT_AUTO; 938 else if (0 == strcmp(arg, "an")) 939 *tflags = INTT_MAN; 940 else { 941 fprintf(stderr, "%s: Bad argument\n", arg); 942 return(0); 943 } 944 945 return(1); 946 } 947 948 static int 949 toptions(struct curparse *curp, char *arg) 950 { 951 952 if (0 == strcmp(arg, "ascii")) 953 curp->outtype = OUTT_ASCII; 954 else if (0 == strcmp(arg, "lint")) { 955 curp->outtype = OUTT_LINT; 956 curp->wlevel = MANDOCLEVEL_WARNING; 957 } 958 else if (0 == strcmp(arg, "tree")) 959 curp->outtype = OUTT_TREE; 960 else if (0 == strcmp(arg, "html")) 961 curp->outtype = OUTT_HTML; 962 else if (0 == strcmp(arg, "xhtml")) 963 curp->outtype = OUTT_XHTML; 964 else if (0 == strcmp(arg, "ps")) 965 curp->outtype = OUTT_PS; 966 else if (0 == strcmp(arg, "pdf")) 967 curp->outtype = OUTT_PDF; 968 else { 969 fprintf(stderr, "%s: Bad argument\n", arg); 970 return(0); 971 } 972 973 return(1); 974 } 975 976 static int 977 woptions(struct curparse *curp, char *arg) 978 { 979 char *v, *o; 980 const char *toks[6]; 981 982 toks[0] = "stop"; 983 toks[1] = "all"; 984 toks[2] = "warning"; 985 toks[3] = "error"; 986 toks[4] = "fatal"; 987 toks[5] = NULL; 988 989 while (*arg) { 990 o = arg; 991 switch (getsubopt(&arg, UNCONST(toks), &v)) { 992 case (0): 993 curp->wstop = 1; 994 break; 995 case (1): 996 /* FALLTHROUGH */ 997 case (2): 998 curp->wlevel = MANDOCLEVEL_WARNING; 999 break; 1000 case (3): 1001 curp->wlevel = MANDOCLEVEL_ERROR; 1002 break; 1003 case (4): 1004 curp->wlevel = MANDOCLEVEL_FATAL; 1005 break; 1006 default: 1007 fprintf(stderr, "-W%s: Bad argument\n", o); 1008 return(0); 1009 } 1010 } 1011 1012 return(1); 1013 } 1014 1015 static int 1016 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg) 1017 { 1018 struct curparse *cp; 1019 enum mandoclevel level; 1020 1021 level = MANDOCLEVEL_FATAL; 1022 while (t < mandoclimits[level]) 1023 /* LINTED */ 1024 level--; 1025 1026 cp = (struct curparse *)arg; 1027 if (level < cp->wlevel) 1028 return(1); 1029 1030 fprintf(stderr, "%s:%d:%d: %s: %s", 1031 cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]); 1032 if (msg) 1033 fprintf(stderr, ": %s", msg); 1034 fputc('\n', stderr); 1035 1036 if (file_status < level) 1037 file_status = level; 1038 1039 return(level < MANDOCLEVEL_FATAL); 1040 } 1041