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