xref: /openbmc/linux/tools/lib/subcmd/pager.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24b6ab94eSJosh Poimboeuf #include <sys/select.h>
34b6ab94eSJosh Poimboeuf #include <stdlib.h>
44b6ab94eSJosh Poimboeuf #include <stdio.h>
54b6ab94eSJosh Poimboeuf #include <string.h>
64b6ab94eSJosh Poimboeuf #include <signal.h>
761eb2eb4SAndi Kleen #include <sys/ioctl.h>
84b6ab94eSJosh Poimboeuf #include "pager.h"
94b6ab94eSJosh Poimboeuf #include "run-command.h"
104b6ab94eSJosh Poimboeuf #include "sigchain.h"
114b6ab94eSJosh Poimboeuf #include "subcmd-config.h"
124b6ab94eSJosh Poimboeuf 
134b6ab94eSJosh Poimboeuf /*
144b6ab94eSJosh Poimboeuf  * This is split up from the rest of git so that we can do
154b6ab94eSJosh Poimboeuf  * something different on Windows.
164b6ab94eSJosh Poimboeuf  */
174b6ab94eSJosh Poimboeuf 
184b6ab94eSJosh Poimboeuf static int spawned_pager;
1961eb2eb4SAndi Kleen static int pager_columns;
204b6ab94eSJosh Poimboeuf 
pager_init(const char * pager_env)214b6ab94eSJosh Poimboeuf void pager_init(const char *pager_env)
224b6ab94eSJosh Poimboeuf {
234b6ab94eSJosh Poimboeuf 	subcmd_config.pager_env = pager_env;
244b6ab94eSJosh Poimboeuf }
254b6ab94eSJosh Poimboeuf 
26*03a1f49fSAndi Kleen static const char *forced_pager;
27*03a1f49fSAndi Kleen 
force_pager(const char * pager)28*03a1f49fSAndi Kleen void force_pager(const char *pager)
29*03a1f49fSAndi Kleen {
30*03a1f49fSAndi Kleen 	forced_pager = pager;
31*03a1f49fSAndi Kleen }
32*03a1f49fSAndi Kleen 
pager_preexec(void)334b6ab94eSJosh Poimboeuf static void pager_preexec(void)
344b6ab94eSJosh Poimboeuf {
354b6ab94eSJosh Poimboeuf 	/*
364b6ab94eSJosh Poimboeuf 	 * Work around bug in "less" by not starting it until we
374b6ab94eSJosh Poimboeuf 	 * have real input
384b6ab94eSJosh Poimboeuf 	 */
394b6ab94eSJosh Poimboeuf 	fd_set in;
40ad343a98SSergey Senozhatsky 	fd_set exception;
414b6ab94eSJosh Poimboeuf 
424b6ab94eSJosh Poimboeuf 	FD_ZERO(&in);
43ad343a98SSergey Senozhatsky 	FD_ZERO(&exception);
444b6ab94eSJosh Poimboeuf 	FD_SET(0, &in);
45ad343a98SSergey Senozhatsky 	FD_SET(0, &exception);
46ad343a98SSergey Senozhatsky 	select(1, &in, NULL, &exception, NULL);
474b6ab94eSJosh Poimboeuf 
484b6ab94eSJosh Poimboeuf 	setenv("LESS", "FRSX", 0);
494b6ab94eSJosh Poimboeuf }
504b6ab94eSJosh Poimboeuf 
514b6ab94eSJosh Poimboeuf static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
524b6ab94eSJosh Poimboeuf static struct child_process pager_process;
534b6ab94eSJosh Poimboeuf 
wait_for_pager(void)544b6ab94eSJosh Poimboeuf static void wait_for_pager(void)
554b6ab94eSJosh Poimboeuf {
564b6ab94eSJosh Poimboeuf 	fflush(stdout);
574b6ab94eSJosh Poimboeuf 	fflush(stderr);
584b6ab94eSJosh Poimboeuf 	/* signal EOF to pager */
594b6ab94eSJosh Poimboeuf 	close(1);
604b6ab94eSJosh Poimboeuf 	close(2);
614b6ab94eSJosh Poimboeuf 	finish_command(&pager_process);
624b6ab94eSJosh Poimboeuf }
634b6ab94eSJosh Poimboeuf 
wait_for_pager_signal(int signo)644b6ab94eSJosh Poimboeuf static void wait_for_pager_signal(int signo)
654b6ab94eSJosh Poimboeuf {
664b6ab94eSJosh Poimboeuf 	wait_for_pager();
674b6ab94eSJosh Poimboeuf 	sigchain_pop(signo);
684b6ab94eSJosh Poimboeuf 	raise(signo);
694b6ab94eSJosh Poimboeuf }
704b6ab94eSJosh Poimboeuf 
setup_pager(void)714b6ab94eSJosh Poimboeuf void setup_pager(void)
724b6ab94eSJosh Poimboeuf {
734b6ab94eSJosh Poimboeuf 	const char *pager = getenv(subcmd_config.pager_env);
7461eb2eb4SAndi Kleen 	struct winsize sz;
754b6ab94eSJosh Poimboeuf 
76*03a1f49fSAndi Kleen 	if (forced_pager)
77*03a1f49fSAndi Kleen 		pager = forced_pager;
78*03a1f49fSAndi Kleen 	if (!isatty(1) && !forced_pager)
794b6ab94eSJosh Poimboeuf 		return;
8061eb2eb4SAndi Kleen 	if (ioctl(1, TIOCGWINSZ, &sz) == 0)
8161eb2eb4SAndi Kleen 		pager_columns = sz.ws_col;
824b6ab94eSJosh Poimboeuf 	if (!pager)
834b6ab94eSJosh Poimboeuf 		pager = getenv("PAGER");
844b6ab94eSJosh Poimboeuf 	if (!(pager || access("/usr/bin/pager", X_OK)))
854b6ab94eSJosh Poimboeuf 		pager = "/usr/bin/pager";
864b6ab94eSJosh Poimboeuf 	if (!(pager || access("/usr/bin/less", X_OK)))
874b6ab94eSJosh Poimboeuf 		pager = "/usr/bin/less";
884b6ab94eSJosh Poimboeuf 	if (!pager)
894b6ab94eSJosh Poimboeuf 		pager = "cat";
904b6ab94eSJosh Poimboeuf 	if (!*pager || !strcmp(pager, "cat"))
914b6ab94eSJosh Poimboeuf 		return;
924b6ab94eSJosh Poimboeuf 
934b6ab94eSJosh Poimboeuf 	spawned_pager = 1; /* means we are emitting to terminal */
944b6ab94eSJosh Poimboeuf 
954b6ab94eSJosh Poimboeuf 	/* spawn the pager */
964b6ab94eSJosh Poimboeuf 	pager_argv[2] = pager;
974b6ab94eSJosh Poimboeuf 	pager_process.argv = pager_argv;
984b6ab94eSJosh Poimboeuf 	pager_process.in = -1;
994b6ab94eSJosh Poimboeuf 	pager_process.preexec_cb = pager_preexec;
1004b6ab94eSJosh Poimboeuf 
1014b6ab94eSJosh Poimboeuf 	if (start_command(&pager_process))
1024b6ab94eSJosh Poimboeuf 		return;
1034b6ab94eSJosh Poimboeuf 
1044b6ab94eSJosh Poimboeuf 	/* original process continues, but writes to the pipe */
1054b6ab94eSJosh Poimboeuf 	dup2(pager_process.in, 1);
1064b6ab94eSJosh Poimboeuf 	if (isatty(2))
1074b6ab94eSJosh Poimboeuf 		dup2(pager_process.in, 2);
1084b6ab94eSJosh Poimboeuf 	close(pager_process.in);
1094b6ab94eSJosh Poimboeuf 
1104b6ab94eSJosh Poimboeuf 	/* this makes sure that the parent terminates after the pager */
1114b6ab94eSJosh Poimboeuf 	sigchain_push_common(wait_for_pager_signal);
1124b6ab94eSJosh Poimboeuf 	atexit(wait_for_pager);
1134b6ab94eSJosh Poimboeuf }
1144b6ab94eSJosh Poimboeuf 
pager_in_use(void)1154b6ab94eSJosh Poimboeuf int pager_in_use(void)
1164b6ab94eSJosh Poimboeuf {
1174b6ab94eSJosh Poimboeuf 	return spawned_pager;
1184b6ab94eSJosh Poimboeuf }
11961eb2eb4SAndi Kleen 
pager_get_columns(void)12061eb2eb4SAndi Kleen int pager_get_columns(void)
12161eb2eb4SAndi Kleen {
12261eb2eb4SAndi Kleen 	char *s;
12361eb2eb4SAndi Kleen 
12461eb2eb4SAndi Kleen 	s = getenv("COLUMNS");
12561eb2eb4SAndi Kleen 	if (s)
12661eb2eb4SAndi Kleen 		return atoi(s);
12761eb2eb4SAndi Kleen 
12861eb2eb4SAndi Kleen 	return (pager_columns ? pager_columns : 80) - 2;
12961eb2eb4SAndi Kleen }
130