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