/* seq - print sequence of numbers to standard output.
- Copyright (C) 94, 1995 Free Software Foundation, Inc.
+ Copyright (C) 1994-2003 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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Written by Ulrich Drepper. */
#include <getopt.h>
#include <math.h>
#include <stdio.h>
+#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "xstrtol.h"
#include "xstrtod.h"
-static double scan_double_arg __P ((const char *arg));
-static int check_format __P ((const char *format_string));
-static char *get_width_format __P ((void));
-static int print_numbers __P ((const char *format_str));
+/* The official name of this program (e.g., no `g' prefix). */
+#define PROGRAM_NAME "seq"
+
+#define WRITTEN_BY _("Written by Ulrich Drepper.")
/* If nonzero print all number with equal width. */
static int equal_width;
-/* The printf(3) format used for output. */
-static char *format_str;
-
-/* The starting number. */
-static double first;
-
/* The name that this program was run with. */
char *program_name;
/* FIXME: make this an option. */
static char *terminator = "\n";
-/* If nonzero, display usage information and exit. */
-static int show_help;
+/* The representation of the decimal point in the current locale.
+ Always "." if the localeconv function is not supported. */
+static char *decimal_point = ".";
-/* If nonzero, print the version on standard output and exit. */
-static int show_version;
+/* The starting number. */
+static double first;
/* The increment. */
-static double step;
+static double step = 1.0;
/* The last number. */
static double last;
{
{ "equal-width", no_argument, NULL, 'w'},
{ "format", required_argument, NULL, 'f'},
- { "help", no_argument, &show_help, 1},
{ "separator", required_argument, NULL, 's'},
- { "version", no_argument, &show_version, 1},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
{ NULL, 0, NULL, 0}
};
-static void
+void
usage (int status)
{
if (status != 0)
- (void) fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
else
{
- (void) printf (_("\
-Usage: %s [OPTION]... [FIRST [step]] LAST\n\
-"), program_name);
- (void) printf (_("\
+ 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\
\n\
- -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
- --help display this help and exit\n\
- -s, --separator STRING use STRING for separating numbers (default: \\n)\n\
- --version output version information and exit\n\
+ -f, --format=FORMAT use printf style floating-point FORMAT (default: %g)\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\
- FROM, STEP, TO are interpreted as floating point. STEP should be > 0 if\n\
- FROM is smaller than TO and vice versa. When given, the FORMAT argument\n\
- must contain exactly one of the float output formats %%e, %%f, or %%g.\n\
-"));
+If FIRST or INCREMENT is omitted, it defaults to 1.\n\
+FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
+INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
+otherwise. When given, the FORMAT argument must contain exactly one of\n\
+the printf-style, floating point output formats %e, %f, %g\n\
+"), stdout);
+ printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
exit (status);
}
-int
-main (int argc, char **argv)
-{
- int errs;
- int optc;
- int step_is_set;
-
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- equal_width = 0;
- format_str = NULL;
- separator = "\n";
- first = 1.0;
- step_is_set = 0;
-
- /* We have to handle negative numbers in the command line but this
- conflicts with the command line arguments. So the getopt mode is
- REQUIRE_ORDER (the '+' in the format string) and it abort on the
- first non-option or negative number. */
- while ((optc = getopt_long (argc, argv, "+0123456789f:s:w", long_options,
- (int *) 0)) != EOF)
- {
- if ('0' <= optc && optc <= '9')
- {
- /* means negative number */
- break;
- }
-
- switch (optc)
- {
- case 0:
- break;
-
- case 'f':
- format_str = optarg;
- break;
-
- case 's':
- separator = optarg;
- break;
-
- case 'w':
- equal_width = 1;
- break;
-
- default:
- usage (1);
- /* NOTREACHED */
- }
- }
-
- if (show_version)
- {
- (void) printf ("seq - %s\n", PACKAGE_VERSION);
- exit (0);
- }
-
- if (show_help)
- {
- usage (0);
- /* NOTREACHED */
- }
-
- if (optind >= argc)
- {
- error (0, 0, _("too few arguments"));
- usage (1);
- /* NOTREACHED */
- }
- last = scan_double_arg (argv[optind++]);
-
- if (optind < argc)
- {
- first = last;
- last = scan_double_arg (argv[optind++]);
-
- if (optind < argc)
- {
- step = last;
- step_is_set = 1;
- last = scan_double_arg (argv[optind++]);
-
- if (optind < argc)
- {
- usage (1);
- /* NOTREACHED */
- }
- }
- }
-
- if (format_str != NULL && equal_width)
- {
- error (0, 0, _("\
-format string may not be specified when printing equal width strings"));
- usage (1);
- }
-
- if (!step_is_set)
- {
- step = first <= last ? 1.0 : -1.0;
- }
-
- if (format_str != NULL)
- {
- if (!check_format (format_str))
- {
- error (0, 0, _("invalid format string: `%s'"), format_str);
- usage (1);
- }
- }
- else
- {
- if (equal_width)
- format_str = get_width_format ();
- else
- format_str = "%g";
- }
-
- errs = print_numbers (format_str);
-
- exit (errs);
- /* NOTREACHED */
-}
-
/* Read a double value from the command line.
Return if the string is correct else signal error. */
if (xstrtod (arg, NULL, &ret_val))
{
error (0, 0, _("invalid floating point argument: %s"), arg);
- usage (1);
- /* NOTREACHED */
+ usage (EXIT_FAILURE);
}
return ret_val;
}
-/* Check whether the format string is valid for a single double
- argument.
- Return 0 if not, 1 if correct. */
+/* Check whether the format string is valid for a single `double'
+ argument. Return 0 if not, 1 if correct. */
static int
-check_format (const char *format_string)
+valid_format (const char *fmt)
{
- while (*format_string != '\0')
+ while (*fmt != '\0')
{
- if (*format_string == '%')
+ if (*fmt == '%')
{
- format_string++;
- if (*format_string != '%')
+ fmt++;
+ if (*fmt != '%')
break;
}
- format_string++;
+ fmt++;
}
- if (*format_string == '\0')
+ if (*fmt == '\0')
return 0;
- format_string += strspn (format_string, "-+#0");
- if (isdigit (*format_string))
+ fmt += strspn (fmt, "-+#0 '");
+ if (ISDIGIT (*fmt) || *fmt == '.')
{
- format_string += strspn (format_string, "012345789");
+ fmt += strspn (fmt, "0123456789");
- if (*format_string == '.')
- format_string += strspn (++format_string, "0123456789");
+ if (*fmt == '.')
+ {
+ ++fmt;
+ fmt += strspn (fmt, "0123456789");
+ }
}
- if (*format_string != 'e' && *format_string != 'f' &&
- *format_string != 'g')
+ if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
return 0;
- format_string++;
- while (*format_string != '\0')
+ fmt++;
+ while (*fmt != '\0')
{
- if (*format_string == '%')
+ if (*fmt == '%')
{
- format_string++;
- if (*format_string != '%')
+ fmt++;
+ if (*fmt != '%')
return 0;
}
- format_string++;
+ fmt++;
}
return 1;
}
-#if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
+/* Actually print the sequence of numbers in the specified range, with the
+ given or default stepping and format. */
+static int
+print_numbers (const char *fmt)
+{
+ int i;
+
+ for (i = 0; /* empty */; i++)
+ {
+ double x = first + i * step;
+ if (step < 0 ? x < last : last < x)
+ break;
+ if (i)
+ fputs (separator, stdout);
+ printf (fmt, x);
+ }
+
+ if (i)
+ fputs (terminator, stdout);
+
+ return 0;
+}
+
+#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. */
max_val = first + step * floor ((last - first) / step);
}
- (void) sprintf (buffer, "%g", rint (max_val));
- if (buffer[strspn (buffer, "0123456789")] != '\0')
+ sprintf (buffer, "%g", rint (max_val));
+ if (buffer[strspn (buffer, "-0123456789")] != '\0')
return "%g";
width1 = strlen (buffer);
if (min_val < 0.0)
{
- (void) sprintf (buffer, "%g", rint (min_val));
+ double int_min_val = rint (min_val);
+ sprintf (buffer, "%g", int_min_val);
if (buffer[strspn (buffer, "-0123456789")] != '\0')
return "%g";
- width2 = strlen (buffer);
+ /* 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;
- (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
+ 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] != '.' ||
- buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
+ if (buffer[0] != '1'
+ /* FIXME: assumes that decimal_point is a single character
+ string. */
+ || buffer[1] != decimal_point[0]
+ || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
return "%g";
width1 -= 2;
}
- (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
+ sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
width2 = strlen (buffer);
if (width2 == 1)
width2 = 0;
else
{
- if (buffer[0] != '1' || buffer[1] != '.' ||
- buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
+ if (buffer[0] != '1'
+ /* FIXME: assumes that decimal_point is a single character
+ string. */
+ || buffer[1] != decimal_point[0]
+ || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
return "%g";
width2 -= 2;
}
frac_width = width1 > width2 ? width1 : width2;
if (frac_width)
- (void) sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
+ sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
else
- (void) sprintf (buffer, "%%0%dg", full_width);
+ sprintf (buffer, "%%0%dg", full_width);
return buffer;
}
#endif
-/* Actually print the sequence of numbers in the specified range, with the
- given or default stepping and format. */
-static int
-print_numbers (const char *format_str)
+int
+main (int argc, char **argv)
{
- if (first > last)
- {
- int i;
+ int errs;
+ int optc;
+
+ /* The printf(3) format used for output. */
+ char *format_str = NULL;
+
+ initialize_main (&argc, &argv);
+ program_name = argv[0];
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
- if (step >= 0)
+ atexit (close_stdout);
+
+ equal_width = 0;
+ separator = "\n";
+ first = 1.0;
+
+ /* Figure out the locale's idea of a decimal point. */
+#if HAVE_LOCALECONV
+ {
+ struct lconv *locale;
+
+ locale = localeconv ();
+ /* Paranoia. */
+ if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
+ decimal_point = locale->decimal_point;
+ }
+#endif
+
+ /* We have to handle negative numbers in the command line but this
+ conflicts with the command line arguments. So explicitly check first
+ whether the next argument looks like a negative number. */
+ while (optind < argc)
+ {
+ if (argv[optind][0] == '-'
+ && ((optc = argv[optind][1]) == decimal_point[0]
+ || ISDIGIT (optc)))
{
- error (0, 0, _("invalid increment: %g"), step);
- usage (1);
- /* NOTREACHED */
+ /* means negative number */
+ break;
}
- for (i = 0; /* empty */; i++)
+ optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
+ if (optc == -1)
+ break;
+
+ switch (optc)
{
- double x = first + i * step;
- printf (format_str, x);
+ case 0:
+ break;
- if (x <= last)
- break;
+ case 'f':
+ format_str = optarg;
+ break;
- fputs (separator, stdout);
+ case 's':
+ separator = optarg;
+ break;
+
+ case 'w':
+ equal_width = 1;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
+
+ default:
+ usage (EXIT_FAILURE);
}
}
- else
+
+ if (argc - optind < 1)
{
- int i;
+ error (0, 0, _("too few arguments"));
+ usage (EXIT_FAILURE);
+ }
- if (step <= 0)
- {
- error (0, 0, _("invalid increment: %g"), step);
- usage (1);
- /* NOTREACHED */
- }
+ if (3 < argc - optind)
+ {
+ error (0, 0, _("too many arguments"));
+ usage (EXIT_FAILURE);
+ }
- for (i = 0; /* empty */; i++)
- {
- double x = first + i * step;
- printf (format_str, x);
+ if (format_str && !valid_format (format_str))
+ {
+ error (0, 0, _("invalid format string: `%s'"), format_str);
+ usage (EXIT_FAILURE);
+ }
- if (x >= last)
- break;
+ last = scan_double_arg (argv[optind++]);
- fputs (separator, stdout);
+ if (optind < argc)
+ {
+ first = last;
+ last = scan_double_arg (argv[optind++]);
+
+ if (optind < argc)
+ {
+ step = last;
+ last = scan_double_arg (argv[optind++]);
}
}
- fputs (terminator, stdout);
- return 0;
+ if (format_str != NULL && equal_width)
+ {
+ error (0, 0, _("\
+format string may not be specified when printing equal width strings"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (format_str == NULL)
+ {
+ if (equal_width)
+ format_str = get_width_format ();
+ else
+ format_str = "%g";
+ }
+
+ errs = print_numbers (format_str);
+
+ exit (errs);
}