Re: [PATCH v3 1/1] vreportf(): avoid relying on stdio buffering
From: Johannes Schindelin <hidden>
Date: 2019-10-30 09:13:53
Hi Junio, On Wed, 30 Oct 2019, Junio C Hamano wrote:
"Johannes Schindelin via GitGitGadget" [off-list ref] writes:quoted
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.I do not think you have to single out Windows in this case; if we are writing directly to file descriptor #2, as long as there have been something written by the process to the standard error stream before our caller called us, we must flush it to make sure what we are going to write out will come after, no matter what platform we are on. The process may have written to the standard error stream and there may be something left in the buffer kept in the stdio layer. Call fflush(stderr) before writing the message we prepare in this function. or something along that line would be sufficient.
I replaced the paragraph with a slightly edited version of this. Thanks.
quoted
diff --git a/usage.c b/usage.c index 2fdb20086b..471efb2de9 100644 --- a/usage.c +++ b/usage.c@@ -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 = off < pend - msg ? msg + off : pend - 1; + if (vsnprintf(p, pend - p, err, params) < 0) + *p = '\0'; /* vsnprintf() failed, clip at prefix */ + + for (; p != pend - 1 && *p; p++) {It may make the result much simpler to start with something like: size_t prefix_len = strlen(prefix); if (sizeof(msg) <= prefix_len) { fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix); abort(); } memcpy(msg, prefix, prefix_len); p = msg + prefix_len; as we agreed that we won't have prefix that will fill or overflow msg[] based on your earlier reading of the callers like BUG-fl.
Makes sense. I was a bit blinded by my endeavor to keep the diffstat small.
That way, we probably may be able to even lose the pend pointer and arithmetic around it.
I tried this, and it still looks more obvious to me with `pend`.
quoted
if (iscntrl(*p) && *p != '\t' && *p != '\n') *p = '?'; }Thanks. With this flow it is crystal clear that the prefix is shown as-is, while the payload is sanitized.
Yes ;-) Thanks, Dscho
quoted
- fprintf(stderr, "%s%s\n", prefix, msg); + + *(p++) = '\n'; /* we no longer need a NUL */ + fflush(stderr); + write_in_full(2, msg, p - msg); } static NORETURN void usage_builtin(const char *err, va_list params)