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