130 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <sys/select.h>3#include <stdlib.h>4#include <stdio.h>5#include <string.h>6#include <signal.h>7#include <sys/ioctl.h>8#include "pager.h"9#include "run-command.h"10#include "sigchain.h"11#include "subcmd-config.h"12 13/*14 * This is split up from the rest of git so that we can do15 * something different on Windows.16 */17 18static int spawned_pager;19static int pager_columns;20 21void pager_init(const char *pager_env)22{23 subcmd_config.pager_env = pager_env;24}25 26static const char *forced_pager;27 28void force_pager(const char *pager)29{30 forced_pager = pager;31}32 33static void pager_preexec(void)34{35 /*36 * Work around bug in "less" by not starting it until we37 * have real input38 */39 fd_set in;40 fd_set exception;41 42 FD_ZERO(&in);43 FD_ZERO(&exception);44 FD_SET(0, &in);45 FD_SET(0, &exception);46 select(1, &in, NULL, &exception, NULL);47 48 setenv("LESS", "FRSX", 0);49}50 51static const char *pager_argv[] = { "sh", "-c", NULL, NULL };52static struct child_process pager_process;53 54static void wait_for_pager(void)55{56 fflush(stdout);57 fflush(stderr);58 /* signal EOF to pager */59 close(1);60 close(2);61 finish_command(&pager_process);62}63 64static void wait_for_pager_signal(int signo)65{66 wait_for_pager();67 sigchain_pop(signo);68 raise(signo);69}70 71void setup_pager(void)72{73 const char *pager = getenv(subcmd_config.pager_env);74 struct winsize sz;75 76 if (forced_pager)77 pager = forced_pager;78 if (!isatty(1) && !forced_pager)79 return;80 if (ioctl(1, TIOCGWINSZ, &sz) == 0)81 pager_columns = sz.ws_col;82 if (!pager)83 pager = getenv("PAGER");84 if (!(pager || access("/usr/bin/pager", X_OK)))85 pager = "/usr/bin/pager";86 if (!(pager || access("/usr/bin/less", X_OK)))87 pager = "/usr/bin/less";88 if (!pager)89 pager = "cat";90 if (!*pager || !strcmp(pager, "cat"))91 return;92 93 spawned_pager = 1; /* means we are emitting to terminal */94 95 /* spawn the pager */96 pager_argv[2] = pager;97 pager_process.argv = pager_argv;98 pager_process.in = -1;99 pager_process.preexec_cb = pager_preexec;100 101 if (start_command(&pager_process))102 return;103 104 /* original process continues, but writes to the pipe */105 dup2(pager_process.in, 1);106 if (isatty(2))107 dup2(pager_process.in, 2);108 close(pager_process.in);109 110 /* this makes sure that the parent terminates after the pager */111 sigchain_push_common(wait_for_pager_signal);112 atexit(wait_for_pager);113}114 115int pager_in_use(void)116{117 return spawned_pager;118}119 120int pager_get_columns(void)121{122 char *s;123 124 s = getenv("COLUMNS");125 if (s)126 return atoi(s);127 128 return (pager_columns ? pager_columns : 80) - 2;129}130