Raw
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
12 static 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
40 static void vreportf(const char *prefix, const char *err, va_list params)
41 {
42 vfreportf(stderr, prefix, err, params);
43 }
44
45 static 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
69 static 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 */
82 static 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
90 static 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
97 static 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
104 static 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. */
128 static NORETURN_PTR report_fn usage_routine = usage_builtin;
129 static NORETURN_PTR report_fn die_routine = die_builtin;
130 static report_fn die_message_routine = die_message_builtin;
131 static report_fn error_routine = error_builtin;
132 static report_fn warn_routine = warn_builtin;
133 static int (*die_is_recursing)(void) = die_is_recursing_builtin;
134
135 void set_die_routine(NORETURN_PTR report_fn routine)
136 {
137 die_routine = routine;
138 }
139
140 report_fn get_die_message_routine(void)
141 {
142 return die_message_routine;
143 }
144
145 void set_error_routine(report_fn routine)
146 {
147 error_routine = routine;
148 }
149
150 report_fn get_error_routine(void)
151 {
152 return error_routine;
153 }
154
155 void set_warn_routine(report_fn routine)
156 {
157 warn_routine = routine;
158 }
159
160 report_fn get_warn_routine(void)
161 {
162 return warn_routine;
163 }
164
165 void set_die_is_recursing_routine(int (*routine)(void))
166 {
167 die_is_recursing = routine;
168 }
169
170 void 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
179 void NORETURN usage(const char *err)
180 {
181 usagef("%s", err);
182 }
183
184 static 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
194 void 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
201 void 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
215 static 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
239 void 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
256 int 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
267 int 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
279 int 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
291 int 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
301 void 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
311 void 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. */
321 int BUG_exit_code;
322
323 static 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
334 static 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
353 NORETURN 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
364 int bug_called_must_BUG;
365 void 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
381 NORETURN 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 }