Skip to content

Commit

Permalink
vreportf(): avoid relying on stdio buffering
Browse files Browse the repository at this point in the history
The MSVC runtime behavior differs from glibc's with respect to
`fprintf(stderr, ...)` in that the former writes out the message
character by character.

In t5516, this leads to a funny problem where a `git fetch` process as
well as the `git upload-pack` process spawned by it _both_ call `die()`
at the same time. The output can look like this:

	fatal: git uploadfata-lp: raemcokte :error:  upload-pnot our arcef k6: n4ot our ea4cr1e3f 36d45ea94fca1398e86a771eda009872d63adb28598f6a9
	8e86a771eda009872d6ab2886

Let's avoid this predicament altogether by rendering the entire message,
including the prefix and the trailing newline, into the buffer we
already have (and which is still fixed size) and then write it out via
`xwrite()`.

We still clip the message to at most 4095 characters.

The history of `vreportf()` with regard to this issue includes the
following commits:

d048a96 (2007-11-09) - 'char msg[256]' is introduced to avoid interleaving
389d176 (2009-03-25) - Buffer size increased to 1024 to avoid truncation
625a860 (2009-11-22) - Buffer size increased to 4096 to avoid truncation
f4c3edc (2015-08-11) - Buffer removed to avoid truncation
b5a9e43 (2017-01-11) - Reverts f4c3edc to be able to replace control
                        chars before sending to stderr
9ac13ec (2006-10-11) - Another attempt to solve interleaving.
                        This is seemingly related to d048a96.
137a0d0 (2007-11-19) - Addresses out-of-order for display()
34df8ab (2009-03-10) - Switches xwrite() to fprintf() in recv_sideband()
                        to support UTF-8 emulation
eac14f8 (2012-01-14) - Removes the need for fprintf() for UTF-8 emulation,
                        so it's safe to use xwrite() again
5e5be9e (2016-06-28) - recv_sideband() uses xwrite() again

Note that we print nothing if the `vsnprintf()` call failed to render
the error message; There is little we can do in that case, and it should
not happen anyway.

Also please note that we `fflush(stderr)` here to help when running in a
Git Bash on Windows: in this case, `stderr` is not actually truly
unbuffered, and needs the extra help.

Helped-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed Oct 29, 2019
1 parent 566a143 commit e426627
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions usage.c
Expand Up @@ -9,14 +9,21 @@
void vreportf(const char *prefix, const char *err, va_list params)
{
char msg[4096];
char *p;
size_t off = strlcpy(msg, prefix, sizeof(msg));
char *p, *pend = msg + sizeof(msg);

vsnprintf(msg, sizeof(msg), err, params);
for (p = msg; *p; p++) {
p = msg + off < pend ? msg + off : pend - 1;
if (vsnprintf(p, pend - p, err, params) < 0)
return; /* vsnprintf() failed, there is nothing we can do */

for (; p != pend - 1 && *p; p++) {
if (iscntrl(*p) && *p != '\t' && *p != '\n')
*p = '?';
}
fprintf(stderr, "%s%s\n", prefix, msg);

*(p++) = '\n'; /* we no longer need a NUL */
fflush(stderr);
xwrite(2, msg, p - msg);
}

static NORETURN void usage_builtin(const char *err, va_list params)
Expand Down

0 comments on commit e426627

Please sign in to comment.