Annotation of mandoc/main.c, Revision 1.180
1.180 ! schwarze 1: /* $Id: main.c,v 1.179 2014/08/16 23:04:25 schwarze Exp $ */
1.1 kristaps 2: /*
1.133 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.170 schwarze 4: * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
1.169 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.25 kristaps 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.25 kristaps 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: */
1.58 kristaps 19: #include "config.h"
1.178 schwarze 20:
21: #include <sys/types.h>
1.58 kristaps 22:
1.1 kristaps 23: #include <assert.h>
24: #include <stdio.h>
1.45 kristaps 25: #include <stdint.h>
1.1 kristaps 26: #include <stdlib.h>
27: #include <string.h>
28: #include <unistd.h>
29:
1.71 kristaps 30: #include "mandoc.h"
1.172 schwarze 31: #include "mandoc_aux.h"
1.90 kristaps 32: #include "main.h"
1.1 kristaps 33: #include "mdoc.h"
1.10 kristaps 34: #include "man.h"
1.180 ! schwarze 35: #include "manpath.h"
! 36: #include "mansearch.h"
1.101 joerg 37:
1.55 kristaps 38: #if !defined(__GNUC__) || (__GNUC__ < 2)
1.56 kristaps 39: # if !defined(lint)
40: # define __attribute__(x)
41: # endif
1.55 kristaps 42: #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
1.16 kristaps 43:
1.42 kristaps 44: typedef void (*out_mdoc)(void *, const struct mdoc *);
45: typedef void (*out_man)(void *, const struct man *);
1.22 kristaps 46: typedef void (*out_free)(void *);
47:
1.19 kristaps 48: enum outt {
1.154 kristaps 49: OUTT_ASCII = 0, /* -Tascii */
1.162 kristaps 50: OUTT_LOCALE, /* -Tlocale */
1.163 kristaps 51: OUTT_UTF8, /* -Tutf8 */
1.154 kristaps 52: OUTT_TREE, /* -Ttree */
1.164 schwarze 53: OUTT_MAN, /* -Tman */
1.154 kristaps 54: OUTT_HTML, /* -Thtml */
55: OUTT_XHTML, /* -Txhtml */
56: OUTT_LINT, /* -Tlint */
57: OUTT_PS, /* -Tps */
58: OUTT_PDF /* -Tpdf */
1.19 kristaps 59: };
60:
1.5 kristaps 61: struct curparse {
1.154 kristaps 62: struct mparse *mp;
1.148 kristaps 63: enum mandoclevel wlevel; /* ignore messages below this */
64: int wstop; /* stop after a file with a warning */
1.173 schwarze 65: enum outt outtype; /* which output to use */
1.79 kristaps 66: out_mdoc outmdoc; /* mdoc output ptr */
1.173 schwarze 67: out_man outman; /* man output ptr */
1.79 kristaps 68: out_free outfree; /* free output ptr */
69: void *outdata; /* data for output */
70: char outopts[BUFSIZ]; /* buf of output opts */
71: };
72:
1.170 schwarze 73: static int moptions(int *, char *);
1.155 kristaps 74: static void mmsg(enum mandocerr, enum mandoclevel,
75: const char *, int, int, const char *);
1.173 schwarze 76: static void parse(struct curparse *, int,
1.154 kristaps 77: const char *, enum mandoclevel *);
1.66 kristaps 78: static int toptions(struct curparse *, char *);
1.180 ! schwarze 79: static void usage(enum argmode) __attribute__((noreturn));
1.55 kristaps 80: static void version(void) __attribute__((noreturn));
1.103 schwarze 81: static int woptions(struct curparse *, char *);
1.1 kristaps 82:
1.54 kristaps 83: static const char *progname;
1.1 kristaps 84:
1.173 schwarze 85:
1.1 kristaps 86: int
87: main(int argc, char *argv[])
88: {
1.5 kristaps 89: struct curparse curp;
1.180 ! schwarze 90: struct mansearch search;
! 91: struct manpaths paths;
! 92: char *conf_file, *defpaths, *auxpaths;
! 93: char *defos;
! 94: #if HAVE_SQLITE3
! 95: struct manpage *res;
! 96: size_t i, sz;
! 97: #endif
! 98: enum mandoclevel rc;
! 99: int show_usage;
1.170 schwarze 100: int options;
1.180 ! schwarze 101: int c;
1.1 kristaps 102:
1.54 kristaps 103: progname = strrchr(argv[0], '/');
104: if (progname == NULL)
105: progname = argv[0];
106: else
107: ++progname;
1.179 schwarze 108:
1.180 ! schwarze 109: /* Search options. */
! 110:
! 111: memset(&paths, 0, sizeof(struct manpaths));
! 112: conf_file = defpaths = auxpaths = NULL;
! 113:
! 114: memset(&search, 0, sizeof(struct mansearch));
! 115: search.outkey = "Nd";
! 116:
! 117: if (strcmp(progname, "man") == 0)
! 118: search.argmode = ARG_NAME;
! 119: else if (strncmp(progname, "apropos", 7) == 0)
! 120: search.argmode = ARG_EXPR;
! 121: else if (strncmp(progname, "whatis", 6) == 0)
! 122: search.argmode = ARG_WORD;
! 123: else
! 124: search.argmode = ARG_FILE;
! 125:
! 126: /* Parser and formatter options. */
1.54 kristaps 127:
1.51 kristaps 128: memset(&curp, 0, sizeof(struct curparse));
1.22 kristaps 129: curp.outtype = OUTT_ASCII;
1.103 schwarze 130: curp.wlevel = MANDOCLEVEL_FATAL;
1.180 ! schwarze 131: options = MPARSE_SO;
1.166 schwarze 132: defos = NULL;
1.19 kristaps 133:
1.180 ! schwarze 134: show_usage = 0;
! 135: while (-1 != (c = getopt(argc, argv, "C:fI:kM:m:O:S:s:T:VW:"))) {
1.1 kristaps 136: switch (c) {
1.180 ! schwarze 137: case 'C':
! 138: conf_file = optarg;
! 139: break;
! 140: case 'f':
! 141: search.argmode = ARG_WORD;
! 142: break;
1.173 schwarze 143: case 'I':
1.166 schwarze 144: if (strncmp(optarg, "os=", 3)) {
1.173 schwarze 145: fprintf(stderr,
1.176 schwarze 146: "%s: -I%s: Bad argument\n",
147: progname, optarg);
1.166 schwarze 148: return((int)MANDOCLEVEL_BADARG);
149: }
150: if (defos) {
1.173 schwarze 151: fprintf(stderr,
1.176 schwarze 152: "%s: -I%s: Duplicate argument\n",
153: progname, optarg);
1.166 schwarze 154: return((int)MANDOCLEVEL_BADARG);
155: }
156: defos = mandoc_strdup(optarg + 3);
157: break;
1.180 ! schwarze 158: case 'k':
! 159: search.argmode = ARG_EXPR;
! 160: break;
! 161: case 'M':
! 162: defpaths = optarg;
! 163: break;
1.173 schwarze 164: case 'm':
1.180 ! schwarze 165: auxpaths = optarg;
1.10 kristaps 166: break;
1.173 schwarze 167: case 'O':
1.180 ! schwarze 168: search.outkey = optarg;
1.49 kristaps 169: (void)strlcat(curp.outopts, optarg, BUFSIZ);
170: (void)strlcat(curp.outopts, ",", BUFSIZ);
1.44 kristaps 171: break;
1.180 ! schwarze 172: case 'S':
! 173: search.arch = optarg;
! 174: break;
! 175: case 's':
! 176: search.sec = optarg;
! 177: break;
1.173 schwarze 178: case 'T':
1.60 kristaps 179: if ( ! toptions(&curp, optarg))
1.105 kristaps 180: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 181: break;
1.173 schwarze 182: case 'W':
1.103 schwarze 183: if ( ! woptions(&curp, optarg))
1.105 kristaps 184: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 185: break;
1.173 schwarze 186: case 'V':
1.1 kristaps 187: version();
188: /* NOTREACHED */
189: default:
1.180 ! schwarze 190: show_usage = 1;
! 191: break;
1.1 kristaps 192: }
1.180 ! schwarze 193: }
! 194:
! 195: if (show_usage)
! 196: usage(search.argmode);
! 197:
! 198: argc -= optind;
! 199: argv += optind;
! 200:
! 201: /* man(1), whatis(1), apropos(1) */
! 202:
! 203: if (search.argmode != ARG_FILE) {
! 204: #if HAVE_SQLITE3
! 205: if (argc == 0)
! 206: usage(search.argmode);
! 207: manpath_parse(&paths, conf_file, defpaths, auxpaths);
! 208: mansearch_setup(1);
! 209: if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
! 210: usage(search.argmode);
! 211: manpath_free(&paths);
! 212: for (i = 0; i < sz; i++)
! 213: printf("%s - %s\n", res[i].names,
! 214: res[i].output == NULL ? "" : res[i].output);
! 215: mansearch_free(res, sz);
! 216: mansearch_setup(0);
! 217: return((int)MANDOCLEVEL_OK);
! 218: #else
! 219: fputs("mandoc: database support not compiled in\n",
! 220: stderr);
! 221: return((int)MANDOCLEVEL_BADARG);
! 222: #endif
! 223: }
! 224:
! 225: /* mandoc(1) */
! 226:
! 227: if ( ! moptions(&options, auxpaths))
! 228: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 229:
1.170 schwarze 230: curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos);
1.154 kristaps 231:
1.165 kristaps 232: /*
233: * Conditionally start up the lookaside buffer before parsing.
234: */
235: if (OUTT_MAN == curp.outtype)
236: mparse_keep(curp.mp);
237:
1.154 kristaps 238: rc = MANDOCLEVEL_OK;
1.39 kristaps 239:
1.154 kristaps 240: if (NULL == *argv)
241: parse(&curp, STDIN_FILENO, "<stdin>", &rc);
1.64 kristaps 242:
243: while (*argv) {
1.154 kristaps 244: parse(&curp, -1, *argv, &rc);
245: if (MANDOCLEVEL_OK != rc && curp.wstop)
1.64 kristaps 246: break;
247: ++argv;
1.1 kristaps 248: }
249:
1.22 kristaps 250: if (curp.outfree)
251: (*curp.outfree)(curp.outdata);
1.154 kristaps 252: if (curp.mp)
253: mparse_free(curp.mp);
1.166 schwarze 254: free(defos);
1.1 kristaps 255:
1.154 kristaps 256: return((int)rc);
1.1 kristaps 257: }
258:
1.55 kristaps 259: static void
1.1 kristaps 260: version(void)
261: {
262:
1.180 ! schwarze 263: printf("mandoc %s\n", VERSION);
1.105 kristaps 264: exit((int)MANDOCLEVEL_OK);
1.1 kristaps 265: }
266:
1.55 kristaps 267: static void
1.180 ! schwarze 268: usage(enum argmode argmode)
1.1 kristaps 269: {
270:
1.180 ! schwarze 271: switch (argmode) {
! 272: case ARG_FILE:
! 273: fputs("usage: mandoc [-V] [-Ios=name] [-mformat]"
! 274: " [-Ooption] [-Toutput] [-Wlevel]\n"
! 275: "\t [file ...]\n", stderr);
! 276: break;
! 277: case ARG_NAME:
! 278: fputs("usage: man [-acfhkVw] [-C file] "
! 279: "[-M path] [-m path] [-S arch] [-s section]\n"
! 280: "\t [section] name ...\n", stderr);
! 281: break;
! 282: case ARG_WORD:
! 283: fputs("usage: whatis [-V] [-C file] [-M path] [-m path] "
! 284: "[-S arch] [-s section] name ...\n", stderr);
! 285: break;
! 286: case ARG_EXPR:
! 287: fputs("usage: apropos [-V] [-C file] [-M path] [-m path] "
! 288: "[-O outkey] [-S arch]\n"
! 289: "\t [-s section] expression ...\n", stderr);
! 290: break;
! 291: }
1.105 kristaps 292: exit((int)MANDOCLEVEL_BADARG);
1.68 joerg 293: }
294:
1.64 kristaps 295: static void
1.173 schwarze 296: parse(struct curparse *curp, int fd, const char *file,
297: enum mandoclevel *level)
1.1 kristaps 298: {
1.154 kristaps 299: enum mandoclevel rc;
300: struct mdoc *mdoc;
301: struct man *man;
1.112 kristaps 302:
1.154 kristaps 303: /* Begin by parsing the file itself. */
1.112 kristaps 304:
1.154 kristaps 305: assert(file);
306: assert(fd >= -1);
1.112 kristaps 307:
1.154 kristaps 308: rc = mparse_readfd(curp->mp, fd, file);
1.112 kristaps 309:
1.154 kristaps 310: /* Stop immediately if the parse has failed. */
1.92 kristaps 311:
1.154 kristaps 312: if (MANDOCLEVEL_FATAL <= rc)
1.111 kristaps 313: goto cleanup;
314:
1.1 kristaps 315: /*
1.154 kristaps 316: * With -Wstop and warnings or errors of at least the requested
317: * level, do not produce output.
1.1 kristaps 318: */
319:
1.154 kristaps 320: if (MANDOCLEVEL_OK != rc && curp->wstop)
1.111 kristaps 321: goto cleanup;
322:
323: /* If unset, allocate output dev now (if applicable). */
324:
325: if ( ! (curp->outman && curp->outmdoc)) {
326: switch (curp->outtype) {
1.173 schwarze 327: case OUTT_XHTML:
1.111 kristaps 328: curp->outdata = xhtml_alloc(curp->outopts);
1.162 kristaps 329: curp->outfree = html_free;
1.111 kristaps 330: break;
1.173 schwarze 331: case OUTT_HTML:
1.111 kristaps 332: curp->outdata = html_alloc(curp->outopts);
1.162 kristaps 333: curp->outfree = html_free;
334: break;
1.173 schwarze 335: case OUTT_UTF8:
1.163 kristaps 336: curp->outdata = utf8_alloc(curp->outopts);
337: curp->outfree = ascii_free;
338: break;
1.173 schwarze 339: case OUTT_LOCALE:
1.162 kristaps 340: curp->outdata = locale_alloc(curp->outopts);
341: curp->outfree = ascii_free;
1.111 kristaps 342: break;
1.173 schwarze 343: case OUTT_ASCII:
1.111 kristaps 344: curp->outdata = ascii_alloc(curp->outopts);
345: curp->outfree = ascii_free;
346: break;
1.173 schwarze 347: case OUTT_PDF:
1.111 kristaps 348: curp->outdata = pdf_alloc(curp->outopts);
349: curp->outfree = pspdf_free;
350: break;
1.173 schwarze 351: case OUTT_PS:
1.111 kristaps 352: curp->outdata = ps_alloc(curp->outopts);
353: curp->outfree = pspdf_free;
354: break;
355: default:
356: break;
357: }
358:
359: switch (curp->outtype) {
1.173 schwarze 360: case OUTT_HTML:
1.111 kristaps 361: /* FALLTHROUGH */
1.173 schwarze 362: case OUTT_XHTML:
1.111 kristaps 363: curp->outman = html_man;
364: curp->outmdoc = html_mdoc;
365: break;
1.173 schwarze 366: case OUTT_TREE:
1.111 kristaps 367: curp->outman = tree_man;
368: curp->outmdoc = tree_mdoc;
369: break;
1.173 schwarze 370: case OUTT_MAN:
1.164 schwarze 371: curp->outmdoc = man_mdoc;
1.165 kristaps 372: curp->outman = man_man;
1.164 schwarze 373: break;
1.173 schwarze 374: case OUTT_PDF:
1.111 kristaps 375: /* FALLTHROUGH */
1.173 schwarze 376: case OUTT_ASCII:
1.111 kristaps 377: /* FALLTHROUGH */
1.173 schwarze 378: case OUTT_UTF8:
1.163 kristaps 379: /* FALLTHROUGH */
1.173 schwarze 380: case OUTT_LOCALE:
1.162 kristaps 381: /* FALLTHROUGH */
1.173 schwarze 382: case OUTT_PS:
1.111 kristaps 383: curp->outman = terminal_man;
384: curp->outmdoc = terminal_mdoc;
385: break;
386: default:
387: break;
388: }
389: }
390:
1.171 schwarze 391: mparse_result(curp->mp, &mdoc, &man, NULL);
1.154 kristaps 392:
1.111 kristaps 393: /* Execute the out device, if it exists. */
394:
1.154 kristaps 395: if (man && curp->outman)
396: (*curp->outman)(curp->outdata, man);
397: if (mdoc && curp->outmdoc)
398: (*curp->outmdoc)(curp->outdata, mdoc);
1.111 kristaps 399:
400: cleanup:
1.112 kristaps 401:
1.154 kristaps 402: mparse_reset(curp->mp);
1.111 kristaps 403:
1.154 kristaps 404: if (*level < rc)
405: *level = rc;
1.10 kristaps 406: }
407:
408: static int
1.170 schwarze 409: moptions(int *options, char *arg)
1.10 kristaps 410: {
411:
1.180 ! schwarze 412: if (arg == NULL)
! 413: /* nothing to do */;
! 414: else if (0 == strcmp(arg, "doc"))
1.170 schwarze 415: *options |= MPARSE_MDOC;
1.19 kristaps 416: else if (0 == strcmp(arg, "andoc"))
1.170 schwarze 417: /* nothing to do */;
1.17 kristaps 418: else if (0 == strcmp(arg, "an"))
1.170 schwarze 419: *options |= MPARSE_MAN;
1.10 kristaps 420: else {
1.176 schwarze 421: fprintf(stderr, "%s: -m%s: Bad argument\n",
422: progname, arg);
1.10 kristaps 423: return(0);
424: }
425:
426: return(1);
1.1 kristaps 427: }
428:
429: static int
1.60 kristaps 430: toptions(struct curparse *curp, char *arg)
1.1 kristaps 431: {
432:
433: if (0 == strcmp(arg, "ascii"))
1.60 kristaps 434: curp->outtype = OUTT_ASCII;
435: else if (0 == strcmp(arg, "lint")) {
436: curp->outtype = OUTT_LINT;
1.103 schwarze 437: curp->wlevel = MANDOCLEVEL_WARNING;
1.154 kristaps 438: } else if (0 == strcmp(arg, "tree"))
1.60 kristaps 439: curp->outtype = OUTT_TREE;
1.164 schwarze 440: else if (0 == strcmp(arg, "man"))
441: curp->outtype = OUTT_MAN;
1.43 kristaps 442: else if (0 == strcmp(arg, "html"))
1.60 kristaps 443: curp->outtype = OUTT_HTML;
1.163 kristaps 444: else if (0 == strcmp(arg, "utf8"))
445: curp->outtype = OUTT_UTF8;
1.162 kristaps 446: else if (0 == strcmp(arg, "locale"))
447: curp->outtype = OUTT_LOCALE;
1.59 kristaps 448: else if (0 == strcmp(arg, "xhtml"))
1.60 kristaps 449: curp->outtype = OUTT_XHTML;
1.85 kristaps 450: else if (0 == strcmp(arg, "ps"))
451: curp->outtype = OUTT_PS;
1.100 kristaps 452: else if (0 == strcmp(arg, "pdf"))
453: curp->outtype = OUTT_PDF;
1.1 kristaps 454: else {
1.176 schwarze 455: fprintf(stderr, "%s: -T%s: Bad argument\n",
456: progname, arg);
1.1 kristaps 457: return(0);
458: }
459:
460: return(1);
461: }
462:
463: static int
1.103 schwarze 464: woptions(struct curparse *curp, char *arg)
1.1 kristaps 465: {
1.34 kristaps 466: char *v, *o;
1.173 schwarze 467: const char *toks[6];
1.1 kristaps 468:
1.103 schwarze 469: toks[0] = "stop";
470: toks[1] = "all";
471: toks[2] = "warning";
472: toks[3] = "error";
473: toks[4] = "fatal";
474: toks[5] = NULL;
1.1 kristaps 475:
1.34 kristaps 476: while (*arg) {
477: o = arg;
1.45 kristaps 478: switch (getsubopt(&arg, UNCONST(toks), &v)) {
1.173 schwarze 479: case 0:
1.103 schwarze 480: curp->wstop = 1;
1.1 kristaps 481: break;
1.173 schwarze 482: case 1:
1.103 schwarze 483: /* FALLTHROUGH */
1.173 schwarze 484: case 2:
1.103 schwarze 485: curp->wlevel = MANDOCLEVEL_WARNING;
1.1 kristaps 486: break;
1.173 schwarze 487: case 3:
1.103 schwarze 488: curp->wlevel = MANDOCLEVEL_ERROR;
1.24 kristaps 489: break;
1.173 schwarze 490: case 4:
1.103 schwarze 491: curp->wlevel = MANDOCLEVEL_FATAL;
1.50 kristaps 492: break;
1.1 kristaps 493: default:
1.176 schwarze 494: fprintf(stderr, "%s: -W%s: Bad argument\n",
495: progname, o);
1.1 kristaps 496: return(0);
497: }
1.34 kristaps 498: }
1.1 kristaps 499:
500: return(1);
501: }
502:
1.153 kristaps 503: static void
1.173 schwarze 504: mmsg(enum mandocerr t, enum mandoclevel lvl,
1.155 kristaps 505: const char *file, int line, int col, const char *msg)
1.71 kristaps 506: {
1.177 schwarze 507: const char *mparse_msg;
1.103 schwarze 508:
1.175 schwarze 509: fprintf(stderr, "%s: %s:", progname, file);
510:
511: if (line)
512: fprintf(stderr, "%d:%d:", line, col + 1);
513:
1.177 schwarze 514: fprintf(stderr, " %s", mparse_strlevel(lvl));
515:
516: if (NULL != (mparse_msg = mparse_strerror(t)))
517: fprintf(stderr, ": %s", mparse_msg);
1.154 kristaps 518:
1.73 kristaps 519: if (msg)
520: fprintf(stderr, ": %s", msg);
1.154 kristaps 521:
1.73 kristaps 522: fputc('\n', stderr);
1.71 kristaps 523: }
CVSweb