]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Fully integrate apropos(1) into mandoc(1).
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.180 2014/08/17 03:24:47 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
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.
18 */
19 #include "config.h"
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "mandoc.h"
31 #include "mandoc_aux.h"
32 #include "main.h"
33 #include "mdoc.h"
34 #include "man.h"
35 #include "manpath.h"
36 #include "mansearch.h"
37
38 #if !defined(__GNUC__) || (__GNUC__ < 2)
39 # if !defined(lint)
40 # define __attribute__(x)
41 # endif
42 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
43
44 typedef void (*out_mdoc)(void *, const struct mdoc *);
45 typedef void (*out_man)(void *, const struct man *);
46 typedef void (*out_free)(void *);
47
48 enum outt {
49 OUTT_ASCII = 0, /* -Tascii */
50 OUTT_LOCALE, /* -Tlocale */
51 OUTT_UTF8, /* -Tutf8 */
52 OUTT_TREE, /* -Ttree */
53 OUTT_MAN, /* -Tman */
54 OUTT_HTML, /* -Thtml */
55 OUTT_XHTML, /* -Txhtml */
56 OUTT_LINT, /* -Tlint */
57 OUTT_PS, /* -Tps */
58 OUTT_PDF /* -Tpdf */
59 };
60
61 struct curparse {
62 struct mparse *mp;
63 enum mandoclevel wlevel; /* ignore messages below this */
64 int wstop; /* stop after a file with a warning */
65 enum outt outtype; /* which output to use */
66 out_mdoc outmdoc; /* mdoc output ptr */
67 out_man outman; /* man output ptr */
68 out_free outfree; /* free output ptr */
69 void *outdata; /* data for output */
70 char outopts[BUFSIZ]; /* buf of output opts */
71 };
72
73 static int moptions(int *, char *);
74 static void mmsg(enum mandocerr, enum mandoclevel,
75 const char *, int, int, const char *);
76 static void parse(struct curparse *, int,
77 const char *, enum mandoclevel *);
78 static int toptions(struct curparse *, char *);
79 static void usage(enum argmode) __attribute__((noreturn));
80 static void version(void) __attribute__((noreturn));
81 static int woptions(struct curparse *, char *);
82
83 static const char *progname;
84
85
86 int
87 main(int argc, char *argv[])
88 {
89 struct curparse curp;
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;
100 int options;
101 int c;
102
103 progname = strrchr(argv[0], '/');
104 if (progname == NULL)
105 progname = argv[0];
106 else
107 ++progname;
108
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. */
127
128 memset(&curp, 0, sizeof(struct curparse));
129 curp.outtype = OUTT_ASCII;
130 curp.wlevel = MANDOCLEVEL_FATAL;
131 options = MPARSE_SO;
132 defos = NULL;
133
134 show_usage = 0;
135 while (-1 != (c = getopt(argc, argv, "C:fI:kM:m:O:S:s:T:VW:"))) {
136 switch (c) {
137 case 'C':
138 conf_file = optarg;
139 break;
140 case 'f':
141 search.argmode = ARG_WORD;
142 break;
143 case 'I':
144 if (strncmp(optarg, "os=", 3)) {
145 fprintf(stderr,
146 "%s: -I%s: Bad argument\n",
147 progname, optarg);
148 return((int)MANDOCLEVEL_BADARG);
149 }
150 if (defos) {
151 fprintf(stderr,
152 "%s: -I%s: Duplicate argument\n",
153 progname, optarg);
154 return((int)MANDOCLEVEL_BADARG);
155 }
156 defos = mandoc_strdup(optarg + 3);
157 break;
158 case 'k':
159 search.argmode = ARG_EXPR;
160 break;
161 case 'M':
162 defpaths = optarg;
163 break;
164 case 'm':
165 auxpaths = optarg;
166 break;
167 case 'O':
168 search.outkey = optarg;
169 (void)strlcat(curp.outopts, optarg, BUFSIZ);
170 (void)strlcat(curp.outopts, ",", BUFSIZ);
171 break;
172 case 'S':
173 search.arch = optarg;
174 break;
175 case 's':
176 search.sec = optarg;
177 break;
178 case 'T':
179 if ( ! toptions(&curp, optarg))
180 return((int)MANDOCLEVEL_BADARG);
181 break;
182 case 'W':
183 if ( ! woptions(&curp, optarg))
184 return((int)MANDOCLEVEL_BADARG);
185 break;
186 case 'V':
187 version();
188 /* NOTREACHED */
189 default:
190 show_usage = 1;
191 break;
192 }
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);
229
230 curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos);
231
232 /*
233 * Conditionally start up the lookaside buffer before parsing.
234 */
235 if (OUTT_MAN == curp.outtype)
236 mparse_keep(curp.mp);
237
238 rc = MANDOCLEVEL_OK;
239
240 if (NULL == *argv)
241 parse(&curp, STDIN_FILENO, "<stdin>", &rc);
242
243 while (*argv) {
244 parse(&curp, -1, *argv, &rc);
245 if (MANDOCLEVEL_OK != rc && curp.wstop)
246 break;
247 ++argv;
248 }
249
250 if (curp.outfree)
251 (*curp.outfree)(curp.outdata);
252 if (curp.mp)
253 mparse_free(curp.mp);
254 free(defos);
255
256 return((int)rc);
257 }
258
259 static void
260 version(void)
261 {
262
263 printf("mandoc %s\n", VERSION);
264 exit((int)MANDOCLEVEL_OK);
265 }
266
267 static void
268 usage(enum argmode argmode)
269 {
270
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 }
292 exit((int)MANDOCLEVEL_BADARG);
293 }
294
295 static void
296 parse(struct curparse *curp, int fd, const char *file,
297 enum mandoclevel *level)
298 {
299 enum mandoclevel rc;
300 struct mdoc *mdoc;
301 struct man *man;
302
303 /* Begin by parsing the file itself. */
304
305 assert(file);
306 assert(fd >= -1);
307
308 rc = mparse_readfd(curp->mp, fd, file);
309
310 /* Stop immediately if the parse has failed. */
311
312 if (MANDOCLEVEL_FATAL <= rc)
313 goto cleanup;
314
315 /*
316 * With -Wstop and warnings or errors of at least the requested
317 * level, do not produce output.
318 */
319
320 if (MANDOCLEVEL_OK != rc && curp->wstop)
321 goto cleanup;
322
323 /* If unset, allocate output dev now (if applicable). */
324
325 if ( ! (curp->outman && curp->outmdoc)) {
326 switch (curp->outtype) {
327 case OUTT_XHTML:
328 curp->outdata = xhtml_alloc(curp->outopts);
329 curp->outfree = html_free;
330 break;
331 case OUTT_HTML:
332 curp->outdata = html_alloc(curp->outopts);
333 curp->outfree = html_free;
334 break;
335 case OUTT_UTF8:
336 curp->outdata = utf8_alloc(curp->outopts);
337 curp->outfree = ascii_free;
338 break;
339 case OUTT_LOCALE:
340 curp->outdata = locale_alloc(curp->outopts);
341 curp->outfree = ascii_free;
342 break;
343 case OUTT_ASCII:
344 curp->outdata = ascii_alloc(curp->outopts);
345 curp->outfree = ascii_free;
346 break;
347 case OUTT_PDF:
348 curp->outdata = pdf_alloc(curp->outopts);
349 curp->outfree = pspdf_free;
350 break;
351 case OUTT_PS:
352 curp->outdata = ps_alloc(curp->outopts);
353 curp->outfree = pspdf_free;
354 break;
355 default:
356 break;
357 }
358
359 switch (curp->outtype) {
360 case OUTT_HTML:
361 /* FALLTHROUGH */
362 case OUTT_XHTML:
363 curp->outman = html_man;
364 curp->outmdoc = html_mdoc;
365 break;
366 case OUTT_TREE:
367 curp->outman = tree_man;
368 curp->outmdoc = tree_mdoc;
369 break;
370 case OUTT_MAN:
371 curp->outmdoc = man_mdoc;
372 curp->outman = man_man;
373 break;
374 case OUTT_PDF:
375 /* FALLTHROUGH */
376 case OUTT_ASCII:
377 /* FALLTHROUGH */
378 case OUTT_UTF8:
379 /* FALLTHROUGH */
380 case OUTT_LOCALE:
381 /* FALLTHROUGH */
382 case OUTT_PS:
383 curp->outman = terminal_man;
384 curp->outmdoc = terminal_mdoc;
385 break;
386 default:
387 break;
388 }
389 }
390
391 mparse_result(curp->mp, &mdoc, &man, NULL);
392
393 /* Execute the out device, if it exists. */
394
395 if (man && curp->outman)
396 (*curp->outman)(curp->outdata, man);
397 if (mdoc && curp->outmdoc)
398 (*curp->outmdoc)(curp->outdata, mdoc);
399
400 cleanup:
401
402 mparse_reset(curp->mp);
403
404 if (*level < rc)
405 *level = rc;
406 }
407
408 static int
409 moptions(int *options, char *arg)
410 {
411
412 if (arg == NULL)
413 /* nothing to do */;
414 else if (0 == strcmp(arg, "doc"))
415 *options |= MPARSE_MDOC;
416 else if (0 == strcmp(arg, "andoc"))
417 /* nothing to do */;
418 else if (0 == strcmp(arg, "an"))
419 *options |= MPARSE_MAN;
420 else {
421 fprintf(stderr, "%s: -m%s: Bad argument\n",
422 progname, arg);
423 return(0);
424 }
425
426 return(1);
427 }
428
429 static int
430 toptions(struct curparse *curp, char *arg)
431 {
432
433 if (0 == strcmp(arg, "ascii"))
434 curp->outtype = OUTT_ASCII;
435 else if (0 == strcmp(arg, "lint")) {
436 curp->outtype = OUTT_LINT;
437 curp->wlevel = MANDOCLEVEL_WARNING;
438 } else if (0 == strcmp(arg, "tree"))
439 curp->outtype = OUTT_TREE;
440 else if (0 == strcmp(arg, "man"))
441 curp->outtype = OUTT_MAN;
442 else if (0 == strcmp(arg, "html"))
443 curp->outtype = OUTT_HTML;
444 else if (0 == strcmp(arg, "utf8"))
445 curp->outtype = OUTT_UTF8;
446 else if (0 == strcmp(arg, "locale"))
447 curp->outtype = OUTT_LOCALE;
448 else if (0 == strcmp(arg, "xhtml"))
449 curp->outtype = OUTT_XHTML;
450 else if (0 == strcmp(arg, "ps"))
451 curp->outtype = OUTT_PS;
452 else if (0 == strcmp(arg, "pdf"))
453 curp->outtype = OUTT_PDF;
454 else {
455 fprintf(stderr, "%s: -T%s: Bad argument\n",
456 progname, arg);
457 return(0);
458 }
459
460 return(1);
461 }
462
463 static int
464 woptions(struct curparse *curp, char *arg)
465 {
466 char *v, *o;
467 const char *toks[6];
468
469 toks[0] = "stop";
470 toks[1] = "all";
471 toks[2] = "warning";
472 toks[3] = "error";
473 toks[4] = "fatal";
474 toks[5] = NULL;
475
476 while (*arg) {
477 o = arg;
478 switch (getsubopt(&arg, UNCONST(toks), &v)) {
479 case 0:
480 curp->wstop = 1;
481 break;
482 case 1:
483 /* FALLTHROUGH */
484 case 2:
485 curp->wlevel = MANDOCLEVEL_WARNING;
486 break;
487 case 3:
488 curp->wlevel = MANDOCLEVEL_ERROR;
489 break;
490 case 4:
491 curp->wlevel = MANDOCLEVEL_FATAL;
492 break;
493 default:
494 fprintf(stderr, "%s: -W%s: Bad argument\n",
495 progname, o);
496 return(0);
497 }
498 }
499
500 return(1);
501 }
502
503 static void
504 mmsg(enum mandocerr t, enum mandoclevel lvl,
505 const char *file, int line, int col, const char *msg)
506 {
507 const char *mparse_msg;
508
509 fprintf(stderr, "%s: %s:", progname, file);
510
511 if (line)
512 fprintf(stderr, "%d:%d:", line, col + 1);
513
514 fprintf(stderr, " %s", mparse_strlevel(lvl));
515
516 if (NULL != (mparse_msg = mparse_strerror(t)))
517 fprintf(stderr, ": %s", mparse_msg);
518
519 if (msg)
520 fprintf(stderr, ": %s", msg);
521
522 fputc('\n', stderr);
523 }