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