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