git

Форк
0
/
pager.c 
306 строк · 5.9 Кб
1
#include "git-compat-util.h"
2
#include "config.h"
3
#include "editor.h"
4
#include "pager.h"
5
#include "run-command.h"
6
#include "sigchain.h"
7
#include "alias.h"
8

9
int pager_use_color = 1;
10

11
#ifndef DEFAULT_PAGER
12
#define DEFAULT_PAGER "less"
13
#endif
14

15
static struct child_process pager_process;
16
static char *pager_program;
17
static int old_fd1 = -1, old_fd2 = -1;
18

19
/* Is the value coming back from term_columns() just a guess? */
20
static int term_columns_guessed;
21

22

23
static void close_pager_fds(void)
24
{
25
	/* signal EOF to pager */
26
	close(1);
27
	if (old_fd2 != -1)
28
		close(2);
29
}
30

31
static void finish_pager(void)
32
{
33
	fflush(stdout);
34
	fflush(stderr);
35
	close_pager_fds();
36
	finish_command(&pager_process);
37
}
38

39
static void wait_for_pager_atexit(void)
40
{
41
	if (old_fd1 == -1)
42
		return;
43

44
	finish_pager();
45
}
46

47
void wait_for_pager(void)
48
{
49
	if (old_fd1 == -1)
50
		return;
51

52
	finish_pager();
53
	sigchain_pop_common();
54
	unsetenv("GIT_PAGER_IN_USE");
55
	dup2(old_fd1, 1);
56
	close(old_fd1);
57
	old_fd1 = -1;
58
	if (old_fd2 != -1) {
59
		dup2(old_fd2, 2);
60
		close(old_fd2);
61
		old_fd2 = -1;
62
	}
63
}
64

65
static void wait_for_pager_signal(int signo)
66
{
67
	if (old_fd1 == -1)
68
		return;
69

70
	close_pager_fds();
71
	finish_command_in_signal(&pager_process);
72
	sigchain_pop(signo);
73
	raise(signo);
74
}
75

76
static int core_pager_config(const char *var, const char *value,
77
			     const struct config_context *ctx UNUSED,
78
			     void *data UNUSED)
79
{
80
	if (!strcmp(var, "core.pager"))
81
		return git_config_string(&pager_program, var, value);
82
	return 0;
83
}
84

85
const char *git_pager(int stdout_is_tty)
86
{
87
	const char *pager;
88

89
	if (!stdout_is_tty)
90
		return NULL;
91

92
	pager = getenv("GIT_PAGER");
93
	if (!pager) {
94
		if (!pager_program)
95
			read_early_config(core_pager_config, NULL);
96
		pager = pager_program;
97
	}
98
	if (!pager)
99
		pager = getenv("PAGER");
100
	if (!pager)
101
		pager = DEFAULT_PAGER;
102
	if (!*pager || !strcmp(pager, "cat"))
103
		pager = NULL;
104

105
	return pager;
106
}
107

108
static void setup_pager_env(struct strvec *env)
109
{
110
	const char **argv;
111
	int i;
112
	char *pager_env = xstrdup(PAGER_ENV);
113
	int n = split_cmdline(pager_env, &argv);
114

115
	if (n < 0)
116
		die("malformed build-time PAGER_ENV: %s",
117
			split_cmdline_strerror(n));
118

119
	for (i = 0; i < n; i++) {
120
		char *cp = strchr(argv[i], '=');
121

122
		if (!cp)
123
			die("malformed build-time PAGER_ENV");
124

125
		*cp = '\0';
126
		if (!getenv(argv[i])) {
127
			*cp = '=';
128
			strvec_push(env, argv[i]);
129
		}
130
	}
131
	free(pager_env);
132
	free(argv);
133
}
134

135
void prepare_pager_args(struct child_process *pager_process, const char *pager)
136
{
137
	strvec_push(&pager_process->args, pager);
138
	pager_process->use_shell = 1;
139
	setup_pager_env(&pager_process->env);
140
	pager_process->trace2_child_class = "pager";
141
}
142

