123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- #include "cache.h"
- #include "config.h"
- #include "run-command.h"
- #include "sigchain.h"
- #include "alias.h"
- #ifndef DEFAULT_PAGER
- #define DEFAULT_PAGER "less"
- #endif
- static struct child_process pager_process = CHILD_PROCESS_INIT;
- static const char *pager_program;
- static void wait_for_pager(int in_signal)
- {
- if (!in_signal) {
- fflush(stdout);
- fflush(stderr);
- }
- /* signal EOF to pager */
- close(1);
- close(2);
- if (in_signal)
- finish_command_in_signal(&pager_process);
- else
- finish_command(&pager_process);
- }
- static void wait_for_pager_atexit(void)
- {
- wait_for_pager(0);
- }
- static void wait_for_pager_signal(int signo)
- {
- wait_for_pager(1);
- sigchain_pop(signo);
- raise(signo);
- }
- static int core_pager_config(const char *var, const char *value, void *data)
- {
- if (!strcmp(var, "core.pager"))
- return git_config_string(&pager_program, var, value);
- return 0;
- }
- const char *git_pager(int stdout_is_tty)
- {
- const char *pager;
- if (!stdout_is_tty)
- return NULL;
- pager = getenv("GIT_PAGER");
- if (!pager) {
- if (!pager_program)
- read_early_config(core_pager_config, NULL);
- pager = pager_program;
- }
- if (!pager)
- pager = getenv("PAGER");
- if (!pager)
- pager = DEFAULT_PAGER;
- if (!*pager || !strcmp(pager, "cat"))
- pager = NULL;
- return pager;
- }
- static void setup_pager_env(struct strvec *env)
- {
- const char **argv;
- int i;
- char *pager_env = xstrdup(PAGER_ENV);
- int n = split_cmdline(pager_env, &argv);
- if (n < 0)
- die("malformed build-time PAGER_ENV: %s",
- split_cmdline_strerror(n));
- for (i = 0; i < n; i++) {
- char *cp = strchr(argv[i], '=');
- if (!cp)
- die("malformed build-time PAGER_ENV");
- *cp = '\0';
- if (!getenv(argv[i])) {
- *cp = '=';
- strvec_push(env, argv[i]);
- }
- }
- free(pager_env);
- free(argv);
- }
- void prepare_pager_args(struct child_process *pager_process, const char *pager)
- {
- strvec_push(&pager_process->args, pager);
- pager_process->use_shell = 1;
- setup_pager_env(&pager_process->env_array);
- pager_process->trace2_child_class = "pager";
- }
- void setup_pager(void)
- {
- const char *pager = git_pager(isatty(1));
- if (!pager)
- return;
- /*
- * After we redirect standard output, we won't be able to use an ioctl
- * to get the terminal size. Let's grab it now, and then set $COLUMNS
- * to communicate it to any sub-processes.
- */
- {
- char buf[64];
- xsnprintf(buf, sizeof(buf), "%d", term_columns());
- setenv("COLUMNS", buf, 0);
- }
- setenv("GIT_PAGER_IN_USE", "true", 1);
- /* spawn the pager */
- prepare_pager_args(&pager_process, pager);
- pager_process.in = -1;
- strvec_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
- if (start_command(&pager_process))
- return;
- /* original process continues, but writes to the pipe */
- dup2(pager_process.in, 1);
- if (isatty(2))
- dup2(pager_process.in, 2);
- close(pager_process.in);
- /* this makes sure that the parent terminates after the pager */
- sigchain_push_common(wait_for_pager_signal);
- atexit(wait_for_pager_atexit);
- }
- int pager_in_use(void)
- {
- return git_env_bool("GIT_PAGER_IN_USE", 0);
- }
- /*
- * Return cached value (if set) or $COLUMNS environment variable (if
- * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
- * and default to 80 if all else fails.
- */
- int term_columns(void)
- {
- static int term_columns_at_startup;
- char *col_string;
- int n_cols;
- if (term_columns_at_startup)
- return term_columns_at_startup;
- term_columns_at_startup = 80;
- col_string = getenv("COLUMNS");
- if (col_string && (n_cols = atoi(col_string)) > 0)
- term_columns_at_startup = n_cols;
- #ifdef TIOCGWINSZ
- else {
- struct winsize ws;
- if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
- term_columns_at_startup = ws.ws_col;
- }
- #endif
- return term_columns_at_startup;
- }
- /*
- * Clear the entire line, leave cursor in first column.
- */
- void term_clear_line(void)
- {
- if (is_terminal_dumb())
- /*
- * Fall back to print a terminal width worth of space
- * characters (hoping that the terminal is still as wide
- * as it was upon the first call to term_columns()).
- */
- fprintf(stderr, "\r%*s\r", term_columns(), "");
- else
- /*
- * On non-dumb terminals use an escape sequence to clear
- * the whole line, no matter how wide the terminal.
- */
- fputs("\r\033[K", stderr);
- }
- /*
- * How many columns do we need to show this number in decimal?
- */
- int decimal_width(uintmax_t number)
- {
- int width;
- for (width = 1; number >= 10; width++)
- number /= 10;
- return width;
- }
- struct pager_command_config_data {
- const char *cmd;
- int want;
- char *value;
- };
- static int pager_command_config(const char *var, const char *value, void *vdata)
- {
- struct pager_command_config_data *data = vdata;
- const char *cmd;
- if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
- int b = git_parse_maybe_bool(value);
- if (b >= 0)
- data->want = b;
- else {
- data->want = 1;
- data->value = xstrdup(value);
- }
- }
- return 0;
- }
- /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
- int check_pager_config(const char *cmd)
- {
- struct pager_command_config_data data;
- data.cmd = cmd;
- data.want = -1;
- data.value = NULL;
- read_early_config(pager_command_config, &data);
- if (data.value)
- pager_program = data.value;
- return data.want;
- }
|