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