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