]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Correctly print `Lk' arguments in -Tascii. Issue raised by Aldis Berzoja.
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.102 2010/08/08 14:45:59 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 <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 unsigned char c;
470 struct man *man;
471 struct mdoc *mdoc;
472 struct roff *roff;
473
474 man = NULL;
475 mdoc = NULL;
476 roff = NULL;
477
478 memset(&ln, 0, sizeof(struct buf));
479
480 /*
481 * Two buffers: ln and buf. buf is the input file and may be
482 * memory mapped. ln is a line buffer and grows on-demand.
483 */
484
485 if ( ! read_whole_file(curp, &blk, &with_mmap))
486 return;
487
488 if (NULL == curp->roff)
489 curp->roff = roff_init(curp);
490 if (NULL == (roff = curp->roff))
491 goto bailout;
492
493 for (i = 0, lnn = 1; i < (int)blk.sz;) {
494 pos = 0;
495 lnn_start = lnn;
496 while (i < (int)blk.sz) {
497 if ('\n' == blk.buf[i]) {
498 ++i;
499 ++lnn;
500 break;
501 }
502
503 /*
504 * Warn about bogus characters. If you're using
505 * non-ASCII encoding, you're screwing your
506 * readers. Since I'd rather this not happen,
507 * I'll be helpful and drop these characters so
508 * we don't display gibberish. Note to manual
509 * writers: use special characters.
510 */
511
512 c = (unsigned char) blk.buf[i];
513 if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) {
514 if ( ! mmsg(MANDOCERR_BADCHAR, curp,
515 lnn_start, pos,
516 "ignoring byte"))
517 goto bailout;
518 i++;
519 continue;
520 }
521
522 /* Trailing backslash is like a plain character. */
523 if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
524 if (pos >= (int)ln.sz)
525 if (! resize_buf(&ln, 256))
526 goto bailout;
527 ln.buf[pos++] = blk.buf[i++];
528 continue;
529 }
530 /* Found an escape and at least one other character. */
531 if ('\n' == blk.buf[i + 1]) {
532 /* Escaped newlines are skipped over */
533 i += 2;
534 ++lnn;
535 continue;
536 }
537 if ('"' == blk.buf[i + 1]) {
538 i += 2;
539 /* Comment, skip to end of line */
540 for (; i < (int)blk.sz; ++i) {
541 if ('\n' == blk.buf[i]) {
542 ++i;
543 ++lnn;
544 break;
545 }
546 }
547 /* Backout trailing whitespaces */
548 for (; pos > 0; --pos) {
549 if (ln.buf[pos - 1] != ' ')
550 break;
551 if (pos > 2 && ln.buf[pos - 2] == '\\')
552 break;
553 }
554 break;
555 }
556 /* Some other escape sequence, copy and continue. */
557 if (pos + 1 >= (int)ln.sz)
558 if (! resize_buf(&ln, 256))
559 goto bailout;
560
561 ln.buf[pos++] = blk.buf[i++];
562 ln.buf[pos++] = blk.buf[i++];
563 }
564
565 if (pos >= (int)ln.sz)
566 if (! resize_buf(&ln, 256))
567 goto bailout;
568 ln.buf[pos] = '\0';
569
570 /*
571 * A significant amount of complexity is contained by
572 * the roff preprocessor. It's line-oriented but can be
573 * expressed on one line, so we need at times to
574 * readjust our starting point and re-run it. The roff
575 * preprocessor can also readjust the buffers with new
576 * data, so we pass them in wholesale.
577 */
578
579 of = 0;
580 do {
581 re = roff_parseln(roff, lnn_start,
582 &ln.buf, &ln.sz, of, &of);
583 } while (ROFF_RERUN == re);
584
585 if (ROFF_IGN == re)
586 continue;
587 else if (ROFF_ERR == re)
588 goto bailout;
589
590 /*
591 * If input parsers have not been allocated, do so now.
592 * We keep these instanced betwen parsers, but set them
593 * locally per parse routine since we can use different
594 * parsers with each one.
595 */
596
597 if ( ! (man || mdoc))
598 if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
599 goto bailout;
600
601 /* Lastly, push down into the parsers themselves. */
602
603 if (man && ! man_parseln(man, lnn_start, ln.buf, of))
604 goto bailout;
605 if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
606 goto bailout;
607 }
608
609 /* NOTE a parser may not have been assigned, yet. */
610
611 if ( ! (man || mdoc)) {
612 fprintf(stderr, "%s: Not a manual\n", curp->file);
613 goto bailout;
614 }
615
616 /* Clean up the parse routine ASTs. */
617
618 if (mdoc && ! mdoc_endparse(mdoc))
619 goto bailout;
620 if (man && ! man_endparse(man))
621 goto bailout;
622 if (roff && ! roff_endparse(roff))
623 goto bailout;
624
625 /* If unset, allocate output dev now (if applicable). */
626
627 if ( ! (curp->outman && curp->outmdoc)) {
628 switch (curp->outtype) {
629 case (OUTT_XHTML):
630 curp->outdata = xhtml_alloc(curp->outopts);
631 break;
632 case (OUTT_HTML):
633 curp->outdata = html_alloc(curp->outopts);
634 break;
635 case (OUTT_ASCII):
636 curp->outdata = ascii_alloc(curp->outopts);
637 curp->outfree = ascii_free;
638 break;
639 case (OUTT_PDF):
640 curp->outdata = pdf_alloc(curp->outopts);
641 curp->outfree = pspdf_free;
642 break;
643 case (OUTT_PS):
644 curp->outdata = ps_alloc(curp->outopts);
645 curp->outfree = pspdf_free;
646 break;
647 default:
648 break;
649 }
650
651 switch (curp->outtype) {
652 case (OUTT_HTML):
653 /* FALLTHROUGH */
654 case (OUTT_XHTML):
655 curp->outman = html_man;
656 curp->outmdoc = html_mdoc;
657 curp->outfree = html_free;
658 break;
659 case (OUTT_TREE):
660 curp->outman = tree_man;
661 curp->outmdoc = tree_mdoc;
662 break;
663 case (OUTT_PDF):
664 /* FALLTHROUGH */
665 case (OUTT_ASCII):
666 /* FALLTHROUGH */
667 case (OUTT_PS):
668 curp->outman = terminal_man;
669 curp->outmdoc = terminal_mdoc;
670 break;
671 default:
672 break;
673 }
674 }
675
676 /* Execute the out device, if it exists. */
677
678 if (man && curp->outman)
679 (*curp->outman)(curp->outdata, man);
680 if (mdoc && curp->outmdoc)
681 (*curp->outmdoc)(curp->outdata, mdoc);
682
683 cleanup:
684 memset(&curp->regs, 0, sizeof(struct regset));
685 if (mdoc)
686 mdoc_reset(mdoc);
687 if (man)
688 man_reset(man);
689 if (roff)
690 roff_reset(roff);
691 if (ln.buf)
692 free(ln.buf);
693 if (with_mmap)
694 munmap(blk.buf, blk.sz);
695 else
696 free(blk.buf);
697
698 return;
699
700 bailout:
701 with_fatal = 1;
702 goto cleanup;
703 }
704
705
706 static int
707 pset(const char *buf, int pos, struct curparse *curp,
708 struct man **man, struct mdoc **mdoc)
709 {
710 int i;
711
712 /*
713 * Try to intuit which kind of manual parser should be used. If
714 * passed in by command-line (-man, -mdoc), then use that
715 * explicitly. If passed as -mandoc, then try to guess from the
716 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
717 * default to -man, which is more lenient.
718 */
719
720 if ('.' == buf[0] || '\'' == buf[0]) {
721 for (i = 1; buf[i]; i++)
722 if (' ' != buf[i] && '\t' != buf[i])
723 break;
724 if (0 == buf[i])
725 return(1);
726 }
727
728 switch (curp->inttype) {
729 case (INTT_MDOC):
730 if (NULL == curp->mdoc)
731 curp->mdoc = mdoc_init(curp);
732 if (NULL == (*mdoc = curp->mdoc))
733 return(0);
734 return(1);
735 case (INTT_MAN):
736 if (NULL == curp->man)
737 curp->man = man_init(curp);
738 if (NULL == (*man = curp->man))
739 return(0);
740 return(1);
741 default:
742 break;
743 }
744
745 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
746 if (NULL == curp->mdoc)
747 curp->mdoc = mdoc_init(curp);
748 if (NULL == (*mdoc = curp->mdoc))
749 return(0);
750 return(1);
751 }
752
753 if (NULL == curp->man)
754 curp->man = man_init(curp);
755 if (NULL == (*man = curp->man))
756 return(0);
757 return(1);
758 }
759
760
761 static int
762 moptions(enum intt *tflags, char *arg)
763 {
764
765 if (0 == strcmp(arg, "doc"))
766 *tflags = INTT_MDOC;
767 else if (0 == strcmp(arg, "andoc"))
768 *tflags = INTT_AUTO;
769 else if (0 == strcmp(arg, "an"))
770 *tflags = INTT_MAN;
771 else {
772 fprintf(stderr, "%s: Bad argument\n", arg);
773 return(0);
774 }
775
776 return(1);
777 }
778
779
780 static int
781 toptions(struct curparse *curp, char *arg)
782 {
783
784 if (0 == strcmp(arg, "ascii"))
785 curp->outtype = OUTT_ASCII;
786 else if (0 == strcmp(arg, "lint")) {
787 curp->outtype = OUTT_LINT;
788 curp->wflags |= WARN_WALL;
789 curp->fflags |= FL_STRICT;
790 }
791 else if (0 == strcmp(arg, "tree"))
792 curp->outtype = OUTT_TREE;
793 else if (0 == strcmp(arg, "html"))
794 curp->outtype = OUTT_HTML;
795 else if (0 == strcmp(arg, "xhtml"))
796 curp->outtype = OUTT_XHTML;
797 else if (0 == strcmp(arg, "ps"))
798 curp->outtype = OUTT_PS;
799 else if (0 == strcmp(arg, "pdf"))
800 curp->outtype = OUTT_PDF;
801 else {
802 fprintf(stderr, "%s: Bad argument\n", arg);
803 return(0);
804 }
805
806 return(1);
807 }
808
809
810 static int
811 foptions(int *fflags, char *arg)
812 {
813 char *v, *o;
814 const char *toks[8];
815
816 toks[0] = "ign-scope";
817 toks[1] = "no-ign-escape";
818 toks[2] = "no-ign-macro";
819 toks[3] = "ign-errors";
820 toks[4] = "strict";
821 toks[5] = "ign-escape";
822 toks[6] = NULL;
823
824 while (*arg) {
825 o = arg;
826 switch (getsubopt(&arg, UNCONST(toks), &v)) {
827 case (0):
828 *fflags |= FL_IGN_SCOPE;
829 break;
830 case (1):
831 *fflags |= FL_NIGN_ESCAPE;
832 break;
833 case (2):
834 *fflags |= FL_NIGN_MACRO;
835 break;
836 case (3):
837 *fflags |= FL_IGN_ERRORS;
838 break;
839 case (4):
840 *fflags |= FL_STRICT;
841 break;
842 case (5):
843 *fflags &= ~FL_NIGN_ESCAPE;
844 break;
845 default:
846 fprintf(stderr, "%s: Bad argument\n", o);
847 return(0);
848 }
849 }
850
851 return(1);
852 }
853
854
855 static int
856 woptions(int *wflags, char *arg)
857 {
858 char *v, *o;
859 const char *toks[3];
860
861 toks[0] = "all";
862 toks[1] = "error";
863 toks[2] = NULL;
864
865 while (*arg) {
866 o = arg;
867 switch (getsubopt(&arg, UNCONST(toks), &v)) {
868 case (0):
869 *wflags |= WARN_WALL;
870 break;
871 case (1):
872 *wflags |= WARN_WERR;
873 break;
874 default:
875 fprintf(stderr, "%s: Bad argument\n", o);
876 return(0);
877 }
878 }
879
880 return(1);
881 }
882
883
884 static int
885 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
886 {
887 struct curparse *cp;
888 const char *level;
889 int rc;
890
891 cp = (struct curparse *)arg;
892 level = NULL;
893 rc = 1;
894
895 if (t >= MANDOCERR_FATAL) {
896 with_fatal = 1;
897 level = "FATAL";
898 rc = 0;
899 } else {
900 if ( ! (WARN_WALL & cp->wflags))
901 return(1);
902 if (t >= MANDOCERR_ERROR) {
903 with_error = 1;
904 level = "ERROR";
905 }
906 if (WARN_WERR & cp->wflags) {
907 with_fatal = 1;
908 rc = 0;
909 }
910 }
911
912 fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1);
913 if (level)
914 fprintf(stderr, " %s:", level);
915 fprintf(stderr, " %s", mandocerrs[t]);
916 if (msg)
917 fprintf(stderr, ": %s", msg);
918 fputc('\n', stderr);
919
920 return(rc);
921 }