1 // SPDX-License-Identifier: GPL-2.0 2 #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 do 15 * something different on Windows. 16 */ 17 18 static int spawned_pager; 19 static int pager_columns; 20 21 void pager_init(const char *pager_env) 22 { 23 subcmd_config.pager_env = pager_env; 24 } 25 26 static void pager_preexec(void) 27 { 28 /* 29 * Work around bug in "less" by not starting it until we 30 * have real input 31 */ 32 fd_set in; 33 34 FD_ZERO(&in); 35 FD_SET(0, &in); 36 select(1, &in, NULL, &in, NULL); 37 38 setenv("LESS", "FRSX", 0); 39 } 40 41 static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; 42 static struct child_process pager_process; 43 44 static void wait_for_pager(void) 45 { 46 fflush(stdout); 47 fflush(stderr); 48 /* signal EOF to pager */ 49 close(1); 50 close(2); 51 finish_command(&pager_process); 52 } 53 54 static void wait_for_pager_signal(int signo) 55 { 56 wait_for_pager(); 57 sigchain_pop(signo); 58 raise(signo); 59 } 60 61 void setup_pager(void) 62 { 63 const char *pager = getenv(subcmd_config.pager_env); 64 struct winsize sz; 65 66 if (!isatty(1)) 67 return; 68 if (ioctl(1, TIOCGWINSZ, &sz) == 0) 69 pager_columns = sz.ws_col; 70 if (!pager) 71 pager = getenv("PAGER"); 72 if (!(pager || access("/usr/bin/pager", X_OK))) 73 pager = "/usr/bin/pager"; 74 if (!(pager || access("/usr/bin/less", X_OK))) 75 pager = "/usr/bin/less"; 76 if (!pager) 77 pager = "cat"; 78 if (!*pager || !strcmp(pager, "cat")) 79 return; 80 81 spawned_pager = 1; /* means we are emitting to terminal */ 82 83 /* spawn the pager */ 84 pager_argv[2] = pager; 85 pager_process.argv = pager_argv; 86 pager_process.in = -1; 87 pager_process.preexec_cb = pager_preexec; 88 89 if (start_command(&pager_process)) 90 return; 91 92 /* original process continues, but writes to the pipe */ 93 dup2(pager_process.in, 1); 94 if (isatty(2)) 95 dup2(pager_process.in, 2); 96 close(pager_process.in); 97 98 /* this makes sure that the parent terminates after the pager */ 99 sigchain_push_common(wait_for_pager_signal); 100 atexit(wait_for_pager); 101 } 102 103 int pager_in_use(void) 104 { 105 return spawned_pager; 106 } 107 108 int pager_get_columns(void) 109 { 110 char *s; 111 112 s = getenv("COLUMNS"); 113 if (s) 114 return atoi(s); 115 116 return (pager_columns ? pager_columns : 80) - 2; 117 } 118