Coverage Report

Created: 2024-09-08 06:23

/src/git/usage.c
Line
Count
Source (jump to first uncovered line)
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
0
{
12
0
  char msg[4096];
13
0
  char *p, *pend = msg + sizeof(msg);
14
0
  size_t prefix_len = strlen(prefix);
15
16
0
  if (sizeof(msg) <= prefix_len) {
17
0
    fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
18
0
    abort();
19
0
  }
20
0
  memcpy(msg, prefix, prefix_len);
21
0
  p = msg + prefix_len;
22
0
  if (vsnprintf(p, pend - p, err, params) < 0) {
23
0
    fprintf(stderr, _("error: unable to format message: %s\n"),
24
0
      err);
25
0
    *p = '\0'; /* vsnprintf() failed, clip at prefix */
26
0
  }
27
28
0
  for (; p != pend - 1 && *p; p++) {
29
0
    if (iscntrl(*p) && *p != '\t' && *p != '\n')
30
0
      *p = '?';
31
0
  }
32
33
0
  *(p++) = '\n'; /* we no longer need a NUL */
34
0
  fflush(stderr);
35
0
  write_in_full(2, msg, p - msg);
36
0
}
37
38
static NORETURN void usage_builtin(const char *err, va_list params)
39
0
{
40
0
  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
0
  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
0
  exit(129);
60
0
}
61
62
static void die_message_builtin(const char *err, va_list params)
63
0
{
64
0
  trace2_cmd_error_va(err, params);
65
0
  vreportf(_("fatal: "), err, params);
66
0
}
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
0
{
75
0
  report_fn die_message_fn = get_die_message_routine();
76
77
0
  die_message_fn(err, params);
78
0
  exit(128);
79
0
}
80
81
static void error_builtin(const char *err, va_list params)
82
0
{
83
0
  trace2_cmd_error_va(err, params);
84
85
0
  vreportf(_("error: "), err, params);
86
0
}
87
88
static void warn_builtin(const char *warn, va_list params)
89
0
{
90
0
  trace2_cmd_error_va(warn, params);
91
92
0
  vreportf(_("warning: "), warn, params);
93
0
}
94
95
static int die_is_recursing_builtin(void)
96
0
{
97
0
  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
0
  static const int recursion_limit = 1024;
105
106
0
  dying++;
107
0
  if (dying > recursion_limit) {
108
0
    return 1;
109
0
  } else if (dying == 2) {
110
0
    warning("die() called many times. Recursion error or racy threaded death!");
111
0
    return 0;
112
0
  } else {
113
0
    return 0;
114
0
  }
115
0
}
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
0
{
128
0
  die_routine = routine;
129
0
}
130
131
report_fn get_die_message_routine(void)
132
0
{
133
0
  return die_message_routine;
134
0
}
135
136
void set_error_routine(report_fn routine)
137
0
{
138
0
  error_routine = routine;
139
0
}
140
141
report_fn get_error_routine(void)
142
0
{
143
0
  return error_routine;
144
0
}
145
146
void set_warn_routine(report_fn routine)
147
0
{
148
0
  warn_routine = routine;
149
0
}
150
151
report_fn get_warn_routine(void)
152
0
{
153
0
  return warn_routine;
154
0
}
155
156
void set_die_is_recursing_routine(int (*routine)(void))
157
0
{
158
0
  die_is_recursing = routine;
159
0
}
160
161
void NORETURN usagef(const char *err, ...)
162
0
{
163
0
  va_list params;
164
165
0
  va_start(params, err);
166
0
  usage_routine(err, params);
167
0
  va_end(params);
168
0
}
169
170
void NORETURN usage(const char *err)
171
0
{
172
0
  usagef("%s", err);
173
0
}
174
175
void NORETURN die(const char *err, ...)
176
0
{
177
0
  va_list params;
178
179
0
  if (die_is_recursing()) {
180
0
    fputs("fatal: recursion detected in die handler\n", stderr);
181
0
    exit(128);
182
0
  }
183
184
0
  va_start(params, err);
185
0
  die_routine(err, params);
186
0
  va_end(params);
187
0
}
188
189
static const char *fmt_with_err(char *buf, int n, const char *fmt)
190
0
{
191
0
  char str_error[256], *err;
192
0
  int i, j;
193
194
0
  err = strerror(errno);
195
0
  for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
196
0
    if ((str_error[j++] = err[i++]) != '%')
197
0
      continue;
198
0
    if (j < sizeof(str_error) - 1) {
199
0
      str_error[j++] = '%';
200
0
    } else {
201
      /* No room to double the '%', so we overwrite it with
202
       * '\0' below */
203
0
      j--;
204
0
      break;
205
0
    }
206
0
  }
