=================================================================== RCS file: /cvs/mandoc/main.c,v retrieving revision 1.181 retrieving revision 1.184 diff -u -p -r1.181 -r1.184 --- mandoc/main.c 2014/08/20 21:04:35 1.181 +++ mandoc/main.c 2014/08/22 04:52:55 1.184 @@ -1,6 +1,6 @@ -/* $Id: main.c,v 1.181 2014/08/20 21:04:35 schwarze Exp $ */ +/* $Id: main.c,v 1.184 2014/08/22 04:52:55 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze * Copyright (c) 2010 Joerg Sonnenberger * @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -84,11 +85,13 @@ static void mmsg(enum mandocerr, enum mandoclevel, const char *, int, int, const char *); static void parse(struct curparse *, int, const char *, enum mandoclevel *); +static void spawn_pager(void); static int toptions(struct curparse *, char *); static void usage(enum argmode) __attribute__((noreturn)); static void version(void) __attribute__((noreturn)); static int woptions(struct curparse *, char *); +static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static const char *progname; @@ -102,11 +105,15 @@ main(int argc, char *argv[]) char *defos; #if HAVE_SQLITE3 struct manpage *res; - size_t i, sz; + char **auxargv; + size_t isec, i, sz; + int prio, best_prio; + char sec; #endif enum mandoclevel rc; enum outmode outmode; int show_usage; + int use_pager; int options; int c; @@ -141,9 +148,11 @@ main(int argc, char *argv[]) options = MPARSE_SO; defos = NULL; + use_pager = 1; show_usage = 0; outmode = OUTMODE_DEF; - while (-1 != (c = getopt(argc, argv, "aC:fI:ikM:m:O:S:s:T:VW:w"))) { + + while (-1 != (c = getopt(argc, argv, "aC:cfI:ikM:m:O:S:s:T:VW:w"))) { switch (c) { case 'a': outmode = OUTMODE_ALL; @@ -151,6 +160,9 @@ main(int argc, char *argv[]) case 'C': conf_file = optarg; break; + case 'c': + use_pager = 0; + break; case 'f': search.argmode = ARG_WORD; break; @@ -219,6 +231,7 @@ main(int argc, char *argv[]) switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; + use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; @@ -231,29 +244,78 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; +#if HAVE_SQLITE3 + auxargv = NULL; +#endif + rc = MANDOCLEVEL_OK; + /* man(1), whatis(1), apropos(1) */ if (search.argmode != ARG_FILE) { #if HAVE_SQLITE3 if (argc == 0) usage(search.argmode); + + /* Access the mandoc database. */ + manpath_parse(&paths, conf_file, defpaths, auxpaths); mansearch_setup(1); if( ! mansearch(&search, &paths, argc, argv, &res, &sz)) usage(search.argmode); manpath_free(&paths); + + /* + * For standard man(1) and -a output mode, + * prepare for copying filename pointers + * into the program parameter array. + */ + + if (outmode == OUTMODE_ONE) { + argc = 1; + argv[0] = res[0].file; + argv[1] = NULL; + best_prio = 10; + } else if (outmode == OUTMODE_ALL) { + argc = (int)sz; + argv = auxargv = mandoc_reallocarray( + NULL, sz + 1, sizeof(char *)); + argv[argc] = NULL; + } + + /* Iterate all matching manuals. */ + for (i = 0; i < sz; i++) { if (outmode == OUTMODE_FLN) puts(res[i].file); - else + else if (outmode == OUTMODE_LST) 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 { + /* Search for the best section. */ + isec = strcspn(res[i].file, "123456789"); + sec = res[i].file[isec]; + if ('\0' == sec) + continue; + prio = sec_prios[sec - '1']; + if (prio >= best_prio) + continue; + best_prio = prio; + argv[0] = res[i].file; + } } - mansearch_free(res, sz); - mansearch_setup(0); - return((int)MANDOCLEVEL_OK); + + /* + * For man(1), -a and -i output mode, fall through + * to the main mandoc(1) code iterating files + * and running the parsers on each of them. + */ + + if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) + goto out; #else fputs("mandoc: database support not compiled in\n", stderr); @@ -266,6 +328,9 @@ main(int argc, char *argv[]) if ( ! moptions(&options, auxpaths)) return((int)MANDOCLEVEL_BADARG); + if (use_pager && isatty(STDOUT_FILENO)) + spawn_pager(); + curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); /* @@ -274,8 +339,6 @@ main(int argc, char *argv[]) if (OUTT_MAN == curp.outtype) mparse_keep(curp.mp); - rc = MANDOCLEVEL_OK; - if (NULL == *argv) parse(&curp, STDIN_FILENO, "", &rc); @@ -290,6 +353,16 @@ main(int argc, char *argv[]) (*curp.outfree)(curp.outdata); if (curp.mp) mparse_free(curp.mp); + +#if HAVE_SQLITE3 +out: + if (search.argmode != ARG_FILE) { + mansearch_free(res, sz); + mansearch_setup(0); + free(auxargv); + } +#endif + free(defos); return((int)rc); @@ -559,4 +632,80 @@ mmsg(enum mandocerr t, enum mandoclevel lvl, fprintf(stderr, ": %s", msg); fputc('\n', stderr); +} + +static void +spawn_pager(void) +{ +#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", + progname, strerror(errno)); + return; + } + + switch (fork()) { + case -1: + fprintf(stderr, "%s: fork: %s\n", + progname, strerror(errno)); + exit((int)MANDOCLEVEL_SYSERR); + case 0: + close(fildes[0]); + if (dup2(fildes[1], STDOUT_FILENO) == -1) { + fprintf(stderr, "%s: dup output: %s\n", + progname, strerror(errno)); + exit((int)MANDOCLEVEL_SYSERR); + } + return; + default: + 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); }