1#include"cache.h" 2#include"run-command.h" 3#include"sigchain.h" 4 5#ifndef DEFAULT_PAGER 6#define DEFAULT_PAGER"less" 7#endif 8 9static struct child_process pager_process = CHILD_PROCESS_INIT; 10static const char*pager_program; 11 12static voidwait_for_pager(int in_signal) 13{ 14if(!in_signal) { 15fflush(stdout); 16fflush(stderr); 17} 18/* signal EOF to pager */ 19close(1); 20close(2); 21if(in_signal) 22finish_command_in_signal(&pager_process); 23else 24finish_command(&pager_process); 25} 26 27static voidwait_for_pager_atexit(void) 28{ 29wait_for_pager(0); 30} 31 32static voidwait_for_pager_signal(int signo) 33{ 34wait_for_pager(1); 35sigchain_pop(signo); 36raise(signo); 37} 38 39static intcore_pager_config(const char*var,const char*value,void*data) 40{ 41if(!strcmp(var,"core.pager")) 42returngit_config_string(&pager_program, var, value); 43return0; 44} 45 46static voidread_early_config(config_fn_t cb,void*data) 47{ 48git_config_with_options(cb, data, NULL,1); 49 50/* 51 * Note that this is a really dirty hack that does the wrong thing in 52 * many cases. The crux of the problem is that we cannot run 53 * setup_git_directory() early on in git's setup, so we have no idea if 54 * we are in a repository or not, and therefore are not sure whether 55 * and how to read repository-local config. 56 * 57 * So if we _aren't_ in a repository (or we are but we would reject its 58 * core.repositoryformatversion), we'll read whatever is in .git/config 59 * blindly. Similarly, if we _are_ in a repository, but not at the 60 * root, we'll fail to find .git/config (because it's really 61 * ../.git/config, etc). See t7006 for a complete set of failures. 62 * 63 * However, we have historically provided this hack because it does 64 * work some of the time (namely when you are at the top-level of a 65 * valid repository), and would rarely make things worse (i.e., you do 66 * not generally have a .git/config file sitting around). 67 */ 68if(!startup_info->have_repository) { 69struct git_config_source repo_config; 70 71memset(&repo_config,0,sizeof(repo_config)); 72 repo_config.file =".git/config"; 73git_config_with_options(cb, data, &repo_config,1); 74} 75} 76 77const char*git_pager(int stdout_is_tty) 78{ 79const char*pager; 80 81if(!stdout_is_tty) 82return NULL; 83 84 pager =getenv("GIT_PAGER"); 85if(!pager) { 86if(!pager_program) 87read_early_config(core_pager_config, NULL); 88 pager = pager_program; 89} 90if(!pager) 91 pager =getenv("PAGER"); 92if(!pager) 93 pager = DEFAULT_PAGER; 94if(!*pager || !strcmp(pager,"cat")) 95 pager = NULL; 96 97return pager; 98} 99 100static voidsetup_pager_env(struct argv_array *env) 101{ 102const char**argv; 103int i; 104char*pager_env =xstrdup(PAGER_ENV); 105int n =split_cmdline(pager_env, &argv); 106 107if(n <0) 108die("malformed build-time PAGER_ENV:%s", 109split_cmdline_strerror(n)); 110 111for(i =0; i < n; i++) { 112char*cp =strchr(argv[i],'='); 113 114if(!cp) 115die("malformed build-time PAGER_ENV"); 116 117*cp ='\0'; 118if(!getenv(argv[i])) { 119*cp ='='; 120argv_array_push(env, argv[i]); 121} 122} 123free(pager_env); 124free(argv); 125} 126 127voidprepare_pager_args(struct child_process *pager_process,const char*pager) 128{ 129argv_array_push(&pager_process->args, pager); 130 pager_process->use_shell =1; 131setup_pager_env(&pager_process->env_array); 132} 133 134voidsetup_pager(void) 135{ 136const char*pager =git_pager(isatty(1)); 137 138if(!pager) 139return; 140 141/* 142 * force computing the width of the terminal before we redirect 143 * the standard output to the pager. 144 */ 145(void)term_columns(); 146 147setenv("GIT_PAGER_IN_USE","true",1); 148 149/* spawn the pager */ 150prepare_pager_args(&pager_process, pager); 151 pager_process.in = -1; 152argv_array_push(&pager_process.env_array,"GIT_PAGER_IN_USE"); 153if(start_command(&pager_process)) 154return; 155 156/* original process continues, but writes to the pipe */ 157dup2(pager_process.in,1); 158if(isatty(2)) 159dup2(pager_process.in,2); 160close(pager_process.in); 161 162/* this makes sure that the parent terminates after the pager */ 163sigchain_push_common(wait_for_pager_signal); 164atexit(wait_for_pager_atexit); 165} 166 167intpager_in_use(void) 168{ 169returngit_env_bool("GIT_PAGER_IN_USE",0); 170} 171 172/* 173 * Return cached value (if set) or $COLUMNS environment variable (if 174 * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive), 175 * and default to 80 if all else fails. 176 */ 177intterm_columns(void) 178{ 179static int term_columns_at_startup; 180 181char*col_string; 182int n_cols; 183 184if(term_columns_at_startup) 185return term_columns_at_startup; 186 187 term_columns_at_startup =80; 188 189 col_string =getenv("COLUMNS"); 190if(col_string && (n_cols =atoi(col_string)) >0) 191 term_columns_at_startup = n_cols; 192#ifdef TIOCGWINSZ 193else{ 194struct winsize ws; 195if(!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col) 196 term_columns_at_startup = ws.ws_col; 197} 198#endif 199 200return term_columns_at_startup; 201} 202 203/* 204 * How many columns do we need to show this number in decimal? 205 */ 206intdecimal_width(uintmax_t number) 207{ 208int width; 209 210for(width =1; number >=10; width++) 211 number /=10; 212return width; 213} 214 215struct pager_command_config_data { 216const char*cmd; 217int want; 218char*value; 219}; 220 221static intpager_command_config(const char*var,const char*value,void*vdata) 222{ 223struct pager_command_config_data *data = vdata; 224const char*cmd; 225 226if(skip_prefix(var,"pager.", &cmd) && !strcmp(cmd, data->cmd)) { 227int b =git_config_maybe_bool(var, value); 228if(b >=0) 229 data->want = b; 230else{ 231 data->want =1; 232 data->value =xstrdup(value); 233} 234} 235 236return0; 237} 238 239/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ 240intcheck_pager_config(const char*cmd) 241{ 242struct pager_command_config_data data; 243 244 data.cmd = cmd; 245 data.want = -1; 246 data.value = NULL; 247 248read_early_config(pager_command_config, &data); 249 250if(data.value) 251 pager_program = data.value; 252return data.want; 253}