207
0
  str_error[j] = 0;
208
  /* Truncation is acceptable here */
209
0
  snprintf(buf, n, "%s: %s", fmt, str_error);
210
0
  return buf;
211
0
}
212
213
void NORETURN die_errno(const char *fmt, ...)
214
0
{
215
0
  char buf[1024];
216
0
  va_list params;
217
218
0
  if (die_is_recursing()) {
219
0
    fputs("fatal: recursion detected in die_errno handler\n",
220
0
      stderr);
221
0
    exit(128);
222
0
  }
223
224
0
  va_start(params, fmt);
225
0
  die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
226
0
  va_end(params);
227
0
}
228
229
#undef die_message
230
int die_message(const char *err, ...)
231
0
{
232
0
  va_list params;
233
234
0
  va_start(params, err);
235
0
  die_message_routine(err, params);
236
0
  va_end(params);
237
0
  return 128;
238
0
}
239
240
#undef die_message_errno
241
int die_message_errno(const char *fmt, ...)
242
0
{
243
0
  char buf[1024];
244
0
  va_list params;
245
246
0
  va_start(params, fmt);
247
0
  die_message_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
248
0
  va_end(params);
249
0
  return 128;
250
0
}
251
252
#undef error_errno
253
int error_errno(const char *fmt, ...)
254
0
{
255
0
  char buf[1024];
256
0
  va_list params;
257
258
0
  va_start(params, fmt);
259
0
  error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
260
0
  va_end(params);
261
0
  return -1;
262
0
}
263
264
#undef error
265
int error(const char *err, ...)
266
0
{
267
0
  va_list params;
268
269
0
  va_start(params, err);
270
0
  error_routine(err, params);
271
0
  va_end(params);
272
0
  return -1;
273
0
}
274
275
void warning_errno(const char *warn, ...)
276
0
{
277
0
  char buf[1024];
278
0
  va_list params;
279
280
0
  va_start(params, warn);
281
0
  warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
282
0
  va_end(params);
283
0
}
284
285
void warning(const char *warn, ...)
286
0
{
287
0
  va_list params;
288
289
0
  va_start(params, warn);
290
0
  warn_routine(warn, params);
291
0
  va_end(params);
292
0
}
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
0
{
300
0
  char prefix[256];
301
302
  /* truncation via snprintf is OK here */
303
0
  snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
304
305
0
  vreportf(prefix, fmt, params);
306
0
}
307
308
static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
309
0
{
310
0
  va_list params_copy;
311
0
  static int in_bug;
312
313
0
  va_copy(params_copy, params);
314
0
  BUG_vfl_common(file, line, fmt, params);
315
316
0
  if (in_bug)
317
0
    abort();
318
0
  in_bug = 1;
319
320
0
  trace2_cmd_error_va(fmt, params_copy);
321
322
0
  if (BUG_exit_code)
323
0
    exit(BUG_exit_code);
324
0
  abort();
325
0
}
326
327
NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
328
0
{
329
0
  va_list ap;
330
331
0
  bug_called_must_BUG = 0;
332
333
0
  va_start(ap, fmt);
334
0
  BUG_vfl(file, line, fmt, ap);
335
0
  va_end(ap);
336
0
}
337
338
int bug_called_must_BUG;
339
void bug_fl(const char *file, int line, const char *fmt, ...)
340
0
{
341
0
  va_list ap;
342
343
0
  bug_called_must_BUG = 1;
344
345
0
  va_start(ap, fmt);
346
0
  BUG_vfl_common(file, line, fmt, ap);
347
0
  va_end(ap);
348
349
0
  va_start(ap, fmt);
350
0
  trace2_cmd_error_va(fmt, ap);
351
0
  va_end(ap);
352
0
}
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