f290089879
Our error() and die() calls may report messages with arbitrary data (e.g., filenames or even data from a remote server). Let's make it harder to cause confusion with mischievous filenames. E.g., try: git rev-parse "$(printf "\rfatal: this argument is too sneaky")" -- or git rev-parse "$(printf "\x1b[5mblinky\x1b[0m")" -- Let's block all ASCII control characters, with the exception of TAB and LF. We use both in our own messages (and we are necessarily sanitizing the complete output of snprintf here, as we do not have access to the individual varargs). And TAB and LF are unlikely to cause confusion (you could put "\nfatal: sneaky\n" in your filename, but it would at least not _cover up_ the message leading to it, unlike "\r"). We'll replace the characters with a "?", which is similar to how "ls" behaves. It might be nice to do something less lossy, like converting them to "\x" hex codes. But replacing with a single character makes it easy to do in-place and without worrying about length limitations. This feature should kick in rarely enough that the "?" marks are almost never seen. We'll leave high-bit characters as-is, as they are likely to be UTF-8 (though there may be some Unicode mischief you could cause, which may require further patches). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
162 lines
3.3 KiB
C
162 lines
3.3 KiB
C
/*
|
|
* GIT - The information manager from hell
|
|
*
|
|
* Copyright (C) Linus Torvalds, 2005
|
|
*/
|
|
#include "git-compat-util.h"
|
|
#include "cache.h"
|
|
|
|
static FILE *error_handle;
|
|
|
|
void vreportf(const char *prefix, const char *err, va_list params)
|
|
{
|
|
char msg[4096];
|
|
FILE *fh = error_handle ? error_handle : stderr;
|
|
char *p;
|
|
|
|
vsnprintf(msg, sizeof(msg), err, params);
|
|
for (p = msg; *p; p++) {
|
|
if (iscntrl(*p) && *p != '\t' && *p != '\n')
|
|
*p = '?';
|
|
}
|
|
fprintf(fh, "%s%s\n", prefix, msg);
|
|
}
|
|
|
|
static NORETURN void usage_builtin(const char *err, va_list params)
|
|
{
|
|
vreportf("usage: ", err, params);
|
|
exit(129);
|
|
}
|
|
|
|
static NORETURN void die_builtin(const char *err, va_list params)
|
|
{
|
|
vreportf("fatal: ", err, params);
|
|
exit(128);
|
|
}
|
|
|
|
static void error_builtin(const char *err, va_list params)
|
|
{
|
|
vreportf("error: ", err, params);
|
|
}
|
|
|
|
static void warn_builtin(const char *warn, va_list params)
|
|
{
|
|
vreportf("warning: ", warn, params);
|
|
}
|
|
|
|
static int die_is_recursing_builtin(void)
|
|
{
|
|
static int dying;
|
|
return dying++;
|
|
}
|
|
|
|
/* If we are in a dlopen()ed .so write to a global variable would segfault
|
|
* (ugh), so keep things static. */
|
|
static NORETURN_PTR void (*usage_routine)(const char *err, va_list params) = usage_builtin;
|
|
static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
|
|
static void (*error_routine)(const char *err, va_list params) = error_builtin;
|
|
static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
|
|
static int (*die_is_recursing)(void) = die_is_recursing_builtin;
|
|
|
|
void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params))
|
|
{
|
|
die_routine = routine;
|
|
}
|
|
|
|
void set_error_routine(void (*routine)(const char *err, va_list params))
|
|
{
|
|
error_routine = routine;
|
|
}
|
|
|
|
void set_die_is_recursing_routine(int (*routine)(void))
|
|
{
|
|
die_is_recursing = routine;
|
|
}
|
|
|
|
void set_error_handle(FILE *fh)
|
|
{
|
|
error_handle = fh;
|
|
}
|
|
|
|
void NORETURN usagef(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
usage_routine(err, params);
|
|
va_end(params);
|
|
}
|
|
|
|
void NORETURN usage(const char *err)
|
|
{
|
|
usagef("%s", err);
|
|
}
|
|
|
|
void NORETURN die(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
if (die_is_recursing()) {
|
|
fputs("fatal: recursion detected in die handler\n", stderr);
|
|
exit(128);
|
|
}
|
|
|
|
va_start(params, err);
|
|
die_routine(err, params);
|
|
va_end(params);
|
|
}
|
|
|
|
void NORETURN die_errno(const char *fmt, ...)
|
|
{
|
|
va_list params;
|
|
char fmt_with_err[1024];
|
|
char str_error[256], *err;
|
|
int i, j;
|
|
|
|
if (die_is_recursing()) {
|
|
fputs("fatal: recursion detected in die_errno handler\n",
|
|
stderr);
|
|
exit(128);
|
|
}
|
|
|
|
err = strerror(errno);
|
|
for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
|
|
if ((str_error[j++] = err[i++]) != '%')
|
|
continue;
|
|
if (j < sizeof(str_error) - 1) {
|
|
str_error[j++] = '%';
|
|
} else {
|
|
/* No room to double the '%', so we overwrite it with
|
|
* '\0' below */
|
|
j--;
|
|
break;
|
|
}
|
|
}
|
|
str_error[j] = 0;
|
|
snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
|
|
|
|
va_start(params, fmt);
|
|
die_routine(fmt_with_err, params);
|
|
va_end(params);
|
|
}
|
|
|
|
#undef error
|
|
int error(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
error_routine(err, params);
|
|
va_end(params);
|
|
return -1;
|
|
}
|
|
|
|
void warning(const char *warn, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, warn);
|
|
warn_routine(warn, params);
|
|
va_end(params);
|
|
}
|