diff options
| author | David Laight <david.laight.linux@gmail.com> | 2026-03-23 11:22:47 +0000 |
|---|---|---|
| committer | Thomas Weißschuh <linux@weissschuh.net> | 2026-04-01 18:02:37 +0200 |
| commit | 1dff9ac2c85860af67a74777fa6d31c2ad07a15a (patch) | |
| tree | 1001788e66cf14e2255e0aee59f188d2dd8c9484 /tools/include | |
| parent | 6285f0881ec68034399d13552f7243e69e6e37bf (diff) | |
| download | lwn-1dff9ac2c85860af67a74777fa6d31c2ad07a15a.tar.gz lwn-1dff9ac2c85860af67a74777fa6d31c2ad07a15a.zip | |
tools/nolibc/printf: Support negative variable width and precision
For (eg) "%*.*s" treat a negative field width as a request to left align
the output (the same as the '-' flag), and a negative precision to
request the default precision.
Set the default precision to -1 (not INT_MAX) and add explicit checks
to the string handling for negative values (makes the tet unsigned).
For numeric output check for 'precision >= 0' instead of testing
_NOLIBC_PF_FLAGS_CONTAIN(flags, '.').
This needs an inverted test, some extra goto and removes an indentation.
The changed conditionals fix printf("%0-#o", 0) - but '0' and '-' shouldn't
both be specified.
Signed-off-by: David Laight <david.laight.linux@gmail.com>
Link: https://patch.msgid.link/20260323112247.3196-1-david.laight.linux@gmail.com
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Diffstat (limited to 'tools/include')
| -rw-r--r-- | tools/include/nolibc/stdio.h | 68 |
1 files changed, 38 insertions, 30 deletions
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 8f7e1948a651..b6d14a58cfe7 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -347,6 +347,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list char *out; const char *outstr; unsigned int sign_prefix; + int got_width; written = 0; while (1) { @@ -377,23 +378,28 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list } /* Width and precision */ - for (;; ch = *fmt++) { + for (got_width = 0;; ch = *fmt++) { if (ch == '*') { - precision = va_arg(args, unsigned int); + precision = va_arg(args, int); ch = *fmt++; } else { for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++) precision = precision * 10 + (ch - '0'); } - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) + if (got_width) break; width = precision; if (ch != '.') { /* Default precision for strings */ - precision = INT_MAX; + precision = -1; break; } - flags |= _NOLIBC_PF_FLAG('.'); + got_width = 1; + } + /* A negative width (e.g. from "%*s") requests left justify. */ + if (width < 0) { + width = -width; + flags |= _NOLIBC_PF_FLAG('-'); } /* Length modifier. @@ -457,7 +463,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list if (!outstr) { outstr = "(null)"; /* Match glibc, nothing output if precision too small */ - len = precision >= 6 ? 6 : 0; + len = precision < 0 || precision >= 6 ? 6 : 0; goto do_output; } goto do_strlen_output; @@ -533,32 +539,34 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list } /* Add zero padding */ - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '0', '.')) { - if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) { - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) - /* Left justify overrides zero pad */ - goto prepend_sign; - /* eg "%05d", Zero pad to field width less sign. - * Note that precision can end up negative so all - * the variables have to be 'signed int'. - */ - precision = width; - if (sign_prefix) { + if (precision < 0) { + /* No explicit precision (or negative from "%.*s"). */ + if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '0')) + goto no_zero_padding; + if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) + /* Left justify overrides zero pad */ + goto no_zero_padding; + /* eg "%05d", Zero pad to field width less sign. + * Note that precision can end up negative so all + * the variables have to be 'signed int'. + */ + precision = width; + if (sign_prefix) { + precision--; + if (sign_prefix >= 256) precision--; - if (sign_prefix >= 256) - precision--; - } - } - if (precision > 31) - /* Don't run off the start of outbuf[], arbitrary limit - * longer than the longest number field. */ - precision = 31; - for (; len < precision; len++) { - /* Stop gcc generating horrid code and memset(). */ - _NOLIBC_OPTIMIZER_HIDE_VAR(len); - *--out = '0'; } } + if (precision > 31) + /* Don't run off the start of outbuf[], arbitrary limit + * longer than the longest number field. */ + precision = 31; + for (; len < precision; len++) { + /* Stop gcc generating horrid code and memset(). */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + *--out = '0'; + } +no_zero_padding: /* %#o has set sign_prefix to '0', but we don't want so add an extra * leading zero here. @@ -603,7 +611,7 @@ prepend_sign: do_strlen_output: /* Open coded strnlen() (slightly smaller). */ - for (len = 0; len < precision; len++) + for (len = 0; precision < 0 || len < precision; len++) if (!outstr[len]) break; |
