1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 1994-2000 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by Ulrich Drepper. */
24 #include <sys/types.h>
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "seq"
34 #define AUTHORS "Ulrich Drepper"
36 /* The C type of value to be printed with format_str. */
42 typedef enum Format_type Format_type;
44 #define DO_printf(Format, Value) \
47 if (format_type == FT_DOUBLE) \
48 printf ((Format), (Value)); \
50 printf ((Format), (int) (Value)); \
54 /* If nonzero print all number with equal width. */
55 static int equal_width;
57 /* The name that this program was run with. */
60 /* The string used to separate two numbers. */
61 static char *separator;
63 /* The string output after all numbers have been output.
64 Usually "\n" or "\0". */
65 /* FIXME: make this an option. */
66 static char *terminator = "\n";
68 /* The representation of the decimal point in the current locale.
69 Always "." if the localeconv function is not supported. */
70 static char *decimal_point = ".";
72 /* The C type of value to be printed with format_str. */
73 static Format_type format_type = FT_DOUBLE;
75 /* The starting number. */
81 /* The last number. */
84 static struct option const long_options[] =
86 { "equal-width", no_argument, NULL, 'w'},
87 { "format", required_argument, NULL, 'f'},
88 { "separator", required_argument, NULL, 's'},
89 {GETOPT_HELP_OPTION_DECL},
90 {GETOPT_VERSION_OPTION_DECL},
98 fprintf (stderr, _("Try `%s --help' for more information.\n"),
103 Usage: %s [OPTION]... LAST\n\
104 or: %s [OPTION]... FIRST LAST\n\
105 or: %s [OPTION]... FIRST INCREMENT LAST\n\
106 "), program_name, program_name, program_name);
108 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
110 -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
111 -s, --separator STRING use STRING to separate numbers (default: \\n)\n\
112 -w, --equal-width equalize width by padding with leading zeroes\n\
113 --help display this help and exit\n\
114 --version output version information and exit\n\
116 If FIRST or INCREMENT is omitted, it defaults to 1.\n\
117 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
118 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
119 otherwise. When given, the FORMAT argument must contain exactly one of\n\
120 the printf-style, floating point output formats %%e, %%f, %%g, or\n\
121 integer output formats %%d, %%u, %%o, %%x, %%X.\n\
123 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
128 /* Read a double value from the command line.
129 Return if the string is correct else signal error. */
132 scan_double_arg (const char *arg)
136 if (xstrtod (arg, NULL, &ret_val))
138 error (0, 0, _("invalid floating point argument: %s"), arg);
146 /* Read an int value from the command line.
147 Return if the string is correct else signal error. */
150 scan_int_arg (const char *arg)
154 if (xstrtol (arg, NULL, 10, &ret_val, "") != LONGINT_OK
155 || ret_val < INT_MIN || ret_val > INT_MAX)
157 error (0, 0, _("invalid integer argument: %s"), arg);
165 /* Read a double value from the command line.
166 Return if the string is correct else signal error. */
169 scan_arg (const char *arg)
174 return (double) scan_int_arg (arg);
176 return scan_double_arg (arg);
182 /* Check whether the format string is valid for a single `double'
183 argument or a single `int' argument. Return 0 if not, 1 if correct.
184 Set *INTCONV to non-zero if the conversion specifier is valid
185 for a single `int' argument, otherwise to zero. */
188 check_format (const char *fmt, Format_type *format_type_ptr)
190 *format_type_ptr = FT_DOUBLE;
206 fmt += strspn (fmt, "-+#0");
209 fmt += strspn (fmt, "0123456789");
212 fmt += strspn (++fmt, "0123456789");
215 if (*fmt == 'd' || *fmt == 'u' || *fmt == 'o' || *fmt == 'x' || *fmt == 'X')
216 *format_type_ptr = FT_INT;
217 else if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
236 /* Actually print the sequence of numbers in the specified range, with the
237 given or default stepping and format. */
239 print_numbers (const char *fmt)
248 _("when the starting value is larger than the limit,\n\
249 the increment must be negative"));
254 DO_printf (fmt, first);
255 for (i = 1; /* empty */; i++)
257 double x = first + i * step;
262 fputs (separator, stdout);
273 _("when the starting value is smaller than the limit,\n\
274 the increment must be positive"));
279 DO_printf (fmt, first);
280 for (i = 1; /* empty */; i++)
282 double x = first + i * step;
287 fputs (separator, stdout);
291 fputs (terminator, stdout);
296 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
298 /* Return a printf-style format string with which all selected numbers
299 will format to strings of the same width. */
304 static char buffer[256];
314 min_val = first - step * floor ((first - last) / step);
320 max_val = first + step * floor ((last - first) / step);
323 sprintf (buffer, "%g", rint (max_val));
324 if (buffer[strspn (buffer, "0123456789")] != '\0')
326 width1 = strlen (buffer);
330 sprintf (buffer, "%g", rint (min_val));
331 if (buffer[strspn (buffer, "-0123456789")] != '\0')
333 width2 = strlen (buffer);
335 width1 = width1 > width2 ? width1 : width2;
339 sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
340 width1 = strlen (buffer);
346 /* FIXME: assumes that decimal_point is a single character
348 || buffer[1] != decimal_point[0]
349 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
354 sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
355 width2 = strlen (buffer);
361 /* FIXME: assumes that decimal_point is a single character
363 || buffer[1] != decimal_point[0]
364 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
368 frac_width = width1 > width2 ? width1 : width2;
371 sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
373 sprintf (buffer, "%%0%dg", full_width);
378 #else /* one of the math functions rint, modf, floor is missing. */
381 get_width_format (void)
383 /* We cannot compute the needed information to determine the correct
384 answer. So we simply return a value that works for all cases. */
391 main (int argc, char **argv)
398 /* The printf(3) format used for output. */
399 char *format_str = NULL;
401 program_name = argv[0];
402 setlocale (LC_ALL, "");
403 bindtextdomain (PACKAGE, LOCALEDIR);
404 textdomain (PACKAGE);
411 /* Figure out the locale's idea of a decimal point. */
412 #ifdef HAVE_LOCALECONV
414 struct lconv *locale;
416 locale = localeconv ();
418 if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
419 decimal_point = locale->decimal_point;
423 /* We have to handle negative numbers in the command line but this
424 conflicts with the command line arguments. So explicitly check first
425 whether the next argument looks like a negative number. */
426 while (optind < argc)
428 if (argv[optind][0] == '-'
429 && ((optc = argv[optind][1]) == decimal_point[0]
432 /* means negative number */
436 optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
457 case_GETOPT_HELP_CHAR;
459 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
467 /* Set format_type before calling scan_arg. */
468 if (format_str != NULL)
469 format_ok = check_format (format_str, &format_type);
473 format_type = FT_DOUBLE;
478 error (0, 0, _("too few arguments"));
482 last = scan_arg (argv[optind++]);
487 last = scan_arg (argv[optind++]);
493 last = scan_arg (argv[optind++]);
503 if (format_str != NULL && equal_width)
506 format string may not be specified when printing equal width strings"));
512 step = first <= last ? 1.0 : -1.0;
515 if (format_str != NULL)
519 error (0, 0, _("invalid format string: `%s'"), format_str);
526 format_str = get_width_format ();
531 errs = print_numbers (format_str);