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