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{ 169const char*env; 170 env =getenv("GIT_PAGER_IN_USE"); 171return env ?git_config_bool("GIT_PAGER_IN_USE", env) :0; 172} 173 174/* 175 * Return cached value (if set) or $COLUMNS environment variable (if 176 * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive), 177 * and default to 80 if all else fails. 178 */ 179intterm_columns(void) 180{ 181static int term_columns_at_startup; 182 183char*col_string; 184int n_cols; 185 186if(term_columns_at_startup) 187return term_columns_at_startup; 188 189 term_columns_at_startup =80; 190 191 col_string =getenv("COLUMNS"); 192if(col_string && (n_cols =atoi(col_string)) >0) 193 term_columns_at_startup = n_cols; 194#ifdef TIOCGWINSZ 195else{ 196struct winsize ws; 197if(!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col) 198 term_columns_at_startup = ws.ws_col; 199} 200#endif 201 202return term_columns_at_startup; 203} 204 205/* 206 * How many columns do we need to show this number in decimal? 207 */ 208intdecimal_width(uintmax_t number) 209{ 210int width; 211 212for(width =1; number >=10; width++) 213 number /=10; 214return width; 215} 216 217struct pager_command_config_data { 218const char*cmd; 219int want; 220char*value; 221}; 222 223static intpager_command_config(const char*var,const char*value,void*vdata) 224{ 225struct pager_command_config_data *data = vdata; 226const char*cmd; 227 228if(skip_prefix(var,"pager.", &cmd) && !strcmp(cmd, data->cmd)) { 229int b =git_config_maybe_bool(var, value); 230if(b >=0) 231 data->want = b; 232else{ 233 data->want =1; 234 data->value =xstrdup(value); 235} 236} 237 238return0; 239} 240 241/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ 242intcheck_pager_config(const char*cmd) 243{ 244struct pager_command_config_data data; 245 246 data.cmd = cmd; 247 data.want = -1; 248 data.value = NULL; 249 250read_early_config(pager_command_config, &data); 251 252if(data.value) 253 pager_program = data.value; 254return data.want; 255}