summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2015-07-28 18:38:55 +0000
committerIngo Schwarze <schwarze@openbsd.org>2015-07-28 18:38:55 +0000
commit2510486fc44a83002c7f77dfeaf94603b0cea866 (patch)
tree3e36cacc3887baa8d0d907ce8105b0b7e0314f98
parentce6fe8383153d10dc1f75f86c3f8fab0d32e2290 (diff)
downloadmandoc-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.c93
-rw-r--r--tag.c75
-rw-r--r--tag.h9
3 files changed, 96 insertions, 81 deletions
diff --git a/main.c b/main.c
index 22707176..20078ba1 100644
--- a/main.c
+++ b/main.c
@@ -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));
diff --git a/tag.c b/tag.c
index f489d8f5..4e2abd72 100644
--- a/tag.c
+++ b/tag.c
@@ -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
diff --git a/tag.h b/tag.h
index a1af255a..8a32c11c 100644
--- a/tag.h
+++ b/tag.h
@@ -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);