printf: Don't die on incomplete conversions

POSIX dictates here that incomplete conversions, like in

    printf %d\n 15.2

or

    printf %d 14g

are still printed along with any error.

This seems alright, as it allows users to silence stderr to accept incomplete conversions.

This commit implements it, but what's a bit weird is the ordering between stdout and stderr,
causing the error to be printed _after_, like

    15
    14
    15.1: value not completely converted
    14,2: value not completely converted

but that seems like a general issue with how we buffer the streams.

(I know that nonfatal_error is a copy of most of fatal_error - I tried
differently, and va_* is weird)

Fixes #5532.
This commit is contained in:
Fabian Homborg 2019-03-17 16:33:58 +01:00
parent aea4062906
commit 0bde698f81
5 changed files with 32 additions and 4 deletions

View file

@ -18,6 +18,7 @@
- `math` now accepts `--scale=max` for the maximum scale (#5579).
- `complete --do-complete` now also does fuzzy matches (#5467).
- `count` now also counts lines fed on stdin (#5744).
- `printf` prints what it can when input hasn't been fully converted to a number, but still prints an error (#5532).
### Interactive improvements
- Major improvements in performance and functionality to the 'sorin' sample prompt (#5411).

View file

@ -90,8 +90,10 @@ struct builtin_printf_state_t {
int print_formatted(const wchar_t *format, int argc, wchar_t **argv);
void nonfatal_error(const wchar_t *fmt, ...);
void fatal_error(const wchar_t *format, ...);
long print_esc(const wchar_t *escstart, bool octal_0);
void print_esc_string(const wchar_t *str);
void print_esc_char(wchar_t c);
@ -193,6 +195,22 @@ static int octal_to_bin(wchar_t c) {
}
}
void builtin_printf_state_t::nonfatal_error(const wchar_t *fmt, ...) {
// Don't error twice.
if (early_exit) return;
va_list va;
va_start(va, fmt);
wcstring errstr = vformat_string(fmt, va);
va_end(va);
streams.err.append(errstr);
if (!string_suffixes_string(L"\n", errstr)) streams.err.push_back(L'\n');
// We set the exit code to error, because one occured,
// but we don't do an early exit so we still print what we can.
this->exit_code = STATUS_CMD_ERROR;
}
void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...) {
// Don't error twice.
if (early_exit) return;
@ -207,7 +225,6 @@ void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...) {
this->exit_code = STATUS_CMD_ERROR;
this->early_exit = true;
}
void builtin_printf_state_t::append_output(wchar_t c) {
// Don't output if we're done.
if (early_exit) return;
@ -241,10 +258,12 @@ void builtin_printf_state_t::verify_numeric(const wchar_t *s, const wchar_t *end
this->fatal_error(L"%ls: %s", s, std::strerror(errcode));
}
} else if (*end) {
if (s == end)
if (s == end) {
this->fatal_error(_(L"%ls: expected a numeric value"), s);
else
this->fatal_error(_(L"%ls: value not completely converted"), s);
} else {
// This isn't entirely fatal - the value should still be printed.
this->nonfatal_error(_(L"%ls: value not completely converted"), s);
}
}
}

View file

@ -1,2 +1,3 @@
2,34: value not completely converted
0xABCDEF12345678901: Number out of range
15.1: value not completely converted

View file

@ -72,3 +72,7 @@ printf 'long hex4 %X\n' 0xABCDEF12345678901
printf 'long decimal %d\n' 498216206594
printf 'long signed %d\n' -498216206595
printf 'long signed to unsigned %u\n' -498216206596
# Verify numeric conversion still happens even if it couldn't be fully converted
printf '%d\n' 15.1
echo $status

View file

@ -16,6 +16,7 @@ a
0000000 376
0000001
1.230000e+00
2.000000e+00
3,450000e+00
4,560000e+00
long hex1 73ffffff9a
@ -24,3 +25,5 @@ long hex3 ABCDEF1234567890
long hex4 long decimal 498216206594
long signed -498216206595
long signed to unsigned 18446743575493345020
15
1