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