1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-03-19 03:13:05 +02:00

Don't include <math.h> or <xstrtol.h>; no longer needed.

(isfinite) [!defined isfinite]: New macro.
(separator, terminator): Now points to const.
(first, step, last): Remove.
(usage): Update to match new behavior.
(struct operand, operand): New type.
(scan_arg): Renamed from scan_double_arg, since we no longer use double.
All uses changed.
Compute and return a value of type operand, not double.
(long_double_format): Renamed from valid_format, and now returns a
new format with an "L" added if needed, if the original format was
valid.  Allow %a, %A, %E, %F, and %G formats.
(print_numbers): Take numeric values as args rather than from globals.
Print long double, not double.
(get_width_format): Remove.
(get_default_format): New function.
(main): Implement new way of calculating default format.
Don't worry about locale's representation of the decimal point, since
the arguments are always processed in the C locale.
This commit is contained in:
Paul Eggert
2006-07-01 00:07:06 +00:00
parent 75a229e3a3
commit 6c5f11fbfe

330
src/seq.c
View File

@@ -1,5 +1,5 @@
/* seq - print sequence of numbers to standard output.
Copyright (C) 1994-2005 Free Software Foundation, Inc.
Copyright (C) 1994-2006 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include <config.h>
#include <getopt.h>
#include <math.h>
#include <stdio.h>
#include <sys/types.h>
@@ -27,9 +26,14 @@
#include "c-strtod.h"
#include "error.h"
#include "quote.h"
#include "xstrtol.h"
#include "xstrtod.h"
/* Roll our own isfinite rather than using <math.h>, so that we don't
have to worry about linking -lm just for isfinite. */
#ifndef isfinite
# define isfinite(x) ((x) * 0 == 0)
#endif
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "seq"
@@ -42,24 +46,12 @@ static bool equal_width;
char *program_name;
/* The string used to separate two numbers. */
static char *separator;
static char const *separator;
/* The string output after all numbers have been output.
Usually "\n" or "\0". */
/* FIXME: make this an option. */
static char *terminator = "\n";
/* The representation of the decimal point in the current locale. */
static char decimal_point;
/* The starting number. */
static double first;
/* The increment. */
static double step = 1.0;
/* The last number. */
static double last;
static char const terminator[] = "\n";
static struct option const long_options[] =
{
@@ -87,7 +79,7 @@ Usage: %s [OPTION]... LAST\n\
fputs (_("\
Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
\n\
-f, --format=FORMAT use printf style floating-point FORMAT (default: %g)\n\
-f, --format=FORMAT use printf style floating-point FORMAT\n\
-s, --separator=STRING use STRING to separate numbers (default: \\n)\n\
-w, --equal-width equalize width by padding with leading zeroes\n\
"), stdout);
@@ -100,92 +92,126 @@ omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.\n\
FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
INCREMENT is usually positive if FIRST is smaller than LAST, and\n\
INCREMENT is usually negative if FIRST is greater than LAST.\n\
When given, the FORMAT argument must contain exactly one of\n\
the printf-style, floating point output formats %e, %f, %g\n\
FORMAT must be suitable for printing one argument of type `double';\n\
it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point\n\
decimal numbers with maximum precision PREC, and to %g otherwise.\n\
"), stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
exit (status);
}
/* Read a double value from the command line.
/* A command-line operand. */
struct operand
{
/* Its value, converted to 'long double'. */
long double value;
/* Its print width, if it were printed out in a form similar to its
input form. An input like "-.1" is treated like "-0.1", and an
input like "1." is treated like "1", but otherwise widths are
left alone. */
size_t width;
/* Number of digits after the decimal point, or INT_MAX if the
number can't easily be expressed as a fixed-point number. */
int precision;
};
typedef struct operand operand;
/* Read a long double value from the command line.
Return if the string is correct else signal error. */
static double
scan_double_arg (const char *arg)
static operand
scan_arg (const char *arg)
{
double ret_val;
operand ret;
if (! xstrtod (arg, NULL, &ret_val, c_strtod))
if (! xstrtold (arg, NULL, &ret.value, c_strtold))
{
error (0, 0, _("invalid floating point argument: %s"), arg);
usage (EXIT_FAILURE);
}
return ret_val;
ret.width = strlen (arg);
ret.precision = INT_MAX;
if (! arg[strcspn (arg, "eExX")] && isfinite (ret.value))
{
char const *decimal_point = strchr (arg, '.');
if (! decimal_point)
ret.precision = 0;
else
{
size_t fraction_len = strlen (decimal_point + 1);
if (fraction_len <= INT_MAX)
ret.precision = fraction_len;
ret.width += (fraction_len == 0
? -1
: (decimal_point == arg
|| ! ISDIGIT (decimal_point[-1])));
}
}
return ret;
}
/* Return true if the format string is valid for a single `double'
argument. */
/* If FORMAT is a valid printf format for a double argument, return
its long double equivalent, possibly allocated from dynamic
storage; otherwise, return NULL. */
static bool
valid_format (const char *fmt)
static char const *
long_double_format (char const *fmt)
{
while (*fmt != '\0')
size_t i;
size_t prefix_len;
bool has_L;
for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
if (! fmt[i])
return NULL;
i++;
i += strspn (fmt + i, "-+#0 '");
i += strspn (fmt + i, "0123456789");
if (fmt[i] == '.')
{
if (*fmt == '%')
{
fmt++;
if (*fmt != '%')
break;
}
fmt++;
}
if (*fmt == '\0')
return false;
fmt += strspn (fmt, "-+#0 '");
if (ISDIGIT (*fmt) || *fmt == '.')
{
fmt += strspn (fmt, "0123456789");
if (*fmt == '.')
{
++fmt;
fmt += strspn (fmt, "0123456789");
}
i++;
i += strspn (fmt + i, "0123456789");
}
if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
return false;
prefix_len = i;
has_L = (fmt[i] == 'L');
i += has_L;
if (! strchr ("efgaEFGA", fmt[i]))
return NULL;
fmt++;
while (*fmt != '\0')
{
if (*fmt == '%')
{
fmt++;
if (*fmt != '%')
return false;
}
for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
if (! fmt[i])
{
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);
return ldfmt;
}
fmt++;
}
return true;
return NULL;
}
/* Actually print the sequence of numbers in the specified range, with the
given or default stepping and format. */
static void
print_numbers (const char *fmt)
print_numbers (char const *fmt,
long double first, long double step, long double last)
{
double i;
long double i;
for (i = 0; /* empty */; i++)
{
double x = first + i * step;
long double x = first + i * step;
if (step < 0 ? x < last : last < x)
break;
if (i)
@@ -197,106 +223,52 @@ print_numbers (const char *fmt)
fputs (terminator, stdout);
}
#if HAVE_RINT && HAVE_MODF && HAVE_FLOOR
/* Return a printf-style format string with which all selected numbers
will format to strings of the same width. */
static char *
get_width_format (void)
/* Return the default format given FIRST, STEP, and LAST. */
static char const *
get_default_format (operand first, operand step, operand last)
{
static char buffer[256];
int full_width;
int frac_width;
int width1, width2;
double max_val;
double min_val;
double temp;
static char format_buf[sizeof "%0.Lf" + 2 * INT_STRLEN_BOUND (int)];
if (first > last)
int prec = MAX (first.precision, step.precision);
if (prec != INT_MAX && last.precision != INT_MAX)
{
min_val = first - step * floor ((first - last) / step);
max_val = first;
}
else
{
min_val = first;
max_val = first + step * floor ((last - first) / step);
if (equal_width)
{
size_t first_width = first.width + (prec - first.precision);
size_t last_width = last.width + (prec - last.precision);
if (first.width <= first_width
&& (last.width < last_width) == (prec < last.precision))
{
size_t width = MAX (first_width, last_width);
if (width <= INT_MAX)
{
int w = width;
sprintf (format_buf, "%%0%d.%dLf", w, prec);
return format_buf;
}
}
}
else
{
sprintf (format_buf, "%%.%dLf", prec);
return format_buf;
}
}
sprintf (buffer, "%g", rint (max_val));
if (buffer[strspn (buffer, "-0123456789")] != '\0')
return "%g";
width1 = strlen (buffer);
if (min_val < 0.0)
{
double int_min_val = rint (min_val);
sprintf (buffer, "%g", int_min_val);
if (buffer[strspn (buffer, "-0123456789")] != '\0')
return "%g";
/* On some systems, `seq -w -.1 .1 .1' results in buffer being `-0'.
On others, it is just `0'. The former results in better output. */
width2 = (int_min_val == 0 ? 2 : strlen (buffer));
width1 = width1 > width2 ? width1 : width2;
}
full_width = width1;
sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
width1 = strlen (buffer);
if (width1 == 1)
width1 = 0;
else
{
if (buffer[0] != '1'
|| buffer[1] != decimal_point
|| buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
return "%g";
width1 -= 2;
}
sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
width2 = strlen (buffer);
if (width2 == 1)
width2 = 0;
else
{
if (buffer[0] != '1'
|| buffer[1] != decimal_point
|| buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
return "%g";
width2 -= 2;
}
frac_width = width1 > width2 ? width1 : width2;
if (frac_width)
sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
else
sprintf (buffer, "%%0%dg", full_width);
return buffer;
return "%Lg";
}
#else /* one of the math functions rint, modf, floor is missing. */
static char *
get_width_format (void)
{
/* We cannot compute the needed information to determine the correct
answer. So we simply return a value that works for all cases. */
return "%g";
}
#endif
int
main (int argc, char **argv)
{
int optc;
operand first = { 1, 1, 0 };
operand step = { 1, 1, 0 };
operand last;
/* The printf(3) format used for output. */
char *format_str = NULL;
char const *format_str = NULL;
initialize_main (&argc, &argv);
program_name = argv[0];
@@ -308,19 +280,6 @@ main (int argc, char **argv)
equal_width = false;
separator = "\n";
first = 1.0;
/* Get locale's representation of the decimal point. */
{
struct lconv const *locale = localeconv ();
/* If the locale doesn't define a decimal point, or if the decimal
point is multibyte, use the C locale's decimal point. FIXME:
add support for multibyte decimal points. */
decimal_point = locale->decimal_point[0];
if (! decimal_point || locale->decimal_point[1])
decimal_point = '.';
}
/* We have to handle negative numbers in the command line but this
conflicts with the command line arguments. So explicitly check first
@@ -328,8 +287,7 @@ main (int argc, char **argv)
while (optind < argc)
{
if (argv[optind][0] == '-'
&& ((optc = argv[optind][1]) == decimal_point
|| ISDIGIT (optc)))
&& ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
{
/* means negative number */
break;
@@ -374,23 +332,28 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
if (format_str && !valid_format (format_str))
if (format_str)
{
error (0, 0, _("invalid format string: %s"), quote (format_str));
usage (EXIT_FAILURE);
char const *f = long_double_format (format_str);
if (! f)
{
error (0, 0, _("invalid format string: %s"), quote (format_str));
usage (EXIT_FAILURE);
}
format_str = f;
}
last = scan_double_arg (argv[optind++]);
last = scan_arg (argv[optind++]);
if (optind < argc)
{
first = last;
last = scan_double_arg (argv[optind++]);
last = scan_arg (argv[optind++]);
if (optind < argc)
{
step = last;
last = scan_double_arg (argv[optind++]);
last = scan_arg (argv[optind++]);
}
}
@@ -402,14 +365,9 @@ format string may not be specified when printing equal width strings"));
}
if (format_str == NULL)
{
if (equal_width)
format_str = get_width_format ();
else
format_str = "%g";
}
format_str = get_default_format (first, step, last);
print_numbers (format_str);
print_numbers (format_str, first.value, step.value, last.value);
exit (EXIT_SUCCESS);
}