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 /* If nonzero print all number with equal width. */
37 static int equal_width;
39 /* The name that this program was run with. */
42 /* The string used to separate two numbers. */
43 static char *separator;
45 /* The string output after all numbers have been output.
46 Usually "\n" or "\0". */
47 /* FIXME: make this an option. */
48 static char *terminator = "\n";
50 /* The representation of the decimal point in the current locale.
51 Always "." if the localeconv function is not supported. */
52 static char *decimal_point = ".";
54 /* The starting number. */
60 /* The last number. */
63 static struct option const long_options[] =
65 { "equal-width", no_argument, NULL, 'w'},
66 { "format", required_argument, NULL, 'f'},
67 { "separator", required_argument, NULL, 's'},
68 {GETOPT_HELP_OPTION_DECL},
69 {GETOPT_VERSION_OPTION_DECL},
77 fprintf (stderr, _("Try `%s --help' for more information.\n"),
82 Usage: %s [OPTION]... LAST\n\
83 or: %s [OPTION]... FIRST LAST\n\
84 or: %s [OPTION]... FIRST INCREMENT LAST\n\
85 "), program_name, program_name, program_name);
87 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
89 -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
90 -s, --separator STRING use STRING to separate numbers (default: \\n)\n\
91 -w, --equal-width equalize width by padding with leading zeroes\n\
92 --help display this help and exit\n\
93 --version output version information and exit\n\
95 If FIRST or INCREMENT is omitted, it defaults to 1.\n\
96 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
97 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
98 otherwise. When given, the FORMAT argument must contain exactly one of\n\
99 the printf-style, floating point output formats %%e, %%f, %%g\n\
101 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
106 /* Read a double value from the command line.
107 Return if the string is correct else signal error. */
110 scan_double_arg (const char *arg)
114 if (xstrtod (arg, NULL, &ret_val))
116 error (0, 0, _("invalid floating point argument: %s"), arg);
123 /* Check whether the format string is valid for a single `double'
124 argument. Return 0 if not, 1 if correct. */
127 valid_format (const char *fmt)
143 fmt += strspn (fmt, "-+#0");
146 fmt += strspn (fmt, "0123456789");
149 fmt += strspn (++fmt, "0123456789");
152 if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
171 /* Actually print the sequence of numbers in the specified range, with the
172 given or default stepping and format. */
174 print_numbers (const char *fmt)
183 _("when the starting value is larger than the limit,\n\
184 the increment must be negative"));
189 for (i = 1; /* empty */; i++)
191 double x = first + i * step;
196 fputs (separator, stdout);
207 _("when the starting value is smaller than the limit,\n\
208 the increment must be positive"));
213 for (i = 1; /* empty */; i++)
215 double x = first + i * step;
220 fputs (separator, stdout);
224 fputs (terminator, stdout);
229 #if HAVE_RINT && HAVE_MODF && HAVE_FLOOR
231 /* Return a printf-style format string with which all selected numbers
232 will format to strings of the same width. */
237 static char buffer[256];
247 min_val = first - step * floor ((first - last) / step);
253 max_val = first + step * floor ((last - first) / step);
256 sprintf (buffer, "%g", rint (max_val));
257 if (buffer[strspn (buffer, "0123456789")] != '\0')
259 width1 = strlen (buffer);
263 double int_min_val = rint (min_val);
264 sprintf (buffer, "%g", int_min_val);
265 if (buffer[strspn (buffer, "-0123456789")] != '\0')
267 /* On some systems, `seq -w -.1 .1 .1' results in buffer being `-0'.
268 On others, it is just `0'. The former results in better output. */
269 width2 = (int_min_val == 0 ? 2 : strlen (buffer));
271 width1 = width1 > width2 ? width1 : width2;
275 sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
276 width1 = strlen (buffer);
282 /* FIXME: assumes that decimal_point is a single character
284 || buffer[1] != decimal_point[0]
285 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
290 sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
291 width2 = strlen (buffer);
297 /* FIXME: assumes that decimal_point is a single character
299 || buffer[1] != decimal_point[0]
300 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
304 frac_width = width1 > width2 ? width1 : width2;
307 sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
309 sprintf (buffer, "%%0%dg", full_width);
314 #else /* one of the math functions rint, modf, floor is missing. */
317 get_width_format (void)
319 /* We cannot compute the needed information to determine the correct
320 answer. So we simply return a value that works for all cases. */
327 main (int argc, char **argv)
333 /* The printf(3) format used for output. */
334 char *format_str = NULL;
336 program_name = argv[0];
337 setlocale (LC_ALL, "");
338 bindtextdomain (PACKAGE, LOCALEDIR);
339 textdomain (PACKAGE);
346 /* Figure out the locale's idea of a decimal point. */
349 struct lconv *locale;
351 locale = localeconv ();
353 if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
354 decimal_point = locale->decimal_point;
358 /* We have to handle negative numbers in the command line but this
359 conflicts with the command line arguments. So explicitly check first
360 whether the next argument looks like a negative number. */
361 while (optind < argc)
363 if (argv[optind][0] == '-'
364 && ((optc = argv[optind][1]) == decimal_point[0]
367 /* means negative number */
371 optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
392 case_GETOPT_HELP_CHAR;
394 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
401 if (argc - optind < 1)
403 error (0, 0, _("too few arguments"));
407 if (3 < argc - optind)
409 error (0, 0, _("too many arguments"));
413 if (format_str && !valid_format (format_str))
415 error (0, 0, _("invalid format string: `%s'"), format_str);
419 last = scan_double_arg (argv[optind++]);
424 last = scan_double_arg (argv[optind++]);
430 last = scan_double_arg (argv[optind++]);
435 if (format_str != NULL && equal_width)
438 format string may not be specified when printing equal width strings"));
444 step = first <= last ? 1.0 : -1.0;
447 if (format_str == NULL)
450 format_str = get_width_format ();
455 errs = print_numbers (format_str);