On 2024-11-19 07:10, Pádraig Brady wrote:
I've not got access to a macos system to test currently,
but the attached should address this.
That patch addresses only the empty string, not other strings that
cannot be converted (e.g., a string consisting of a single space). This
turns out to be a longstanding bug in printf. I installed the attached
fancier patch which should address the more-general problem, and which
adds some test cases. I tested this on cfarm104.cfarm.net which is
running macOS 12.6.From 91e95f1f86977a66ac0d6165f6db554eff174128 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Nov 2024 18:19:54 -0800
Subject: [PATCH] printf: diagnose empty args correctly
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Also, port better to macOS.
* src/printf.c (verify_numeric): Don’t assume that when s == end
then errno is zero; it is EINVAL on macOS, and POSIX allows this.
(print_direc): Treat missing arg as zero for numeric conversions,
and as an empty string for the others.
(print_formatted): Use null pointer, not an empty string,
to represent missing arg.
* tests/printf/printf.sh: Test empty and space widths and precisions.
---
src/printf.c | 33 ++++++++++++++++++++-------------
tests/printf/printf.sh | 9 +++++++++
2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/src/printf.c b/src/printf.c
index 488e8e006..d7730ae6c 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -104,17 +104,19 @@ ARGUMENTs converted to proper type first. Variable widths are handled.\n\
static void
verify_numeric (char const *s, char const *end)
{
- if (errno)
+ if (s == end)
+ {
+ error (0, 0, _("%s: expected a numeric value"), quote (s));
+ exit_status = EXIT_FAILURE;
+ }
+ else if (errno)
{
error (0, errno, "%s", quote (s));
exit_status = EXIT_FAILURE;
}
else if (*end)
{
- if (s == end)
- error (0, 0, _("%s: expected a numeric value"), quote (s));
- else
- error (0, 0, _("%s: value not completely converted"), quote (s));
+ error (0, 0, _("%s: value not completely converted"), quote (s));
exit_status = EXIT_FAILURE;
}
}
@@ -344,7 +346,7 @@ print_direc (char const *start, char conversion,
case 'd':
case 'i':
{
- intmax_t arg = vstrtoimax (argument);
+ intmax_t arg = argument ? vstrtoimax (argument) : 0;
if (!have_field_width)
{
if (!have_precision)
@@ -367,7 +369,7 @@ print_direc (char const *start, char conversion,
case 'x':
case 'X':
{
- uintmax_t arg = vstrtoumax (argument);
+ uintmax_t arg = argument ? vstrtoumax (argument) : 0;
if (!have_field_width)
{
if (!have_precision)
@@ -394,7 +396,7 @@ print_direc (char const *start, char conversion,
case 'g':
case 'G':
{
- long double arg = vstrtold (argument);
+ long double arg = argument ? vstrtold (argument) : 0;
if (!have_field_width)
{
if (!have_precision)
@@ -413,13 +415,18 @@ print_direc (char const *start, char conversion,
break;
case 'c':
- if (!have_field_width)
- xprintf (p, *argument);
- else
- xprintf (p, field_width, *argument);
+ {
+ char c = argument ? *argument : '\0';
+ if (!have_field_width)
+ xprintf (p, c);
+ else
+ xprintf (p, field_width, c);
+ }
break;
case 's':
+ if (!argument)
+ argument = "";
if (!have_field_width)
{
if (!have_precision)
@@ -662,7 +669,7 @@ print_formatted (char const *format, int argc, char **argv)
print_direc (direc, *ac.f,
have_field_width, field_width,
have_precision, precision,
- argc <= ac.curr_arg ? "" : argv[ac.curr_arg]);
+ ac.curr_arg < argc ? argv[ac.curr_arg] : nullptr);
break;
diff --git a/tests/printf/printf.sh b/tests/printf/printf.sh
index 7e489161e..a411b9d9f 100755
--- a/tests/printf/printf.sh
+++ b/tests/printf/printf.sh
@@ -73,6 +73,11 @@ returns_ 1 $prog '%.*dx\n' $INT_OFLOW 0 >>out 2> /dev/null || fail=1
$prog '11 %*c\n' 2 x >>out || fail=1
+returns_ 1 $prog '12 %*s\n' '' 'empty width' >>out 2>/dev/null || fail=1
+returns_ 1 $prog '13 %*s\n' ' ' 'space width' >>out 2>/dev/null || fail=1
+returns_ 1 $prog '14 %.*sx\n' '' 'empty precision' >>out 2>/dev/null || fail=1
+returns_ 1 $prog '15 %.*sx\n' ' ' 'space precision' >>out 2>/dev/null || fail=1
+
returns_ 1 $prog '%#d\n' 0 >>out 2> /dev/null || fail=1
returns_ 1 $prog '%0s\n' 0 >>out 2> /dev/null || fail=1
@@ -93,6 +98,10 @@ cat <<\EOF > exp
9 0 x
10 0x
11 x
+12 empty width
+13 space width
+14 x
+15 x
EOF
compare exp out || fail=1
--
2.43.0