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