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