git

Форк
0
/
usage.c 
367 строк · 7.5 Кб
1
/*
2
 * GIT - The information manager from hell
3
 *
4
 * Copyright (C) Linus Torvalds, 2005
5
 */
6
#include "git-compat-util.h"
7
#include "gettext.h"
8
#include "trace2.h"
9

10
static void vreportf(const char *prefix, const char *err, va_list params)
11
{
12
	char msg[4096];
13
	char *p, *pend = msg + sizeof(msg);
14
	size_t prefix_len = strlen(prefix);
15

16
	if (sizeof(msg) <= prefix_len) {
17
		fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
18
		abort();
19
	}
20
	memcpy(msg, prefix, prefix_len);
21
	p = msg + prefix_len;
22
	if (vsnprintf(p, pend - p, err, params) < 0) {
23
		fprintf(stderr, _("error: unable to format message: %s\n"),
24
			err);
25
		*p = '\0'; /* vsnprintf() failed, clip at prefix */
26
	}
27

28
	for (; p != pend - 1 && *p; p++) {
29
		if (iscntrl(*p) && *p != '\t' && *p != '\n')
30
			*p = '?';
31
	}
32

33
	*(p++) = '\n'; /* we no longer need a NUL */
34
	fflush(stderr);
35
	write_in_full(2, msg, p - msg);
36
}
37

38
static NORETURN void usage_builtin(const char *err, va_list params)
39
{
40
	vreportf(_("usage: "), err, params);
41

42
	/*
43
	 * When we detect a usage error *before* the command dispatch in
44
	 * cmd_main(), we don't know what verb to report.  Force it to this
45
	 * to facilitate post-processing.
46
	 */
47
	trace2_cmd_name("_usage_");
48

49
	/*
50
	 * Currently, the (err, params) are usually just the static usage
51
	 * string which isn't very useful here.  Usually, the call site
52
	 * manually calls fprintf(stderr,...) with the actual detailed
53
	 * syntax error before calling usage().
54
	 *
55
	 * TODO It would be nice to update the call sites to pass both
56
	 * the static usage string and the detailed error message.
57
	 */
58

59
	exit(129);
60
}
61

62
static void die_message_builtin(const char *err, va_list params)
63
{
64
	trace2_cmd_error_va(err, params);
65
	vreportf(_("fatal: "), err, params);
66
}
67

68
/*
69
 * We call trace2_cmd_error_va() in the below functions first and
70
 * expect it to va_copy 'params' before using it (because an 'ap' can
71
 * only be walked once).
72
 */
73
static NORETURN void die_builtin(const char *err, va_list params)
74
{
75
	report_fn die_message_fn = get_die_message_routine();
76

77
	die_message_fn(err, params);
78
	exit(128);
79
}
80

81
static void error_builtin(const char *err, va_list params)
82
{
83
	trace2_cmd_error_va(err, params);
84

85
	vreportf(_("error: "), err, params);
86
}
87

88
static void warn_builtin(const char *warn, va_list params)
89
{
90
	trace2_cmd_error_va(warn, params);
91

92
	vreportf(_("warning: "), warn, params);
93
}
94

95
static int die_is_recursing_builtin(void)
96
{
97
	static int dying;
98
	/*
99
	 * Just an arbitrary number X where "a < x < b" where "a" is
100
	 * "maximum number of pthreads we'll ever plausibly spawn" and
101
	 * "b" is "something less than Inf", since the point is to
102
	 * prevent infinite recursion.
103
	 */
104
	static const int recursion_limit = 1024;
105

106
	dying++;
107
	if (dying > recursion_limit) {
108
		return 1;
109
	} else if (dying == 2) {
110
		warning("die() called many times. Recursion error or racy threaded death!");
111
		return 0;
112
	} else {
113
		return 0;
114
	}
115
}
116

117
/* If we are in a dlopen()ed .so write to a global variable would segfault
118
 * (ugh), so keep things static. */
119
static NORETURN_PTR report_fn usage_routine = usage_builtin;
120
static NORETURN_PTR report_fn die_routine = die_builtin;
121
static report_fn die_message_routine = die_message_builtin;
122
static report_fn error_routine = error_builtin;
123
static report_fn warn_routine = warn_builtin;
124
static int (*die_is_recursing)(void) = die_is_recursing_builtin;
125

126
void set_die_routine(NORETURN_PTR report_fn routine)
127
{
128
	die_routine = routine;
129
}
130

131
report_fn get_die_message_routine(void)
132
{
133
	return die_message_routine;
134
}
135

136
void set_error_routine(report_fn routine)
137
{
138
	error_routine = routine;
139
}
140

141
report_fn get_error_routine(void)
142
{
143
	return error_routine;
144
}
145

146
void set_warn_routine(report_fn routine)
147
{
148
	warn_routine = routine;
149
}
150

151
report_fn get_warn_routine(void)
152
{
153
	return warn_routine;
154
}
155

156
void set_die_is_recursing_routine(int (*routine)(void))
157
{
158
	die_is_recursing = routine;
159
}
160