143
void setup_pager(void)
144
{
145
	static int once = 0;
146
	const char *pager = git_pager(isatty(1));
147

148
	if (!pager)
149
		return;
150

151
	/*
152
	 * After we redirect standard output, we won't be able to use an ioctl
153
	 * to get the terminal size. Let's grab it now, and then set $COLUMNS
154
	 * to communicate it to any sub-processes.
155
	 */
156
	{
157
		char buf[64];
158
		xsnprintf(buf, sizeof(buf), "%d", term_columns());
159
		if (!term_columns_guessed)
160
			setenv("COLUMNS", buf, 0);
161
	}
162

163
	setenv("GIT_PAGER_IN_USE", "true", 1);
164

165
	child_process_init(&pager_process);
166

167
	/* spawn the pager */
168
	prepare_pager_args(&pager_process, pager);
169
	pager_process.in = -1;
170
	strvec_push(&pager_process.env, "GIT_PAGER_IN_USE");
171
	if (start_command(&pager_process))
172
		die("unable to execute pager '%s'", pager);
173

174
	/* original process continues, but writes to the pipe */
175
	old_fd1 = dup(1);
176
	dup2(pager_process.in, 1);
177
	if (isatty(2)) {
178
		old_fd2 = dup(2);
179
		dup2(pager_process.in, 2);
180
	}
181
	close(pager_process.in);
182

183
	sigchain_push_common(wait_for_pager_signal);
184

185
	if (!once) {
186
		once++;
187
		atexit(wait_for_pager_atexit);
188
	}
189
}
190

191
int pager_in_use(void)
192
{
193
	return git_env_bool("GIT_PAGER_IN_USE", 0);
194
}
195

196
/*
197
 * Return cached value (if set) or $COLUMNS environment variable (if
198
 * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
199
 * and default to 80 if all else fails.
200
 */
201
int term_columns(void)
202
{
203
	static int term_columns_at_startup;
204

205
	char *col_string;
206
	int n_cols;
207

208
	if (term_columns_at_startup)
209
		return term_columns_at_startup;
210

211
	term_columns_at_startup = 80;
212
	term_columns_guessed = 1;
213

214
	col_string = getenv("COLUMNS");
215
	if (col_string && (n_cols = atoi(col_string)) > 0) {
216
		term_columns_at_startup = n_cols;
217
		term_columns_guessed = 0;
218
	}
219
#ifdef TIOCGWINSZ
220
	else {
221
		struct winsize ws;
222
		if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col) {
223
			term_columns_at_startup = ws.ws_col;
224
			term_columns_guessed = 0;
225
		}
226
	}
227
#endif
228

229
	return term_columns_at_startup;
230
}
231

232
/*
233
 * Clear the entire line, leave cursor in first column.
234
 */
235
void term_clear_line(void)
236
{
237
	if (!isatty(2))
238
		return;
239
	if (is_terminal_dumb())
240
		/*
241
		 * Fall back to print a terminal width worth of space
242
		 * characters (hoping that the terminal is still as wide
243
		 * as it was upon the first call to term_columns()).
244
		 */
245
		fprintf(stderr, "\r%*s\r", term_columns(), "");
246
	else
247
		/*
248
		 * On non-dumb terminals use an escape sequence to clear
249
		 * the whole line, no matter how wide the terminal.
250
		 */
251
		fputs("\r\033[K", stderr);
252
}
253

254
/*
255
 * How many columns do we need to show this number in decimal?
256
 */
257
int decimal_width(uintmax_t number)
258
{
259
	int width;
260

261
	for (width = 1; number >= 10; width++)
262
		number /= 10;
263
	return width;
264
}
265

266
struct pager_command_config_data {
267
	const char *cmd;
268
	int want;
269
	char *value;
270
};
271

272
static int pager_command_config(const char *var, const char *value,
273
				const struct config_context *ctx UNUSED,
274
				void *vdata)
275
{
276
	struct pager_command_config_data *data = vdata;
277
	const char *cmd;
278

279
	if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
280
		int b = git_parse_maybe_bool(value);
281
		if (b >= 0)
282
			data->want = b;
283
		else {
284
			data->want = 1;
285
			data->value = xstrdup(value);
286
		}
287
	}
288

289
	return 0;
290
}
291

292
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
293
int check_pager_config(const char *cmd)
294
{
295
	struct pager_command_config_data data;
296

297
	data.cmd = cmd;
298
	data.want = -1;
299
	data.value = NULL;
300

301
	read_early_config(pager_command_config, &data);
302

303
	if (data.value)
304
		pager_program = data.value;
305
	return data.want;
306
}
307

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.