summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2019-05-31 22:32:41 -0400
committerTavian Barnes <tavianator@tavianator.com>2019-05-31 22:43:44 -0400
commit4e4d13827cda87a983ef543e63f41b977d5fbdaa (patch)
tree508b6a393cba409fa37eb64fbbb2e9a2f532c41a
parent7df809c6223a89beb8d2c186a85c56c880b4fbbf (diff)
downloadbfs-4e4d13827cda87a983ef543e63f41b977d5fbdaa.tar.xz
parse: Pipe bfs -help to a pager when interactive
-rw-r--r--parse.c128
1 files changed, 125 insertions, 3 deletions
diff --git a/parse.c b/parse.c
index d1db4fb..2a8bfef 100644
--- a/parse.c
+++ b/parse.c
@@ -31,6 +31,7 @@
#include "fsade.h"
#include "mtab.h"
#include "printf.h"
+#include "spawn.h"
#include "stat.h"
#include "typo.h"
#include "util.h"
@@ -49,6 +50,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -320,7 +322,9 @@ struct parser_state {
/** The current regex flags to use. */
int regex_flags;
- /** Whether this session is interactive. */
+ /** Whether stdout is a terminal. */
+ bool stdout_tty;
+ /** Whether this session is interactive (stdin and stderr are each a terminal). */
bool interactive;
/** Whether -color or -nocolor has been passed. */
enum use_color use_color;
@@ -2357,10 +2361,121 @@ static struct expr *parse_xattr(struct parser_state *state, int arg1, int arg2)
}
/**
+ * Launch a pager for the help output.
+ */
+static CFILE *launch_pager(pid_t *pid, CFILE *cout) {
+ char *pager = getenv("PAGER");
+ if (!pager) {
+ pager = "more";
+ }
+
+ int pipefd[2];
+ if (pipe(pipefd) != 0) {
+ goto fail;
+ }
+
+ FILE *file = fdopen(pipefd[1], "w");
+ if (!file) {
+ goto fail_pipe;
+ }
+ pipefd[1] = -1;
+
+ CFILE *ret = cfdup(file, NULL);
+ if (!ret) {
+ goto fail_file;
+ }
+ file = NULL;
+ ret->close = true;
+ ret->colors = cout->colors;
+
+ struct bfs_spawn ctx;
+ if (bfs_spawn_init(&ctx) != 0) {
+ goto fail_ret;
+ }
+
+ if (bfs_spawn_setflags(&ctx, BFS_SPAWN_USEPATH) != 0) {
+ goto fail_ctx;
+ }
+
+ if (bfs_spawn_addclose(&ctx, fileno(ret->file)) != 0) {
+ goto fail_ctx;
+ }
+ if (bfs_spawn_adddup2(&ctx, pipefd[0], STDIN_FILENO) != 0) {
+ goto fail_ctx;
+ }
+ if (bfs_spawn_addclose(&ctx, pipefd[0]) != 0) {
+ goto fail_ctx;
+ }
+
+ char *argv[] = {
+ pager,
+ NULL,
+ };
+
+ extern char **environ;
+ char **envp = environ;
+
+ if (!getenv("LESS")) {
+ size_t envc;
+ for (envc = 0; environ[envc]; ++envc);
+ ++envc;
+
+ envp = malloc((envc + 1)*sizeof(*envp));
+ if (!envp) {
+ goto fail_ctx;
+ }
+
+ memcpy(envp, environ, (envc - 1)*sizeof(*envp));
+ envp[envc - 1] = "LESS=FKRX";
+ envp[envc] = NULL;
+ }
+
+ *pid = bfs_spawn(pager, &ctx, argv, envp);
+ if (*pid < 0) {
+ goto fail_envp;
+ }
+
+ close(pipefd[0]);
+ if (envp != environ) {
+ free(envp);
+ }
+ bfs_spawn_destroy(&ctx);
+ return ret;
+
+fail_envp:
+ if (envp != environ) {
+ free(envp);
+ }
+fail_ctx:
+ bfs_spawn_destroy(&ctx);
+fail_ret:
+ cfclose(ret);
+fail_file:
+ if (file) {
+ fclose(file);
+ }
+fail_pipe:
+ if (pipefd[1] >= 0) {
+ close(pipefd[1]);
+ }
+ if (pipefd[0] >= 0) {
+ close(pipefd[0]);
+ }
+fail:
+ return cout;
+}
+
+/**
* "Parse" -help.
*/
static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) {
CFILE *cout = state->cmdline->cout;
+
+ pid_t pager = -1;
+ if (state->stdout_tty) {
+ cout = launch_pager(&pager, cout);
+ }
+
cfprintf(cout, "Usage: ${ex}%s${rs} [${cyn}flags${rs}...] [${mag}paths${rs}...] [${blu}expression${rs}...]\n\n",
state->command);
@@ -2583,6 +2698,11 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) {
cfprintf(cout, "%s\n", BFS_HOMEPAGE);
+ if (pager > 0) {
+ cfclose(cout);
+ waitpid(pager, NULL, 0);
+ }
+
state->just_info = true;
return NULL;
}
@@ -3221,15 +3341,17 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) {
cmdline->mtab_error = errno;
}
- bool stderr_tty = isatty(STDERR_FILENO);
bool stdin_tty = isatty(STDIN_FILENO);
+ bool stdout_tty = isatty(STDOUT_FILENO);
+ bool stderr_tty = isatty(STDERR_FILENO);
struct parser_state state = {
.cmdline = cmdline,
.argv = cmdline->argv + 1,
.command = cmdline->argv[0],
.regex_flags = 0,
- .interactive = stderr_tty && stdin_tty,
+ .stdout_tty = stdout_tty,
+ .interactive = stdin_tty && stderr_tty,
.use_color = use_color,
.implicit_print = true,
.warn = stdin_tty,