161
void NORETURN usagef(const char *err, ...)
162
{
163
	va_list params;
164

165
	va_start(params, err);
166
	usage_routine(err, params);
167
	va_end(params);
168
}
169

170
void NORETURN usage(const char *err)
171
{
172
	usagef("%s", err);
173
}
174

175
void NORETURN die(const char *err, ...)
176
{
177
	va_list params;
178

179
	if (die_is_recursing()) {
180
		fputs("fatal: recursion detected in die handler\n", stderr);
181
		exit(128);
182
	}
183

184
	va_start(params, err);
185
	die_routine(err, params);
186
	va_end(params);
187
}
188

189
static const char *fmt_with_err(char *buf, int n, const char *fmt)
190
{
191
	char str_error[256], *err;
192
	int i, j;
193

194
	err = strerror(errno);
195
	for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
196
		if ((str_error[j++] = err[i++]) != '%')
197
			continue;
198
		if (j < sizeof(str_error) - 1) {
199
			str_error[j++] = '%';
200
		} else {
201
			/* No room to double the '%', so we overwrite it with
202
			 * '\0' below */
203
			j--;
204
			break;
205
		}
206
	}
207
	str_error[j] = 0;
208
	/* Truncation is acceptable here */
209
	snprintf(buf, n, "%s: %s", fmt, str_error);
210
	return buf;
211
}
212

213
void NORETURN die_errno(const char *fmt, ...)
214
{
215
	char buf[1024];
216
	va_list params;
217

218
	if (die_is_recursing()) {
219
		fputs("fatal: recursion detected in die_errno handler\n",
220
			stderr);
221
		exit(128);
222
	}
223

224
	va_start(params, fmt);
225
	die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
226
	va_end(params);
227
}
228

229
#undef die_message
230
int die_message(const char *err, ...)
231
{
232
	va_list params;
233

234
	va_start(params, err);
235
	die_message_routine(err, params);
236
	va_end(params);
237
	return 128;
238
}
239

240
#undef die_message_errno
241
int die_message_errno(const char *fmt, ...)
242
{
243
	char buf[1024];
244
	va_list params;
245

246
	va_start(params, fmt);
247
	die_message_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
248
	va_end(params);
249
	return 128;
250
}
251

252
#undef error_errno
253
int error_errno(const char *fmt, ...)
254
{
255
	char buf[1024];
256
	va_list params;
257

258
	va_start(params, fmt);
259
	error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
260
	va_end(params);
261
	return -1;
262
}
263

264
#undef error
265
int error(const char *err, ...)
266
{
267
	va_list params;
268

269
	va_start(params, err);
270
	error_routine(err, params);
271
	va_end(params);
272
	return -1;
273
}
274

275
void warning_errno(const char *warn, ...)
276
{
277
	char buf[1024];
278
	va_list params;
279

280
	va_start(params, warn);
281
	warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
282
	va_end(params);
283
}
284

285
void warning(const char *warn, ...)
286
{
287
	va_list params;
288

289
	va_start(params, warn);
290
	warn_routine(warn, params);
291
	va_end(params);
292
}
293

294
/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */
295
int BUG_exit_code;
296

297
static void BUG_vfl_common(const char *file, int line, const char *fmt,
298
			   va_list params)
299
{
300
	char prefix[256];
301

302
	/* truncation via snprintf is OK here */
303
	snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
304

305
	vreportf(prefix, fmt, params);
306
}
307

308
static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
309
{
310
	va_list params_copy;
311
	static int in_bug;
312

313
	va_copy(params_copy, params);
314
	BUG_vfl_common(file, line, fmt, params);
315

316
	if (in_bug)
317
		abort();
318
	in_bug = 1;
319

320
	trace2_cmd_error_va(fmt, params_copy);
321

322
	if (BUG_exit_code)
323
		exit(BUG_exit_code);
324
	abort();
325
}
326

327
NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
328
{
329
	va_list ap;
330

331
	bug_called_must_BUG = 0;
332

333
	va_start(ap, fmt);
334
	BUG_vfl(file, line, fmt, ap);
335
	va_end(ap);
336
}
337

338
int bug_called_must_BUG;
339
void bug_fl(const char *file, int line, const char *fmt, ...)
340
{
341
	va_list ap;
342

343
	bug_called_must_BUG = 1;
344

345
	va_start(ap, fmt);
346
	BUG_vfl_common(file, line, fmt, ap);
347
	va_end(ap);
348

349
	va_start(ap, fmt);
350
	trace2_cmd_error_va(fmt, ap);
351
	va_end(ap);
352
}
353

354
#ifdef SUPPRESS_ANNOTATED_LEAKS
355
void unleak_memory(const void *ptr, size_t len)
356
{
357
	static struct suppressed_leak_root {
358
		struct suppressed_leak_root *next;
359
		char data[FLEX_ARRAY];
360
	} *suppressed_leaks;
361
	struct suppressed_leak_root *root;
362

363
	FLEX_ALLOC_MEM(root, data, ptr, len);
364
	root->next = suppressed_leaks;
365
	suppressed_leaks = root;
366
}
367
#endif
368

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

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

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

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