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