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