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