Annotation of src/usr.bin/mandoc/main.c, Revision 1.88
1.88 ! schwarze 1: /* $Id: main.c,v 1.87 2014/03/19 21:50:59 schwarze Exp $ */
1.1 kristaps 2: /*
1.64 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.87 schwarze 4: * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
1.86 schwarze 5: * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
1.1 kristaps 6: *
7: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 10: *
1.2 schwarze 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 18: */
19:
20: #include <assert.h>
21: #include <stdio.h>
1.17 schwarze 22: #include <stdint.h>
1.1 kristaps 23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
26:
1.30 schwarze 27: #include "mandoc.h"
1.38 schwarze 28: #include "main.h"
1.1 kristaps 29: #include "mdoc.h"
30: #include "man.h"
31:
1.16 schwarze 32: typedef void (*out_mdoc)(void *, const struct mdoc *);
33: typedef void (*out_man)(void *, const struct man *);
1.1 kristaps 34: typedef void (*out_free)(void *);
35:
36: enum outt {
1.76 schwarze 37: OUTT_ASCII = 0, /* -Tascii */
1.77 schwarze 38: OUTT_LOCALE, /* -Tlocale */
39: OUTT_UTF8, /* -Tutf8 */
1.76 schwarze 40: OUTT_TREE, /* -Ttree */
1.78 schwarze 41: OUTT_MAN, /* -Tman */
1.76 schwarze 42: OUTT_HTML, /* -Thtml */
43: OUTT_XHTML, /* -Txhtml */
44: OUTT_LINT, /* -Tlint */
45: OUTT_PS, /* -Tps */
46: OUTT_PDF /* -Tpdf */
1.1 kristaps 47: };
48:
49: struct curparse {
1.76 schwarze 50: struct mparse *mp;
1.75 schwarze 51: enum mandoclevel wlevel; /* ignore messages below this */
52: int wstop; /* stop after a file with a warning */
1.33 schwarze 53: enum outt outtype; /* which output to use */
54: out_mdoc outmdoc; /* mdoc output ptr */
55: out_man outman; /* man output ptr */
56: out_free outfree; /* free output ptr */
57: void *outdata; /* data for output */
58: char outopts[BUFSIZ]; /* buf of output opts */
59: };
60:
1.79 schwarze 61: int apropos(int, char**);
62: int mandocdb(int, char**);
63:
1.87 schwarze 64: static int moptions(int *, char *);
1.76 schwarze 65: static void mmsg(enum mandocerr, enum mandoclevel,
66: const char *, int, int, const char *);
67: static void parse(struct curparse *, int,
68: const char *, enum mandoclevel *);
1.27 schwarze 69: static int toptions(struct curparse *, char *);
70: static void usage(void) __attribute__((noreturn));
1.20 schwarze 71: static void version(void) __attribute__((noreturn));
1.45 schwarze 72: static int woptions(struct curparse *, char *);
1.1 kristaps 73:
1.19 schwarze 74: static const char *progname;
1.1 kristaps 75:
76: int
77: main(int argc, char *argv[])
78: {
1.27 schwarze 79: int c;
1.1 kristaps 80: struct curparse curp;
1.87 schwarze 81: int options;
1.76 schwarze 82: enum mandoclevel rc;
1.83 schwarze 83: char *defos;
1.1 kristaps 84:
1.19 schwarze 85: progname = strrchr(argv[0], '/');
86: if (progname == NULL)
87: progname = argv[0];
88: else
89: ++progname;
1.79 schwarze 90:
1.81 schwarze 91: if (0 == strncmp(progname, "apropos", 7) ||
92: 0 == strncmp(progname, "whatis", 6))
1.79 schwarze 93: return(apropos(argc, argv));
1.82 schwarze 94: if (0 == strncmp(progname, "mandocdb", 8) ||
95: 0 == strncmp(progname, "makewhatis", 10))
1.79 schwarze 96: return(mandocdb(argc, argv));
1.19 schwarze 97:
98: memset(&curp, 0, sizeof(struct curparse));
1.1 kristaps 99:
1.87 schwarze 100: options = MPARSE_SO;
1.1 kristaps 101: curp.outtype = OUTT_ASCII;
1.45 schwarze 102: curp.wlevel = MANDOCLEVEL_FATAL;
1.83 schwarze 103: defos = NULL;
1.1 kristaps 104:
105: /* LINTED */
1.83 schwarze 106: while (-1 != (c = getopt(argc, argv, "I:m:O:T:VW:")))
1.1 kristaps 107: switch (c) {
1.83 schwarze 108: case ('I'):
109: if (strncmp(optarg, "os=", 3)) {
110: fprintf(stderr, "-I%s: Bad argument\n",
111: optarg);
112: return((int)MANDOCLEVEL_BADARG);
113: }
114: if (defos) {
115: fprintf(stderr, "-I%s: Duplicate argument\n",
116: optarg);
117: return((int)MANDOCLEVEL_BADARG);
118: }
119: defos = mandoc_strdup(optarg + 3);
120: break;
1.1 kristaps 121: case ('m'):
1.87 schwarze 122: if ( ! moptions(&options, optarg))
1.47 schwarze 123: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 124: break;
1.18 schwarze 125: case ('O'):
126: (void)strlcat(curp.outopts, optarg, BUFSIZ);
127: (void)strlcat(curp.outopts, ",", BUFSIZ);
1.17 schwarze 128: break;
1.1 kristaps 129: case ('T'):
1.22 schwarze 130: if ( ! toptions(&curp, optarg))
1.47 schwarze 131: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 132: break;
133: case ('W'):
1.45 schwarze 134: if ( ! woptions(&curp, optarg))
1.47 schwarze 135: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 136: break;
1.3 schwarze 137: case ('V'):
138: version();
139: /* NOTREACHED */
1.1 kristaps 140: default:
141: usage();
142: /* NOTREACHED */
143: }
144:
1.87 schwarze 145: curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos);
1.76 schwarze 146:
1.80 schwarze 147: /*
148: * Conditionally start up the lookaside buffer before parsing.
149: */
150: if (OUTT_MAN == curp.outtype)
151: mparse_keep(curp.mp);
152:
1.1 kristaps 153: argc -= optind;
154: argv += optind;
155:
1.76 schwarze 156: rc = MANDOCLEVEL_OK;
1.14 schwarze 157:
1.76 schwarze 158: if (NULL == *argv)
159: parse(&curp, STDIN_FILENO, "<stdin>", &rc);
1.27 schwarze 160:
161: while (*argv) {
1.76 schwarze 162: parse(&curp, -1, *argv, &rc);
163: if (MANDOCLEVEL_OK != rc && curp.wstop)
1.27 schwarze 164: break;
165: ++argv;
1.1 kristaps 166: }
167:
168: if (curp.outfree)
169: (*curp.outfree)(curp.outdata);
1.76 schwarze 170: if (curp.mp)
171: mparse_free(curp.mp);
1.83 schwarze 172: free(defos);
1.1 kristaps 173:
1.76 schwarze 174: return((int)rc);
1.1 kristaps 175: }
176:
1.20 schwarze 177: static void
1.3 schwarze 178: version(void)
179: {
180:
1.76 schwarze 181: printf("%s %s\n", progname, VERSION);
1.47 schwarze 182: exit((int)MANDOCLEVEL_OK);
1.3 schwarze 183: }
184:
1.20 schwarze 185: static void
1.1 kristaps 186: usage(void)
187: {
188:
1.76 schwarze 189: fprintf(stderr, "usage: %s "
1.59 schwarze 190: "[-V] "
1.84 jmc 191: "[-Ios=name] "
1.59 schwarze 192: "[-mformat] "
193: "[-Ooption] "
194: "[-Toutput] "
1.84 jmc 195: "[-Wlevel]\n"
196: "\t [file ...]\n",
1.59 schwarze 197: progname);
198:
1.47 schwarze 199: exit((int)MANDOCLEVEL_BADARG);
1.1 kristaps 200: }
201:
1.27 schwarze 202: static void
1.76 schwarze 203: parse(struct curparse *curp, int fd,
204: const char *file, enum mandoclevel *level)
1.52 schwarze 205: {
1.76 schwarze 206: enum mandoclevel rc;
207: struct mdoc *mdoc;
208: struct man *man;
1.52 schwarze 209:
1.76 schwarze 210: /* Begin by parsing the file itself. */
1.52 schwarze 211:
1.76 schwarze 212: assert(file);
213: assert(fd >= -1);
1.52 schwarze 214:
1.76 schwarze 215: rc = mparse_readfd(curp->mp, fd, file);
1.1 kristaps 216:
1.76 schwarze 217: /* Stop immediately if the parse has failed. */
1.1 kristaps 218:
1.76 schwarze 219: if (MANDOCLEVEL_FATAL <= rc)
1.51 schwarze 220: goto cleanup;
221:
222: /*
1.76 schwarze 223: * With -Wstop and warnings or errors of at least the requested
224: * level, do not produce output.
1.51 schwarze 225: */
226:
1.76 schwarze 227: if (MANDOCLEVEL_OK != rc && curp->wstop)
1.51 schwarze 228: goto cleanup;
229:
230: /* If unset, allocate output dev now (if applicable). */
231:
232: if ( ! (curp->outman && curp->outmdoc)) {
233: switch (curp->outtype) {
234: case (OUTT_XHTML):
235: curp->outdata = xhtml_alloc(curp->outopts);
1.77 schwarze 236: curp->outfree = html_free;
1.51 schwarze 237: break;
238: case (OUTT_HTML):
239: curp->outdata = html_alloc(curp->outopts);
1.77 schwarze 240: curp->outfree = html_free;
241: break;
242: case (OUTT_UTF8):
243: curp->outdata = utf8_alloc(curp->outopts);
244: curp->outfree = ascii_free;
245: break;
246: case (OUTT_LOCALE):
247: curp->outdata = locale_alloc(curp->outopts);
248: curp->outfree = ascii_free;
1.51 schwarze 249: break;
250: case (OUTT_ASCII):
251: curp->outdata = ascii_alloc(curp->outopts);
252: curp->outfree = ascii_free;
253: break;
254: case (OUTT_PDF):
255: curp->outdata = pdf_alloc(curp->outopts);
256: curp->outfree = pspdf_free;
257: break;
258: case (OUTT_PS):
259: curp->outdata = ps_alloc(curp->outopts);
260: curp->outfree = pspdf_free;
261: break;
262: default:
263: break;
264: }
265:
266: switch (curp->outtype) {
267: case (OUTT_HTML):
268: /* FALLTHROUGH */
269: case (OUTT_XHTML):
270: curp->outman = html_man;
271: curp->outmdoc = html_mdoc;
272: break;
273: case (OUTT_TREE):
274: curp->outman = tree_man;
275: curp->outmdoc = tree_mdoc;
276: break;
1.78 schwarze 277: case (OUTT_MAN):
278: curp->outmdoc = man_mdoc;
1.80 schwarze 279: curp->outman = man_man;
1.78 schwarze 280: break;
1.51 schwarze 281: case (OUTT_PDF):
282: /* FALLTHROUGH */
283: case (OUTT_ASCII):
284: /* FALLTHROUGH */
1.77 schwarze 285: case (OUTT_UTF8):
286: /* FALLTHROUGH */
287: case (OUTT_LOCALE):
288: /* FALLTHROUGH */
1.51 schwarze 289: case (OUTT_PS):
290: curp->outman = terminal_man;
291: curp->outmdoc = terminal_mdoc;
292: break;
293: default:
294: break;
295: }
296: }
297:
1.88 ! schwarze 298: mparse_result(curp->mp, &mdoc, &man, NULL);
1.76 schwarze 299:
1.51 schwarze 300: /* Execute the out device, if it exists. */
301:
1.76 schwarze 302: if (man && curp->outman)
303: (*curp->outman)(curp->outdata, man);
304: if (mdoc && curp->outmdoc)
305: (*curp->outmdoc)(curp->outdata, mdoc);
1.51 schwarze 306:
307: cleanup:
1.59 schwarze 308:
1.76 schwarze 309: mparse_reset(curp->mp);
1.59 schwarze 310:
1.76 schwarze 311: if (*level < rc)
312: *level = rc;
1.1 kristaps 313: }
314:
315: static int
1.87 schwarze 316: moptions(int *options, char *arg)
1.1 kristaps 317: {
318:
319: if (0 == strcmp(arg, "doc"))
1.87 schwarze 320: *options |= MPARSE_MDOC;
1.1 kristaps 321: else if (0 == strcmp(arg, "andoc"))
1.87 schwarze 322: /* nothing to do */;
1.1 kristaps 323: else if (0 == strcmp(arg, "an"))
1.87 schwarze 324: *options |= MPARSE_MAN;
1.1 kristaps 325: else {
1.20 schwarze 326: fprintf(stderr, "%s: Bad argument\n", arg);
1.1 kristaps 327: return(0);
328: }
329:
330: return(1);
331: }
332:
333: static int
1.22 schwarze 334: toptions(struct curparse *curp, char *arg)
1.1 kristaps 335: {
336:
337: if (0 == strcmp(arg, "ascii"))
1.22 schwarze 338: curp->outtype = OUTT_ASCII;
339: else if (0 == strcmp(arg, "lint")) {
340: curp->outtype = OUTT_LINT;
1.45 schwarze 341: curp->wlevel = MANDOCLEVEL_WARNING;
1.76 schwarze 342: } else if (0 == strcmp(arg, "tree"))
1.22 schwarze 343: curp->outtype = OUTT_TREE;
1.78 schwarze 344: else if (0 == strcmp(arg, "man"))
345: curp->outtype = OUTT_MAN;
1.17 schwarze 346: else if (0 == strcmp(arg, "html"))
1.22 schwarze 347: curp->outtype = OUTT_HTML;
1.77 schwarze 348: else if (0 == strcmp(arg, "utf8"))
349: curp->outtype = OUTT_UTF8;
350: else if (0 == strcmp(arg, "locale"))
351: curp->outtype = OUTT_LOCALE;
1.21 schwarze 352: else if (0 == strcmp(arg, "xhtml"))
1.22 schwarze 353: curp->outtype = OUTT_XHTML;
1.36 schwarze 354: else if (0 == strcmp(arg, "ps"))
355: curp->outtype = OUTT_PS;
1.43 schwarze 356: else if (0 == strcmp(arg, "pdf"))
357: curp->outtype = OUTT_PDF;
1.1 kristaps 358: else {
1.20 schwarze 359: fprintf(stderr, "%s: Bad argument\n", arg);
1.1 kristaps 360: return(0);
361: }
362:
363: return(1);
364: }
365:
366: static int
1.45 schwarze 367: woptions(struct curparse *curp, char *arg)
1.1 kristaps 368: {
1.10 schwarze 369: char *v, *o;
1.45 schwarze 370: const char *toks[6];
1.1 kristaps 371:
1.45 schwarze 372: toks[0] = "stop";
373: toks[1] = "all";
374: toks[2] = "warning";
375: toks[3] = "error";
376: toks[4] = "fatal";
377: toks[5] = NULL;
1.1 kristaps 378:
1.10 schwarze 379: while (*arg) {
380: o = arg;
1.17 schwarze 381: switch (getsubopt(&arg, UNCONST(toks), &v)) {
1.1 kristaps 382: case (0):
1.45 schwarze 383: curp->wstop = 1;
1.1 kristaps 384: break;
385: case (1):
1.45 schwarze 386: /* FALLTHROUGH */
1.1 kristaps 387: case (2):
1.45 schwarze 388: curp->wlevel = MANDOCLEVEL_WARNING;
1.1 kristaps 389: break;
390: case (3):
1.45 schwarze 391: curp->wlevel = MANDOCLEVEL_ERROR;
1.1 kristaps 392: break;
393: case (4):
1.45 schwarze 394: curp->wlevel = MANDOCLEVEL_FATAL;
1.19 schwarze 395: break;
1.1 kristaps 396: default:
1.45 schwarze 397: fprintf(stderr, "-W%s: Bad argument\n", o);
1.1 kristaps 398: return(0);
399: }
1.10 schwarze 400: }
1.1 kristaps 401:
402: return(1);
403: }
404:
1.75 schwarze 405: static void
1.76 schwarze 406: mmsg(enum mandocerr t, enum mandoclevel lvl,
407: const char *file, int line, int col, const char *msg)
1.30 schwarze 408: {
1.45 schwarze 409:
1.76 schwarze 410: fprintf(stderr, "%s:%d:%d: %s: %s",
411: file, line, col + 1,
412: mparse_strlevel(lvl),
413: mparse_strerror(t));
1.30 schwarze 414:
415: if (msg)
416: fprintf(stderr, ": %s", msg);
1.76 schwarze 417:
1.30 schwarze 418: fputc('\n', stderr);
419: }