Git fork
1/*
2 * GIT - The information manager from hell
3 *
4 * Copyright (C) Linus Torvalds, 2005
5 */
6
7#include "git-compat-util.h"
8#include "gettext.h"
9#include "trace2.h"
10#include "strbuf.h"
11
12static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)
13{
14 char msg[4096];
15 char *p, *pend = msg + sizeof(msg);
16 size_t prefix_len = strlen(prefix);
17
18 if (sizeof(msg) <= prefix_len) {
19 fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
20 abort();
21 }
22 memcpy(msg, prefix, prefix_len);
23 p = msg + prefix_len;
24 if (vsnprintf(p, pend - p, err, params) < 0) {
25 fprintf(stderr, _("error: unable to format message: %s\n"),
26 err);
27 *p = '\0'; /* vsnprintf() failed, clip at prefix */
28 }
29
30 for (; p != pend - 1 && *p; p++) {
31 if (iscntrl(*p) && *p != '\t' && *p != '\n')
32 *p = '?';
33 }
34
35 *(p++) = '\n'; /* we no longer need a NUL */
36 fflush(f);
37 write_in_full(fileno(f), msg, p - msg);
38}
39
40static void vreportf(const char *prefix, const char *err, va_list params)
41{
42 vfreportf(stderr, prefix, err, params);
43}
44
45static NORETURN void usage_builtin(const char *err, va_list params)
46{
47 vreportf(_("usage: "), err, params);
48
49 /*
50 * When we detect a usage error *before* the command dispatch in
51 * cmd_main(), we don't know what verb to report. Force it to this
52 * to facilitate post-processing.
53 */
54 trace2_cmd_name("_usage_");
55
56 /*
57 * Currently, the (err, params) are usually just the static usage
58 * string which isn't very useful here. Usually, the call site
59 * manually calls fprintf(stderr,...) with the actual detailed
60 * syntax error before calling usage().
61 *
62 * TODO It would be nice to update the call sites to pass both
63 * the static usage string and the detailed error message.
64 */
65
66 exit(129);
67}
68
69static void die_message_builtin(const char *err, va_list params)
70{
71 if (!err)
72 return;
73 trace2_cmd_error_va(err, params);
74 vreportf(_("fatal: "), err, params);
75}
76
77/*
78 * We call trace2_cmd_error_va() in the below functions first and
79 * expect it to va_copy 'params' before using it (because an 'ap' can
80 * only be walked once).
81 */
82static NORETURN void die_builtin(const char *err, va_list params)
83{
84 report_fn die_message_fn = get_die_message_routine();
85
86 die_message_fn(err, params);
87 exit(128);
88}
89
90static void error_builtin(const char *err, va_list params)
91{
92 trace2_cmd_error_va(err, params);
93
94 vreportf(_("error: "), err, params);
95}
96
97static void warn_builtin(const char *warn, va_list params)
98{
99 trace2_cmd_error_va(warn, params);
100
101 vreportf(_("warning: "), warn, params);
102}
103
104static int die_is_recursing_builtin(void)
105{
106 static int dying;
107 /*
108 * Just an arbitrary number X where "a < x < b" where "a" is
109 * "maximum number of pthreads we'll ever plausibly spawn" and
110 * "b" is "something less than Inf", since the point is to
111 * prevent infinite recursion.
112 */
113 static const int recursion_limit = 1024;
114
115 dying++;
116 if (dying > recursion_limit) {
117 return 1;
118 } else if (dying == 2) {
119 warning("die() called many times. Recursion error or racy threaded death!");
120 return 0;
121 } else {
122 return 0;
123 }
124}
125
126/* If we are in a dlopen()ed .so write to a global variable would segfault
127 * (ugh), so keep things static. */
128static NORETURN_PTR report_fn usage_routine = usage_builtin;
129static NORETURN_PTR report_fn die_routine = die_builtin;
130static report_fn die_message_routine = die_message_builtin;
131static report_fn error_routine = error_builtin;
132static report_fn warn_routine = warn_builtin;
133static int (*die_is_recursing)(void) = die_is_recursing_builtin;
134
135void set_die_routine(NORETURN_PTR report_fn routine)
136{
137 die_routine = routine;
138}
139
140report_fn get_die_message_routine(void)
141{
142 return die_message_routine;
143}
144
145void set_error_routine(report_fn routine)
146{
147 error_routine = routine;
148}
149
150report_fn get_error_routine(void)
151{
152 return error_routine;
153}
154
155void set_warn_routine(report_fn routine)
156{
157 warn_routine = routine;
158}
159
160report_fn get_warn_routine(void)
161{
162 return warn_routine;
163}
164
165void set_die_is_recursing_routine(int (*routine)(void))
166{
167 die_is_recursing = routine;
168}
169
170void NORETURN usagef(const char *err, ...)
171{
172 va_list params;
173
174 va_start(params, err);
175 usage_routine(err, params);
176 va_end(params);
177}
178
179void NORETURN usage(const char *err)
180{
181 usagef("%s", err);
182}
183
184static void show_usage_if_asked_helper(const char *err, ...)
185{
186 va_list params;
187
188 va_start(params, err);
189 vfreportf(stdout, _("usage: "), err, params);
190 va_end(params);
191 exit(129);
192}
193
194void show_usage_if_asked(int ac, const char **av, const char *err)
195{
196 if (ac == 2 && (!strcmp(av[1], "-h") ||
197 !strcmp(av[1], "--help-all")))
198 show_usage_if_asked_helper(err);
199}
200
201void NORETURN die(const char *err, ...)
202{
203 va_list params;
204
205 if (die_is_recursing()) {
206 fputs("fatal: recursion detected in die handler\n", stderr);
207 exit(128);
208 }
209
210 va_start(params, err);
211 die_routine(err, params);
212 va_end(params);
213}
214
215static const char *fmt_with_err(char *buf, int n, const char *fmt)
216{
217 char str_error[256], *err;
218 size_t i, j;
219
220 err = strerror(errno);
221 for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
222 if ((str_error[j++] = err[i++]) != '%')
223 continue;
224 if (j < sizeof(str_error) - 1) {
225 str_error[j++] = '%';
226 } else {
227 /* No room to double the '%', so we overwrite it with
228 * '\0' below */
229 j--;
230 break;
231 }
232 }
233 str_error[j] = 0;
234 /* Truncation is acceptable here */
235 snprintf(buf, n, "%s: %s", fmt, str_error);
236 return buf;
237}
238
239void NORETURN die_errno(const char *fmt, ...)
240{
241 char buf[1024];
242 va_list params;
243
244 if (die_is_recursing()) {
245 fputs("fatal: recursion detected in die_errno handler\n",
246 stderr);
247 exit(128);
248 }
249
250 va_start(params, fmt);
251 die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
252 va_end(params);
253}
254
255#undef die_message
256int die_message(const char *err, ...)
257{
258 va_list params;
259
260 va_start(params, err);
261 die_message_routine(err, params);
262 va_end(params);
263 return 128;
264}
265
266#undef die_message_errno
267int die_message_errno(const char *fmt, ...)
268{
269 char buf[1024];
270 va_list params;
271
272 va_start(params, fmt);
273 die_message_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
274 va_end(params);
275 return 128;
276}
277
278#undef error_errno
279int error_errno(const char *fmt, ...)
280{
281 char buf[1024];
282 va_list params;
283
284 va_start(params, fmt);
285 error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
286 va_end(params);
287 return -1;
288}
289
290#undef error
291int error(const char *err, ...)
292{
293 va_list params;
294
295 va_start(params, err);
296 error_routine(err, params);
297 va_end(params);
298 return -1;
299}
300
301void warning_errno(const char *warn, ...)
302{
303 char buf[1024];
304 va_list params;
305
306 va_start(params, warn);
307 warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
308 va_end(params);
309}
310
311void warning(const char *warn, ...)
312{
313 va_list params;
314
315 va_start(params, warn);
316 warn_routine(warn, params);
317 va_end(params);
318}
319
320/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */
321int BUG_exit_code;
322
323static void BUG_vfl_common(const char *file, int line, const char *fmt,
324 va_list params)
325{
326 char prefix[256];
327
328 /* truncation via snprintf is OK here */
329 snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
330
331 vreportf(prefix, fmt, params);
332}
333
334static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
335{
336 va_list params_copy;
337 static int in_bug;
338
339 va_copy(params_copy, params);
340 BUG_vfl_common(file, line, fmt, params);
341
342 if (in_bug)
343 abort();
344 in_bug = 1;
345
346 trace2_cmd_error_va(fmt, params_copy);
347
348 if (BUG_exit_code)
349 exit(BUG_exit_code);
350 abort();
351}
352
353NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
354{
355 va_list ap;
356
357 bug_called_must_BUG = 0;
358
359 va_start(ap, fmt);
360 BUG_vfl(file, line, fmt, ap);
361 va_end(ap);
362}
363
364int bug_called_must_BUG;
365void bug_fl(const char *file, int line, const char *fmt, ...)
366{
367 va_list ap;
368
369 bug_called_must_BUG = 1;
370
371 va_start(ap, fmt);
372 BUG_vfl_common(file, line, fmt, ap);
373 va_end(ap);
374
375 va_start(ap, fmt);
376 trace2_cmd_error_va(fmt, ap);
377 va_end(ap);
378}
379
380
381NORETURN void you_still_use_that(const char *command_name, const char *hint)
382{
383 struct strbuf percent_encoded = STRBUF_INIT;
384 strbuf_add_percentencode(&percent_encoded,
385 command_name,
386 STRBUF_ENCODE_SLASH);
387
388 fprintf(stderr,
389 _("'%s' is nominated for removal.\n"), command_name);
390
391 if (hint)
392 fputs(hint, stderr);
393
394 fprintf(stderr,
395 _("If you still use this command, here's what you can do:\n"
396 "\n"
397 "- read https://git-scm.com/docs/BreakingChanges.html\n"
398 "- check if anyone has discussed this on the mailing\n"
399 " list and if they came up with something that can\n"
400 " help you: https://lore.kernel.org/git/?q=%s\n"
401 "- send an email to <git@vger.kernel.org> to let us\n"
402 " know that you still use this command and were unable\n"
403 " to determine a suitable replacement\n"
404 "\n"),
405 percent_encoded.buf);
406 strbuf_release(&percent_encoded);
407 die(_("refusing to run without --i-still-use-this"));
408}