-/* $Id: main.c,v 1.183 2014/08/22 03:42:18 schwarze Exp $ */
+/* $Id: main.c,v 1.192 2014/09/03 23:21:47 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
#include <sys/types.h>
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
const char *, int, int, const char *);
static void parse(struct curparse *, int,
const char *, enum mandoclevel *);
+static enum mandoclevel passthrough(const char *, int);
static void spawn_pager(void);
static int toptions(struct curparse *, char *);
static void usage(enum argmode) __attribute__((noreturn));
char *conf_file, *defpaths, *auxpaths;
char *defos;
#if HAVE_SQLITE3
- struct manpage *res;
- char **auxargv;
+ struct manpage *res, *resp;
size_t isec, i, sz;
int prio, best_prio;
char sec;
#endif
enum mandoclevel rc;
enum outmode outmode;
+ pid_t child_pid;
+ int fd;
int show_usage;
int use_pager;
int options;
show_usage = 0;
outmode = OUTMODE_DEF;
- while (-1 != (c = getopt(argc, argv, "aC:cfI:ikM:m:O:S:s:T:VW:w"))) {
+ while (-1 != (c = getopt(argc, argv, "aC:cfhI:iklM:m:O:S:s:T:VW:w"))) {
switch (c) {
case 'a':
outmode = OUTMODE_ALL;
case 'f':
search.argmode = ARG_WORD;
break;
+ case 'h':
+ (void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
+ outmode = OUTMODE_ALL;
+ break;
case 'I':
if (strncmp(optarg, "os=", 3)) {
fprintf(stderr,
case 'k':
search.argmode = ARG_EXPR;
break;
+ case 'l':
+ search.argmode = ARG_FILE;
+ outmode = OUTMODE_ALL;
+ break;
case 'M':
defpaths = optarg;
break;
if (show_usage)
usage(search.argmode);
+ /* Postprocess options. */
+
if (outmode == OUTMODE_DEF) {
switch (search.argmode) {
case ARG_FILE:
}
}
+ /* Parse arguments. */
+
argc -= optind;
argv += optind;
#if HAVE_SQLITE3
- auxargv = NULL;
+ resp = NULL;
#endif
+ /* Quirk for a man(1) section argument without -s. */
+
+ if (search.argmode == ARG_NAME &&
+ argv[0] != NULL &&
+ isdigit((unsigned char)argv[0][0]) &&
+ (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) {
+ search.sec = argv[0];
+ argv++;
+ argc--;
+ }
+
rc = MANDOCLEVEL_OK;
/* man(1), whatis(1), apropos(1) */
mansearch_setup(1);
if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
usage(search.argmode);
- manpath_free(&paths);
+ resp = res;
+
+ if (sz == 0) {
+ if (search.argmode == ARG_NAME)
+ fprintf(stderr, "%s: No entry for %s "
+ "in the manual.\n", progname, argv[0]);
+ rc = MANDOCLEVEL_BADARG;
+ goto out;
+ }
/*
* For standard man(1) and -a output mode,
if (outmode == OUTMODE_ONE) {
argc = 1;
- argv[0] = res[0].file;
- argv[1] = NULL;
best_prio = 10;
- } else if (outmode == OUTMODE_ALL) {
+ } else if (outmode == OUTMODE_ALL)
argc = (int)sz;
- argv = auxargv = mandoc_reallocarray(
- NULL, sz + 1, sizeof(char *));
- argv[argc] = NULL;
- }
/* Iterate all matching manuals. */
printf("%s - %s\n", res[i].names,
res[i].output == NULL ? "" :
res[i].output);
- else if (outmode == OUTMODE_ALL)
- argv[i] = res[i].file;
- else {
+ else if (outmode == OUTMODE_ONE) {
/* Search for the best section. */
isec = strcspn(res[i].file, "123456789");
sec = res[i].file[isec];
if (prio >= best_prio)
continue;
best_prio = prio;
- argv[0] = res[i].file;
+ resp = res + i;
}
}
if (OUTT_MAN == curp.outtype)
mparse_keep(curp.mp);
- if (NULL == *argv)
+ if (argc == 0)
parse(&curp, STDIN_FILENO, "<stdin>", &rc);
- while (*argv) {
- parse(&curp, -1, *argv, &rc);
+ while (argc) {
+#if HAVE_SQLITE3
+ if (resp != NULL) {
+ rc = mparse_open(curp.mp, &fd, resp->file,
+ &child_pid);
+ if (fd == -1)
+ /* nothing */;
+ else if (resp->form & FORM_SRC) {
+ /* For .so only; ignore failure. */
+ chdir(paths.paths[resp->ipath]);
+ parse(&curp, fd, resp->file, &rc);
+ } else
+ rc = passthrough(resp->file, fd);
+ resp++;
+ } else
+#endif
+ {
+ rc = mparse_open(curp.mp, &fd, *argv++,
+ &child_pid);
+ if (fd != -1)
+ parse(&curp, fd, argv[-1], &rc);
+ }
+
+ if (child_pid &&
+ mparse_wait(curp.mp, child_pid) != MANDOCLEVEL_OK)
+ rc = MANDOCLEVEL_SYSERR;
+
if (MANDOCLEVEL_OK != rc && curp.wstop)
break;
- ++argv;
+ argc--;
}
if (curp.outfree)
#if HAVE_SQLITE3
out:
if (search.argmode != ARG_FILE) {
+ manpath_free(&paths);
mansearch_free(res, sz);
mansearch_setup(0);
- free(auxargv);
}
#endif
switch (argmode) {
case ARG_FILE:
- fputs("usage: mandoc [-V] [-Ios=name] [-mformat]"
- " [-Ooption] [-Toutput] [-Wlevel]\n"
+ fputs("usage: mandoc [-acfhklV] [-Ios=name] "
+ "[-mformat] [-Ooption] [-Toutput] [-Wlevel]\n"
"\t [file ...]\n", stderr);
break;
case ARG_NAME:
- fputs("usage: man [-acfhkVw] [-C file] "
+ fputs("usage: man [-acfhklVw] [-C file] "
"[-M path] [-m path] [-S arch] [-s section]\n"
"\t [section] name ...\n", stderr);
break;
case ARG_WORD:
- fputs("usage: whatis [-V] [-C file] [-M path] [-m path] "
- "[-S arch] [-s section] name ...\n", stderr);
+ fputs("usage: whatis [-acfhklVw] [-C file] "
+ "[-M path] [-m path] [-O outkey] [-S arch]\n"
+ "\t [-s section] name ...\n", stderr);
break;
case ARG_EXPR:
- fputs("usage: apropos [-V] [-C file] [-M path] [-m path] "
- "[-O outkey] [-S arch]\n"
+ fputs("usage: apropos [-acfhklVw] [-C file] "
+ "[-M path] [-m path] [-O outkey] [-S arch]\n"
"\t [-s section] expression ...\n", stderr);
break;
}
*level = rc;
}
+static enum mandoclevel
+passthrough(const char *file, int fd)
+{
+ char buf[BUFSIZ];
+ const char *syscall;
+ ssize_t nr, nw, off;
+
+ while ((nr = read(fd, buf, BUFSIZ)) != -1 && nr != 0)
+ for (off = 0; off < nr; off += nw)
+ if ((nw = write(STDOUT_FILENO, buf + off,
+ (size_t)(nr - off))) == -1 || nw == 0) {
+ syscall = "write";
+ goto fail;
+ }
+
+ if (nr == 0) {
+ close(fd);
+ return(MANDOCLEVEL_OK);
+ }
+
+ syscall = "read";
+fail:
+ fprintf(stderr, "%s: %s: SYSERR: %s: %s",
+ progname, file, syscall, strerror(errno));
+ return(MANDOCLEVEL_SYSERR);
+}
+
static int
moptions(int *options, char *arg)
{
static void
spawn_pager(void)
{
- int fildes[2];
+#define MAX_PAGER_ARGS 16
+ char *argv[MAX_PAGER_ARGS];
+ const char *pager;
+ char *cp;
+ int fildes[2];
+ int argc;
if (pipe(fildes) == -1) {
fprintf(stderr, "%s: pipe: %s\n",
}
return;
default:
- close(fildes[1]);
- if (dup2(fildes[0], STDIN_FILENO) == -1) {
- fprintf(stderr, "%s: dup input: %s\n",
- progname, strerror(errno));
- } else {
- execlp("more", "more", "-s", NULL);
- fprintf(stderr, "%s: exec: %s\n",
- progname, strerror(errno));
- }
+ break;
+ }
+
+ /* The original process becomes the pager. */
+
+ close(fildes[1]);
+ if (dup2(fildes[0], STDIN_FILENO) == -1) {
+ fprintf(stderr, "%s: dup input: %s\n",
+ progname, strerror(errno));
exit((int)MANDOCLEVEL_SYSERR);
}
+
+ pager = getenv("MANPAGER");
+ if (pager == NULL || *pager == '\0')
+ pager = getenv("PAGER");
+ if (pager == NULL || *pager == '\0')
+ pager = "/usr/bin/more -s";
+ cp = mandoc_strdup(pager);
+
+ /*
+ * Parse the pager command into words.
+ * Intentionally do not do anything fancy here.
+ */
+
+ argc = 0;
+ while (argc + 1 < MAX_PAGER_ARGS) {
+ argv[argc++] = cp;
+ cp = strchr(cp, ' ');
+ if (cp == NULL)
+ break;
+ *cp++ = '\0';
+ while (*cp == ' ')
+ cp++;
+ if (*cp == '\0')
+ break;
+ }
+ argv[argc] = NULL;
+
+ /* Hand over to the pager. */
+
+ execvp(argv[0], argv);
+ fprintf(stderr, "%s: exec: %s\n",
+ progname, strerror(errno));
+ exit((int)MANDOCLEVEL_SYSERR);
}