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