summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@CS.UCLA.EDU>2007-11-03 01:10:59 -0700
committerJim Meyering <meyering@redhat.com>2007-11-03 10:00:26 +0100
commita860ca32b0cadfff722f7639b2158a59be7bd346 (patch)
treeca1b1691377feb8351fe71dcf18b097b82645673
parent3e3f8f0161645a7ca3730f1e8307ab7011b8c6f8 (diff)
downloadcoreutils-a860ca32b0cadfff722f7639b2158a59be7bd346.tar.xz
Fix bug with "seq 10.8 0.1 10.95", plus another bug with %% in format.
* NEWS: Mention the %%-in-format bug fix. * src/seq.c (struct layout): New type. (long_double_format): New arg LAYOUT. Fill it in. Fix mishandling of %% in formats. (print_numbers): New arg LAYOUT. Don't convert LAST to output format when deciding whether to go slightly past LAST. Instead, convert X to output format and back. This fixes a bug reported by Andreas Schwab in <http://lists.gnu.org/archive/html/bug-coreutils/2007-10/msg00237.html> where "seq 10.8 0.1 10.95" would output 11.0 on platforms where 10.95 rounds to a value that prints as 11.0 when only one digit past the decimal point is asked for. (main): Compute layout, for benefit of print_numbers. * tests/misc/seq (float-3): Undo previous change, since the bug should be fixed now. (fmt-b): New test, for the %% bug.
-rw-r--r--ChangeLog21
-rw-r--r--NEWS3
-rw-r--r--src/seq.c64
-rwxr-xr-xtests/misc/seq3
4 files changed, 68 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index 052e13c16..ed47c2446 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2007-11-03 Paul Eggert <eggert@cs.ucla.edu>
+
+ Fix bug with "seq 10.8 0.1 10.95", plus another bug with %% in format.
+
+ * NEWS: Mention the %%-in-format bug fix.
+ * src/seq.c (struct layout): New type.
+ (long_double_format): New arg LAYOUT. Fill it in. Fix mishandling
+ of %% in formats.
+ (print_numbers): New arg LAYOUT. Don't convert LAST to output format
+ when deciding whether to go slightly past LAST. Instead, convert
+ X to output format and back. This fixes a bug reported by
+ Andreas Schwab in
+ <http://lists.gnu.org/archive/html/bug-coreutils/2007-10/msg00237.html>
+ where "seq 10.8 0.1 10.95" would output 11.0 on platforms where
+ 10.95 rounds to a value that prints as 11.0 when only one digit
+ past the decimal point is asked for.
+ (main): Compute layout, for benefit of print_numbers.
+ * tests/misc/seq (float-3): Undo previous change, since the bug
+ should be fixed now.
+ (fmt-b): New test, for the %% bug.
+
2007-11-01 Jim Meyering <meyering@redhat.com>
* tests/misc/printf-surprise: Correct sed transform.
diff --git a/NEWS b/NEWS
index 0ccfc49fa..a8434c54c 100644
--- a/NEWS
+++ b/NEWS
@@ -139,6 +139,9 @@ GNU coreutils NEWS -*- outline -*-
seq no longer mishandles obvious cases like "seq 0 0.000001 0.000003",
so workarounds like "seq 0 0.000001 0.0000031" are no longer needed.
+ seq would mistakenly reject some valid format strings containing %%,
+ and would mistakenly accept some invalid ones. e.g., %g%% and %%g, resp.
+
Obsolete sort usage with an invalid ordering-option character, e.g.,
"env _POSIX2_VERSION=199209 sort +1x" no longer makes sort free an
invalid pointer [introduced in coreutils-6.5]
diff --git a/src/seq.c b/src/seq.c
index d9420abd1..d7d2521b1 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -120,6 +120,14 @@ struct operand
};
typedef struct operand operand;
+/* Description of what a number-generating format will generate. */
+struct layout
+{
+ /* Number of bytes before and after the number. */
+ size_t prefix_len;
+ size_t suffix_len;
+};
+
/* Read a long double value from the command line.
Return if the string is correct else signal error. */
@@ -171,17 +179,22 @@ scan_arg (const char *arg)
/* If FORMAT is a valid printf format for a double argument, return
its long double equivalent, possibly allocated from dynamic
- storage; otherwise, return NULL. */
+ storage, and store into *LAYOUT a description of the output layout;
+ otherwise, return NULL. */
static char const *
-long_double_format (char const *fmt)
+long_double_format (char const *fmt, struct layout *layout)
{
size_t i;
- size_t prefix_len;
+ size_t prefix_len = 0;
+ size_t suffix_len = 0;
+ size_t length_modifier_offset;
bool has_L;
- for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
- if (! fmt[i])
+ for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
+ if (fmt[i])
+ prefix_len++;
+ else
return NULL;
i++;
@@ -193,20 +206,25 @@ long_double_format (char const *fmt)
i += strspn (fmt + i, "0123456789");
}
- prefix_len = i;
+ length_modifier_offset = i;
has_L = (fmt[i] == 'L');
i += has_L;
if (! strchr ("efgaEFGA", fmt[i]))
return NULL;
- for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
- if (! fmt[i])
+ for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
+ if (fmt[i])
+ suffix_len++;
+ else
{
size_t format_size = i + 1;
char *ldfmt = xmalloc (format_size + 1);
- memcpy (ldfmt, fmt, prefix_len);
- ldfmt[prefix_len] = 'L';
- strcpy (ldfmt + prefix_len + 1, fmt + prefix_len + has_L);
+ memcpy (ldfmt, fmt, length_modifier_offset);
+ ldfmt[length_modifier_offset] = 'L';
+ strcpy (ldfmt + length_modifier_offset + 1,
+ fmt + length_modifier_offset + has_L);
+ layout->prefix_len = prefix_len;
+ layout->suffix_len = suffix_len;
return ldfmt;
}
@@ -217,7 +235,7 @@ long_double_format (char const *fmt)
given or default stepping and format. */
static void
-print_numbers (char const *fmt,
+print_numbers (char const *fmt, struct layout layout,
long double first, long double step, long double last)
{
long double i;
@@ -229,8 +247,8 @@ print_numbers (char const *fmt,
if (step < 0 ? x < last : last < x)
{
- /* If we go one past the end, but that number prints the
- same way "last" does, and prints differently from the
+ /* If we go one past the end, but that number prints as a
+ value equal to "last", and prints differently from the
previous number, then print "last". This avoids problems
with rounding. For example, with the x86 it causes "seq
0 0.000001 0.000003" to print 0.000003 instead of
@@ -238,13 +256,15 @@ print_numbers (char const *fmt,
if (i)
{
- char *x_str = NULL;
- char *last_str = NULL;
- if (asprintf (&x_str, fmt, x) < 0
- || asprintf (&last_str, fmt, last) < 0)
+ long double x_val;
+ char *x_str;
+ int x_strlen = asprintf (&x_str, fmt, x);
+ if (x_strlen < 0)
xalloc_die ();
+ x_str[x_strlen - layout.suffix_len] = '\0';
- if (STREQ (x_str, last_str))
+ if (xstrtold (x_str + layout.prefix_len, NULL, &x_val, c_strtold)
+ && x_val == last)
{
char *x0_str = NULL;
if (asprintf (&x0_str, fmt, x0) < 0)
@@ -258,7 +278,6 @@ print_numbers (char const *fmt,
}
free (x_str);
- free (last_str);
}
break;
@@ -317,6 +336,7 @@ main (int argc, char **argv)
operand first = { 1, 1, 0 };
operand step = { 1, 1, 0 };
operand last;
+ struct layout layout = { 0, 0 };
/* The printf(3) format used for output. */
char const *format_str = NULL;
@@ -385,7 +405,7 @@ main (int argc, char **argv)
if (format_str)
{
- char const *f = long_double_format (format_str);
+ char const *f = long_double_format (format_str, &layout);
if (! f)
{
error (0, 0, _("invalid format string: %s"), quote (format_str));
@@ -418,7 +438,7 @@ format string may not be specified when printing equal width strings"));
if (format_str == NULL)
format_str = get_default_format (first, step, last);
- print_numbers (format_str, first.value, step.value, last.value);
+ print_numbers (format_str, layout, first.value, step.value, last.value);
exit (EXIT_SUCCESS);
}
diff --git a/tests/misc/seq b/tests/misc/seq
index 1f3ec06c1..73f41336d 100755
--- a/tests/misc/seq
+++ b/tests/misc/seq
@@ -47,7 +47,7 @@ my @Tests =
['float-1', qw(0.8 0.1 0.9), {OUT => [qw(0.8 0.9)]}],
['float-2', qw(0.1 0.99 1.99), {OUT => [qw(0.10 1.09)]}],
- ['float-3', qw(10.8 0.1 10.94), {OUT => [qw(10.8 10.9)]}],
+ ['float-3', qw(10.8 0.1 10.95), {OUT => [qw(10.8 10.9)]}],
['float-4', qw(0.1 -0.1 -0.2), {OUT => [qw(0.1 0.0 -0.1 -0.2)]},
{OUT_SUBST => 's,^-0\.0$,0.0,'},
],
@@ -79,6 +79,7 @@ my @Tests =
['fmt-8', qw(-f %0+.0f 1 2), {OUT => [qw(+1 +2)]}],
['fmt-9', '-f "% -3.0f"', qw(-1 0), {OUT => ['-1 ', ' 0 ']}],
['fmt-a', '-f "% -.0f"',qw(-1 0), {OUT => ['-1', ' 0']}],
+ ['fmt-b', qw(-f %%%g%% 1), {OUT => ['%1%']}],
);
# Append a newline to each entry in the OUT array.