src/seq.c - coreutils-8.23
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include <config.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "c-strtod.h"
#include "error.h"
#include "quote.h"
#include "xstrtod.h"
#ifndef isfinite
# define isfinite(x) ((x) * 0 == 0)
#endif
#define PROGRAM_NAME "seq"
#define AUTHORS proper_name ("Ulrich Drepper")
static bool equal_width;
static char const *separator;
static char const terminator[] = "\n";
static struct option const long_options[] =
{
{ "equal-width", no_argument, NULL, 'w'},
{ "format", required_argument, NULL, 'f'},
{ "separator", required_argument, NULL, 's'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{ NULL, 0, NULL, 0}
};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... LAST\n\
or: %s [OPTION]... FIRST LAST\n\
or: %s [OPTION]... FIRST INCREMENT LAST\n\
"), program_name, program_name, program_name);
fputs (_("\
Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
"), stdout);
emit_mandatory_arg_note ();
fputs (_("\
-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);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
If FIRST or INCREMENT is omitted, it defaults to 1. That is, an\n\
omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.\n\
The sequence of numbers ends when the sum of the current number and\n\
INCREMENT would become greater than LAST.\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\
"), stdout);
fputs (_("\
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);
emit_ancillary_info ();
}
exit (status);
}
struct operand
{
long double value;
size_t width;
int precision;
};
typedef struct operand operand;
struct layout
{
size_t prefix_len;
size_t suffix_len;
};
static operand
scan_arg (const char *arg)
{
operand ret;
if (! xstrtold (arg, NULL, &ret.value, c_strtold))
{
error (0, 0, _("invalid floating point argument: %s"), arg);
usage (EXIT_FAILURE);
}
while (isspace (to_uchar (*arg)) || *arg == '+')
arg++;
ret.width = strlen (arg);
ret.precision = INT_MAX;
if (! arg[strcspn (arg, "xX")] && isfinite (ret.value))
{
char const *decimal_point = strchr (arg, '.');
if (! decimal_point)
ret.precision = 0;
else
{
size_t fraction_len = strcspn (decimal_point + 1, "eE");
if (fraction_len <= INT_MAX)
ret.precision = fraction_len;
ret.width += (fraction_len == 0 ? -1
: (decimal_point == arg || ! ISDIGIT (decimal_point[-1]))); }
char const *e = strchr (arg, 'e');
if (! e)
e = strchr (arg, 'E');
if (e)
{
long exponent = strtol (e + 1, NULL, 10);
ret.precision += exponent < 0 ? -exponent : 0;
ret.width -= strlen (arg) - (e - arg);
if (exponent < 0)
{
if (decimal_point)
{
if (e == decimal_point + 1) ret.width++;
}
else
ret.width++;
exponent = -exponent;
}
ret.width += exponent;
}
}
return ret;
}
static char const *
long_double_format (char const *fmt, struct layout *layout)
{
size_t i;
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 += (fmt[i] == '%') + 1)
{
if (!fmt[i])
error (EXIT_FAILURE, 0,
_("format %s has no %% directive"), quote (fmt));
prefix_len++;
}
i++;
i += strspn (fmt + i, "-+#0 '");
i += strspn (fmt + i, "0123456789");
if (fmt[i] == '.')
{
i++;
i += strspn (fmt + i, "0123456789");
}
length_modifier_offset = i;
has_L = (fmt[i] == 'L');
i += has_L;
if (fmt[i] == '\0')
error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
if (! strchr ("efgaEFGA", fmt[i]))
error (EXIT_FAILURE, 0,
_("format %s has unknown %%%c directive"), quote (fmt), fmt[i]);
for (i++; ; i += (fmt[i] == '%') + 1)
if (fmt[i] == '%' && fmt[i + 1] != '%')
error (EXIT_FAILURE, 0, _("format %s has too many %% directives"),
quote (fmt));
else if (fmt[i])
suffix_len++;
else
{
size_t format_size = i + 1;
char *ldfmt = xmalloc (format_size + 1);
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;
}
}
static void
print_numbers (char const *fmt, struct layout layout,
long double first, long double step, long double last)
{
bool out_of_range = (step < 0 ? first < last : last < first);
if (! out_of_range)
{
long double x = first;
long double i;
for (i = 1; ; i++)
{
long double x0 = x;
printf (fmt, x);
if (out_of_range)
break;
x = first + i * step;
out_of_range = (step < 0 ? x < last : last < x);
if (out_of_range)
{
bool print_extra_number = false;
long double x_val;
char *x_str;
int x_strlen;
setlocale (LC_NUMERIC, "C");
x_strlen = asprintf (&x_str, fmt, x);
setlocale (LC_NUMERIC, "");
if (x_strlen < 0)
xalloc_die ();
x_str[x_strlen - layout.suffix_len] = '\0';
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)
xalloc_die ();
print_extra_number = !STREQ (x0_str, x_str);
free (x0_str);
}
free (x_str);
if (! print_extra_number)
break;
}
fputs (separator, stdout);
}
fputs (terminator, stdout);
}
}
static char const *
get_default_format (operand first, operand step, operand last)
{
static char format_buf[sizeof "%0.Lf" + 2 * INT_STRLEN_BOUND (int)];
int prec = MAX (first.precision, step.precision);
if (prec != INT_MAX && last.precision != INT_MAX)
{
if (equal_width)
{
size_t first_width = first.width + (prec - first.precision);
size_t last_width = last.width + (prec - last.precision);
if (last.precision && prec == 0)
last_width--; if (last.precision == 0 && prec)
last_width++; if (first.precision == 0 && prec)
first_width++; 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;
}
}
return "%Lg";
}
static void
incr (char **s0, size_t *s_len)
{
char *s = *s0;
char *endp = s + *s_len - 1;
do
{
if ((*endp)++ < '9')
return;
*endp-- = '0';
}
while (endp >= s);
*--(*s0) = '1';
++*s_len;
}
static int
cmp (char const *a, size_t a_len, char const *b, size_t b_len)
{
if (a_len < b_len)
return -1;
if (b_len < a_len)
return 1;
return (strcmp (a, b));
}
static char const * _GL_ATTRIBUTE_PURE
trim_leading_zeros (char const *s)
{
char const *p = s;
while (*s == '0')
++s;
if (!*s && s != p)
--s;
return s;
}
static bool
seq_fast (char const *a, char const *b)
{
a = trim_leading_zeros (a);
b = trim_leading_zeros (b);
size_t p_len = strlen (a);
size_t q_len = strlen (b);
size_t n = MAX (p_len, q_len);
char *p0 = xmalloc (n + 1);
char *p = memcpy (p0 + n - p_len, a, p_len + 1);
char *q0 = xmalloc (n + 1);
char *q = memcpy (q0 + n - q_len, b, q_len + 1);
bool ok = cmp (p, p_len, q, q_len) <= 0;
if (ok)
{
enum {N = 40};
char *buf = xmalloc (N * (n + 1));
char const *buf_end = buf + N * (n + 1);
char *z = buf;
z = mempcpy (z, p, p_len);
while (cmp (p, p_len, q, q_len) < 0)
{
*z++ = *separator;
incr (&p, &p_len);
z = mempcpy (z, p, p_len);
if (buf_end - (n + 1) < z)
{
fwrite (buf, z - buf, 1, stdout);
z = buf;
}
}
*z++ = *terminator;
fwrite (buf, z - buf, 1, stdout);
IF_LINT (free (buf));
}
free (p0);
free (q0);
return ok;
}
static bool _GL_ATTRIBUTE_PURE
all_digits_p (char const *s)
{
size_t n = strlen (s);
return ISDIGIT (s[0]) && n == strspn (s, "0123456789");
}
int
main (int argc, char **argv)
{
int optc;
operand first = { 1, 1, 0 };
operand step = { 1, 1, 0 };
operand last;
struct layout layout = { 0, 0 };
char const *format_str = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
equal_width = false;
separator = "\n";
while (optind < argc)
{
if (argv[optind][0] == '-'
&& ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
{
break;
}
optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
if (optc == -1)
break;
switch (optc)
{
case 'f':
format_str = optarg;
break;
case 's':
separator = optarg;
break;
case 'w':
equal_width = true;
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
}
unsigned int n_args = argc - optind;
if (n_args < 1)
{
error (0, 0, _("missing operand"));
usage (EXIT_FAILURE);
}
if (3 < n_args)
{
error (0, 0, _("extra operand %s"), quote (argv[optind + 3]));
usage (EXIT_FAILURE);
}
if (format_str)
format_str = long_double_format (format_str, &layout);
if (format_str != NULL && equal_width)
{
error (0, 0, _("format string may not be specified"
" when printing equal width strings"));
usage (EXIT_FAILURE);
}
FIXMEFIXME if (all_digits_p (argv[optind])
&& (n_args == 1 || all_digits_p (argv[optind + 1]))
&& (n_args < 3 || (STREQ ("1", argv[optind + 1])
&& all_digits_p (argv[optind + 2])))
&& !equal_width && !format_str && strlen (separator) == 1)
{
char const *s1 = n_args == 1 ? "1" : argv[optind];
char const *s2 = argv[optind + (n_args - 1)];
if (seq_fast (s1, s2))
exit (EXIT_SUCCESS);
}
last = scan_arg (argv[optind++]);
if (optind < argc)
{
first = last;
last = scan_arg (argv[optind++]);
if (optind < argc)
{
step = last;
last = scan_arg (argv[optind++]);
}
}
if (first.precision == 0 && step.precision == 0 && last.precision == 0
&& 0 <= first.value && step.value == 1 && 0 <= last.value
&& !equal_width && !format_str && strlen (separator) == 1)
{
char *s1;
char *s2;
if (asprintf (&s1, "%0.Lf", first.value) < 0)
xalloc_die ();
if (asprintf (&s2, "%0.Lf", last.value) < 0)
xalloc_die ();
if (*s1 != '-' && *s2 != '-' && seq_fast (s1, s2))
{
IF_LINT (free (s1));
IF_LINT (free (s2));
exit (EXIT_SUCCESS);
}
free (s1);
free (s2);
}
if (format_str == NULL)
format_str = get_default_format (first, step, last);
print_numbers (format_str, layout, first.value, step.value, last.value);
exit (EXIT_SUCCESS);
}