diff options
author | Ingo Schwarze <schwarze@openbsd.org> | 2015-07-28 18:38:55 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@openbsd.org> | 2015-07-28 18:38:55 +0000 |
commit | 2510486fc44a83002c7f77dfeaf94603b0cea866 (patch) | |
tree | 3e36cacc3887baa8d0d907ce8105b0b7e0314f98 | |
parent | ce6fe8383153d10dc1f75f86c3f8fab0d32e2290 (diff) | |
download | mandoc-2510486fc44a83002c7f77dfeaf94603b0cea866.tar.gz |
Remove the hack of scrolling forward and backward with +G1G that
many (jmc@, millert@, espie@, deraadt@) considered revolting.
Instead, when using a pager, since we are using a temporary file
for tags anyway, use another temporary file for the formatted
page(s), as suggested by millert@ and similar to what the traditional
BSD man(1) did, except that we use only one single temporary output
file rather than one for each formatted manual page, such that
searching (both with / and :t) works across all the displayed files.
-rw-r--r-- | main.c | 93 | ||||
-rw-r--r-- | tag.c | 75 | ||||
-rw-r--r-- | tag.h | 9 |
3 files changed, 96 insertions, 81 deletions
@@ -95,7 +95,6 @@ static int fs_lookup(const struct manpaths *, static void fs_search(const struct mansearch *, const struct manpaths *, int, char**, struct manpage **, size_t *); -static void handle_sigpipe(int); static int koptions(int *, char *); #if HAVE_SQLITE3 int mandocdb(int, char**); @@ -105,7 +104,7 @@ static void mmsg(enum mandocerr, enum mandoclevel, const char *, int, int, const char *); static void parse(struct curparse *, int, const char *); static void passthrough(const char *, int, int); -static pid_t spawn_pager(void); +static pid_t spawn_pager(struct tag_files *); static int toptions(struct curparse *, char *); static void usage(enum argmode) __attribute__((noreturn)); static int woptions(struct curparse *, char *); @@ -123,6 +122,7 @@ main(int argc, char *argv[]) struct manconf conf; struct curparse curp; struct mansearch search; + struct tag_files *tag_files; char *auxpaths; char *defos; unsigned char *uc; @@ -136,8 +136,8 @@ main(int argc, char *argv[]) int fd; int show_usage; int options; + int use_pager; int c; - pid_t pager_pid; /* 0: don't use; 1: not yet spawned. */ if (argc < 1) progname = "mandoc"; @@ -180,7 +180,8 @@ main(int argc, char *argv[]) options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; defos = NULL; - pager_pid = 1; + use_pager = 1; + tag_files = NULL; show_usage = 0; outmode = OUTMODE_DEF; @@ -194,14 +195,14 @@ main(int argc, char *argv[]) conf_file = optarg; break; case 'c': - pager_pid = 0; + use_pager = 0; break; case 'f': search.argmode = ARG_WORD; break; case 'h': conf.output.synopsisonly = 1; - pager_pid = 0; + use_pager = 0; outmode = OUTMODE_ALL; break; case 'I': @@ -277,7 +278,7 @@ main(int argc, char *argv[]) switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; - pager_pid = 0; + use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; @@ -419,8 +420,8 @@ main(int argc, char *argv[]) if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) return((int)MANDOCLEVEL_BADARG); - if (pager_pid == 1 && isatty(STDOUT_FILENO) == 0) - pager_pid = 0; + if (use_pager && ! isatty(STDOUT_FILENO)) + use_pager = 0; curp.mchars = mchars_alloc(); curp.mp = mparse_alloc(options, curp.wlevel, mmsg, @@ -433,8 +434,8 @@ main(int argc, char *argv[]) mparse_keep(curp.mp); if (argc < 1) { - if (pager_pid == 1) - pager_pid = spawn_pager(); + if (use_pager) + tag_files = tag_init(); parse(&curp, STDIN_FILENO, "<stdin>"); } @@ -445,8 +446,10 @@ main(int argc, char *argv[]) rc = rctmp; if (fd != -1) { - if (pager_pid == 1) - pager_pid = spawn_pager(); + if (use_pager) { + tag_files = tag_init(); + use_pager = 0; + } if (resp == NULL) parse(&curp, fd, *argv); @@ -490,15 +493,14 @@ out: free(defos); /* - * If a pager is attached, flush the pipe leading to it - * and signal end of file such that the user can browse - * to the end. Then wait for the user to close the pager. + * When using a pager, finish writing both temporary files, + * fork it, wait for the user to close it, and clean up. */ - if (pager_pid != 0 && pager_pid != 1) { + if (tag_files != NULL) { fclose(stdout); tag_write(); - waitpid(pager_pid, NULL, 0); + waitpid(spawn_pager(tag_files), NULL, 0); tag_unlink(); } @@ -947,22 +949,14 @@ mmsg(enum mandocerr t, enum mandoclevel lvl, fputc('\n', stderr); } -static void -handle_sigpipe(int signum) -{ - - exit((int)rc); -} - static pid_t -spawn_pager(void) +spawn_pager(struct tag_files *tag_files) { #define MAX_PAGER_ARGS 16 char *argv[MAX_PAGER_ARGS]; const char *pager; char *cp; size_t cmdlen; - int fildes[2]; int argc; pid_t pager_pid; @@ -991,29 +985,18 @@ spawn_pager(void) break; } - /* Read all text right away and use the tag file. */ + /* For more(1) and less(1), use the tag file. */ - for (;;) { - if ((cmdlen = strlen(argv[0])) < 4) - break; + if ((cmdlen = strlen(argv[0])) >= 4) { cp = argv[0] + cmdlen - 4; - if (strcmp(cp, "less") && strcmp(cp, "more")) - break; - if ((cp = tag_init()) == NULL) - break; - argv[argc++] = mandoc_strdup("+G1G"); - argv[argc++] = mandoc_strdup("-T"); - argv[argc++] = cp; - break; + if (strcmp(cp, "less") == 0 || strcmp(cp, "more") == 0) { + argv[argc++] = mandoc_strdup("-T"); + argv[argc++] = tag_files->tfn; + } } + argv[argc++] = tag_files->ofn; argv[argc] = NULL; - if (pipe(fildes) == -1) { - fprintf(stderr, "%s: pipe: %s\n", - progname, strerror(errno)); - return(0); - } - switch (pager_pid = fork()) { case -1: fprintf(stderr, "%s: fork: %s\n", @@ -1022,29 +1005,17 @@ spawn_pager(void) case 0: break; default: - close(fildes[0]); - if (dup2(fildes[1], STDOUT_FILENO) == -1) { - fprintf(stderr, "%s: dup output: %s\n", - progname, strerror(errno)); - exit((int)MANDOCLEVEL_SYSERR); - } - close(fildes[1]); - signal(SIGPIPE, handle_sigpipe); return(pager_pid); } /* The child process becomes the pager. */ - close(fildes[1]); - if (dup2(fildes[0], STDIN_FILENO) == -1) { - fprintf(stderr, "%s: dup input: %s\n", - progname, strerror(errno)); + if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) { + fprintf(stderr, "pager: stdout: %s\n", strerror(errno)); exit((int)MANDOCLEVEL_SYSERR); } - close(fildes[0]); - - /* Hand over to the pager. */ - + close(tag_files->ofd); + close(tag_files->tfd); execvp(argv[0], argv); fprintf(stderr, "%s: exec %s: %s\n", progname, argv[0], strerror(errno)); @@ -44,29 +44,49 @@ static void tag_free(void *, void *); static void *tag_calloc(size_t, size_t, void *); static struct ohash tag_data; -static char *tag_fn = NULL; -static int tag_fd = -1; +static struct tag_files tag_files; /* - * Set up the ohash table to collect output line numbers - * where various marked-up terms are documented and create - * the temporary tags file, saving the name for the pager. + * Prepare for using a pager. + * Not all pagers are capable of using a tag file, + * but for simplicity, create it anyway. */ -char * +struct tag_files * tag_init(void) { struct ohash_info tag_info; + int ofd; - tag_fn = mandoc_strdup("/tmp/man.XXXXXXXXXX"); + ofd = -1; + tag_files.tfd = -1; + + /* Save the original standard output for use by the pager. */ + + if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) + goto fail; + + /* Create both temporary output files. */ + + (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX", + sizeof(tag_files.ofn)); + (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", + sizeof(tag_files.tfn)); signal(SIGHUP, tag_signal); signal(SIGINT, tag_signal); signal(SIGTERM, tag_signal); - if ((tag_fd = mkstemp(tag_fn)) == -1) { - free(tag_fn); - tag_fn = NULL; - return(NULL); - } + if ((ofd = mkstemp(tag_files.ofn)) == -1) + goto fail; + if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1) + goto fail; + if (dup2(ofd, STDOUT_FILENO) == -1) + goto fail; + close(ofd); + + /* + * Set up the ohash table to collect output line numbers + * where various marked-up terms are documented. + */ tag_info.alloc = tag_alloc; tag_info.calloc = tag_calloc; @@ -74,7 +94,21 @@ tag_init(void) tag_info.key_offset = offsetof(struct tag_entry, s); tag_info.data = NULL; ohash_init(&tag_data, 4, &tag_info); - return(tag_fn); + return(&tag_files); + +fail: + tag_unlink(); + if (ofd != -1) + close(ofd); + if (tag_files.ofd != -1) + close(tag_files.ofd); + if (tag_files.tfd != -1) + close(tag_files.tfd); + *tag_files.ofn = '\0'; + *tag_files.tfn = '\0'; + tag_files.ofd = -1; + tag_files.tfd = -1; + return(NULL); } /* @@ -88,7 +122,7 @@ tag_put(const char *s, int prio, size_t line) size_t len; unsigned int slot; - if (tag_fd == -1) + if (tag_files.tfd <= 0) return; slot = ohash_qlookup(&tag_data, s); entry = ohash_find(&tag_data, slot); @@ -114,13 +148,14 @@ tag_write(void) struct tag_entry *entry; unsigned int slot; - if (tag_fd == -1) + if (tag_files.tfd <= 0) return; - stream = fdopen(tag_fd, "w"); + stream = fdopen(tag_files.tfd, "w"); entry = ohash_first(&tag_data, &slot); while (entry != NULL) { if (stream != NULL) - fprintf(stream, "%s - %zu\n", entry->s, entry->line); + fprintf(stream, "%s %s %zu\n", + entry->s, tag_files.ofn, entry->line); free(entry); entry = ohash_next(&tag_data, &slot); } @@ -133,8 +168,10 @@ void tag_unlink(void) { - if (tag_fn != NULL) - unlink(tag_fn); + if (*tag_files.ofn != '\0') + unlink(tag_files.ofn); + if (*tag_files.tfn != '\0') + unlink(tag_files.tfn); } static void @@ -15,9 +15,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +struct tag_files { + char ofn[20]; + char tfn[20]; + int ofd; + int tfd; +}; + __BEGIN_DECLS -char *tag_init(void); +struct tag_files *tag_init(void); void tag_put(const char *, int, size_t); void tag_write(void); void tag_unlink(void); |