Git fork
at reftables-rust 408 lines 8.8 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#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}