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