All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] lib: add pager functionality
@ 2012-04-02 14:35 Davidlohr Bueso
  2012-04-23  9:58 ` Karel Zak
  0 siblings, 1 reply; 2+ messages in thread
From: Davidlohr Bueso @ 2012-04-02 14:35 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux

From: Davidlohr Bueso <dave@gnu.org>

When some program' output exceeds the terminal's dimensions, it is a nice
feature to call a pager that acts as calling 'less' to allow better user
navigation. This patch adds this functionality, based on what perf and git
have (ie: git log).

Signed-off-by: Davidlohr Bueso <dave@gnu.org>
---
 include/pager.h |    6 ++
 lib/Makefile.am |    2 +
 lib/pager.c     |  214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 222 insertions(+), 0 deletions(-)
 create mode 100644 include/pager.h
 create mode 100644 lib/pager.c

diff --git a/include/pager.h b/include/pager.h
new file mode 100644
index 0000000..9ca42eb
--- /dev/null
+++ b/include/pager.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_PAGER
+#define UTIL_LINUX_PAGER
+
+void setup_pager(void);
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index c34481d..d27f323 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -9,6 +9,7 @@ noinst_PROGRAMS = \
 	test_fileutils \
 	test_ismounted \
 	test_mangle \
+	test_pager \
 	test_procutils \
 	test_strutils \
 	test_tt \
@@ -35,6 +36,7 @@ test_procutils_SOURCES = procutils.c
 if LINUX
 test_cpuset_SOURCES = cpuset.c
 test_sysfs_SOURCES = sysfs.c at.c
+test_pager_SOURCES = pager.c
 test_sysfs_CFLAGS = -DTEST_PROGRAM_SYSFS
 
 test_loopdev_SOURCES = \
diff --git a/lib/pager.c b/lib/pager.c
new file mode 100644
index 0000000..fabd112
--- /dev/null
+++ b/lib/pager.c
@@ -0,0 +1,214 @@
+/*
+ * Based on linux-perf/git scm
+ *
+ * Some modifications and simplifications for util-linux
+ * by Davidlohr Bueso <dave@gnu.org> - March 2012.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+
+static struct child_process pager_process;
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+
+struct child_process {
+	const char **argv;
+	pid_t pid;
+	int in;
+	int out;
+	int err;
+	unsigned no_stdin:1;
+	void (*preexec_cb)(void);
+};
+
+static inline void close_pair(int fd[2])
+{
+	close(fd[0]);
+	close(fd[1]);
+}
+
+static inline void dup_devnull(int to)
+{
+	int fd = open("/dev/null", O_RDWR);
+	dup2(fd, to);
+	close(fd);
+}
+
+static int start_command(struct child_process *cmd)
+{
+	int need_in, need_out, need_err;
+	int fdin[2], fdout[2], fderr[2];
+
+	/*
+	 * In case of errors we must keep the promise to close FDs
+	 * that have been passed in via ->in and ->out.
+	 */
+	need_in = !cmd->no_stdin && cmd->in < 0;
+	if (need_in) {
+		if (pipe(fdin) < 0) {
+			if (cmd->out > 0)
+				close(cmd->out);
+			return -1;
+		}
+		cmd->in = fdin[1];
+	}
+
+	fflush(NULL);
+	cmd->pid = fork();
+	if (!cmd->pid) {
+		if (need_in) {
+			dup2(fdin[0], 0);
+			close_pair(fdin);
+		} else if (cmd->in) {
+			dup2(cmd->in, 0);
+			close(cmd->in);
+		}
+
+		cmd->preexec_cb();
+		execvp(cmd->argv[0], (char *const*) cmd->argv);
+		exit(127); /* cmd not found */
+	}
+
+	if (cmd->pid < 0) {
+		int err = errno;
+		if (need_in)
+			close_pair(fdin);
+		else if (cmd->in)
+			close(cmd->in);
+
+		return -1;
+	}
+
+	if (need_in)
+		close(fdin[0]);
+	else if (cmd->in)
+		close(cmd->in);
+	return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+	for (;;) {
+		int status, code;
+		pid_t waiting = waitpid(pid, &status, 0);
+
+		if (waiting < 0) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, _("waitpid failed (%s)"), strerror(errno));
+		}
+		if (waiting != pid)
+			return -1;
+		if (WIFSIGNALED(status))
+			return -1;
+
+		if (!WIFEXITED(status))
+			return -1;
+		code = WEXITSTATUS(status);
+		switch (code) {
+		case 127:
+			return -1;
+		case 0:
+			return 0;
+		default:
+			return -1;
+		}
+	}
+}
+
+static int finish_command(struct child_process *cmd)
+{
+	return wait_or_whine(cmd->pid);
+}
+
+static void pager_preexec(void)
+{
+	/*
+	 * Work around bug in "less" by not starting it until we
+	 * have real input
+	 */
+	fd_set in;
+
+	FD_ZERO(&in);
+	FD_SET(0, &in);
+	select(1, &in, NULL, &in, NULL);
+
+	setenv("LESS", "FRSX", 0);
+}
+
+static void wait_for_pager(void)
+{
+	fflush(stdout);
+	fflush(stderr);
+	/* signal EOF to pager */
+	close(1);
+	close(2);
+	finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+	wait_for_pager();
+	raise(signo);
+}
+
+void setup_pager(void)
+{
+	const char *pager = getenv("PAGER");
+
+	if (!isatty(1))
+		return;
+
+	if (!pager)
+		pager = "less";
+	else if (!*pager || !strcmp(pager, "cat"))
+		return;
+
+	/* spawn the pager */
+	pager_argv[2] = pager;
+	pager_process.argv = pager_argv;
+	pager_process.in = -1;
+	pager_process.preexec_cb = pager_preexec;
+
+	if (start_command(&pager_process))
+		return;
+
+	/* original process continues, but writes to the pipe */
+	dup2(pager_process.in, 1);
+	if (isatty(2))
+		dup2(pager_process.in, 2);
+	close(pager_process.in);
+
+	/* this makes sure that the parent terminates after the pager */
+	signal(SIGINT, wait_for_pager_signal);
+	signal(SIGHUP, wait_for_pager_signal);
+	signal(SIGTERM, wait_for_pager_signal);
+	signal(SIGQUIT, wait_for_pager_signal);
+	signal(SIGPIPE, wait_for_pager_signal);
+
+	atexit(wait_for_pager);
+}
+
+#ifdef TEST_PROGRAM
+
+#define MAX 255
+
+int main(int argc, char *argv[])
+{
+	int i;
+
+	setup_pager();
+	for (i = 0; i < MAX; i++)
+		printf("%d\n", i);
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH 1/3] lib: add pager functionality
  2012-04-02 14:35 [PATCH 1/3] lib: add pager functionality Davidlohr Bueso
@ 2012-04-23  9:58 ` Karel Zak
  0 siblings, 0 replies; 2+ messages in thread
From: Karel Zak @ 2012-04-23  9:58 UTC (permalink / raw)
  To: Davidlohr Bueso; +Cc: util-linux

On Mon, Apr 02, 2012 at 04:35:47PM +0200, Davidlohr Bueso wrote:
>  include/pager.h |    6 ++
>  lib/Makefile.am |    2 +
>  lib/pager.c     |  214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 222 insertions(+), 0 deletions(-)
>  create mode 100644 include/pager.h
>  create mode 100644 lib/pager.c

 Applied, thanks.

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2012-04-23  9:58 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-02 14:35 [PATCH 1/3] lib: add pager functionality Davidlohr Bueso
2012-04-23  9:58 ` Karel Zak

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.