1#include "cache.h"
2#include "run-command.h"
3#include "sigchain.h"
45
#ifndef DEFAULT_PAGER
6#define DEFAULT_PAGER "less"
7#endif
89
/*
10* This is split up from the rest of git so that we can do
11* something different on Windows.
12*/
1314
#ifndef WIN32
15static void pager_preexec(void)
16{
17/*
18* Work around bug in "less" by not starting it until we
19* have real input
20*/
21fd_set in;
2223
FD_ZERO(&in);
24FD_SET(0, &in);
25select(1, &in, NULL, &in, NULL);
26}
27#endif
2829
static const char *pager_argv[] = { NULL, NULL };
30static struct child_process pager_process;
3132
static void wait_for_pager(void)
33{
34fflush(stdout);
35fflush(stderr);
36/* signal EOF to pager */
37close(1);
38close(2);
39finish_command(&pager_process);
40}
4142
static void wait_for_pager_signal(int signo)
43{
44wait_for_pager();
45sigchain_pop(signo);
46raise(signo);
47}
4849
const char *git_pager(int stdout_is_tty)
50{
51const char *pager;
5253
if (!stdout_is_tty)
54return NULL;
5556
pager = getenv("GIT_PAGER");
57if (!pager) {
58if (!pager_program)
59git_config(git_default_config, NULL);
60pager = pager_program;
61}
62if (!pager)
63pager = getenv("PAGER");
64if (!pager)
65pager = DEFAULT_PAGER;
66else if (!*pager || !strcmp(pager, "cat"))
67pager = NULL;
6869
return pager;
70}
7172
void setup_pager(void)
73{
74const char *pager = git_pager(isatty(1));
7576
if (!pager)
77return;
7879
/*
80* force computing the width of the terminal before we redirect
81* the standard output to the pager.
82*/
83(void) term_columns();
8485
setenv("GIT_PAGER_IN_USE", "true", 1);
8687
/* spawn the pager */
88pager_argv[0] = pager;
89pager_process.use_shell = 1;
90pager_process.argv = pager_argv;
91pager_process.in = -1;
92if (!getenv("LESS")) {
93static const char *env[] = { "LESS=FRSX", NULL };
94pager_process.env = env;
95}
96#ifndef WIN32
97pager_process.preexec_cb = pager_preexec;
98#endif
99if (start_command(&pager_process))
100return;
101102
/* original process continues, but writes to the pipe */
103dup2(pager_process.in, 1);
104if (isatty(2))
105dup2(pager_process.in, 2);
106close(pager_process.in);
107108
/* this makes sure that the parent terminates after the pager */
109sigchain_push_common(wait_for_pager_signal);
110atexit(wait_for_pager);
111}
112113
int pager_in_use(void)
114{
115const char *env;
116env = getenv("GIT_PAGER_IN_USE");
117return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
118}
119120
/*
121* Return cached value (if set) or $COLUMNS environment variable (if
122* set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
123* and default to 80 if all else fails.
124*/
125int term_columns(void)
126{
127static int term_columns_at_startup;
128129
char *col_string;
130int n_cols;
131132
if (term_columns_at_startup)
133return term_columns_at_startup;
134135
term_columns_at_startup = 80;
136137
col_string = getenv("COLUMNS");
138if (col_string && (n_cols = atoi(col_string)) > 0)
139term_columns_at_startup = n_cols;
140#ifdef TIOCGWINSZ
141else {
142struct winsize ws;
143if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
144term_columns_at_startup = ws.ws_col;
145}
146#endif
147148
return term_columns_at_startup;
149}
150151
/*
152* How many columns do we need to show this number in decimal?
153*/
154int decimal_width(int number)
155{
156int i, width;
157158
for (width = 1, i = 10; i <= number; width++)
159i *= 10;
160return width;
161}