Merge branch 'sb/plug-leaks'
[git] / pager.c
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
9 struct pager_config {
10         const char *cmd;
11         int want;
12         char *value;
13 };
14
15 /*
16  * This is split up from the rest of git so that we can do
17  * something different on Windows.
18  */
19
20 static const char *pager_argv[] = { NULL, NULL };
21 static struct child_process pager_process;
22
23 static void wait_for_pager(void)
24 {
25         fflush(stdout);
26         fflush(stderr);
27         /* signal EOF to pager */
28         close(1);
29         close(2);
30         finish_command(&pager_process);
31 }
32
33 static void wait_for_pager_signal(int signo)
34 {
35         wait_for_pager();
36         sigchain_pop(signo);
37         raise(signo);
38 }
39
40 const char *git_pager(int stdout_is_tty)
41 {
42         const char *pager;
43
44         if (!stdout_is_tty)
45                 return NULL;
46
47         pager = getenv("GIT_PAGER");
48         if (!pager) {
49                 if (!pager_program)
50                         git_config(git_default_config, NULL);
51                 pager = pager_program;
52         }
53         if (!pager)
54                 pager = getenv("PAGER");
55         if (!pager)
56                 pager = DEFAULT_PAGER;
57         if (!*pager || !strcmp(pager, "cat"))
58                 pager = NULL;
59
60         return pager;
61 }
62
63 void setup_pager(void)
64 {
65         const char *pager = git_pager(isatty(1));
66
67         if (!pager)
68                 return;
69
70         /*
71          * force computing the width of the terminal before we redirect
72          * the standard output to the pager.
73          */
74         (void) term_columns();
75
76         setenv("GIT_PAGER_IN_USE", "true", 1);
77
78         /* spawn the pager */
79         pager_argv[0] = pager;
80         pager_process.use_shell = 1;
81         pager_process.argv = pager_argv;
82         pager_process.in = -1;
83         if (!getenv("LESS") || !getenv("LV")) {
84                 static const char *env[3];
85                 int i = 0;
86
87                 if (!getenv("LESS"))
88                         env[i++] = "LESS=FRX";
89                 if (!getenv("LV"))
90                         env[i++] = "LV=-c";
91                 env[i] = NULL;
92                 pager_process.env = env;
93         }
94         if (start_command(&pager_process))
95                 return;
96
97         /* original process continues, but writes to the pipe */
98         dup2(pager_process.in, 1);
99         if (isatty(2))
100                 dup2(pager_process.in, 2);
101         close(pager_process.in);
102
103         /* this makes sure that the parent terminates after the pager */
104         sigchain_push_common(wait_for_pager_signal);
105         atexit(wait_for_pager);
106 }
107
108 int pager_in_use(void)
109 {
110         const char *env;
111         env = getenv("GIT_PAGER_IN_USE");
112         return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
113 }
114
115 /*
116  * Return cached value (if set) or $COLUMNS environment variable (if
117  * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
118  * and default to 80 if all else fails.
119  */
120 int term_columns(void)
121 {
122         static int term_columns_at_startup;
123
124         char *col_string;
125         int n_cols;
126
127         if (term_columns_at_startup)
128                 return term_columns_at_startup;
129
130         term_columns_at_startup = 80;
131
132         col_string = getenv("COLUMNS");
133         if (col_string && (n_cols = atoi(col_string)) > 0)
134                 term_columns_at_startup = n_cols;
135 #ifdef TIOCGWINSZ
136         else {
137                 struct winsize ws;
138                 if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
139                         term_columns_at_startup = ws.ws_col;
140         }
141 #endif
142
143         return term_columns_at_startup;
144 }
145
146 /*
147  * How many columns do we need to show this number in decimal?
148  */
149 int decimal_width(int number)
150 {
151         int i, width;
152
153         for (width = 1, i = 10; i <= number; width++)
154                 i *= 10;
155         return width;
156 }
157
158 static int pager_command_config(const char *var, const char *value, void *data)
159 {
160         struct pager_config *c = data;
161         if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) {
162                 int b = git_config_maybe_bool(var, value);
163                 if (b >= 0)
164                         c->want = b;
165                 else {
166                         c->want = 1;
167                         c->value = xstrdup(value);
168                 }
169         }
170         return 0;
171 }
172
173 /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
174 int check_pager_config(const char *cmd)
175 {
176         struct pager_config c;
177         c.cmd = cmd;
178         c.want = -1;
179         c.value = NULL;
180         git_config(pager_command_config, &c);
181         if (c.value)
182                 pager_program = c.value;
183         return c.want;
184 }