.
[coreutils.git] / src / seq.c
blob667cf7766b8e286adcdd21bf8015e9a0f9b835ca
1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 94, 1995 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)
7 any later version.
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
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Ulrich Drepper */
20 #include <getopt.h>
21 #include <math.h>
22 #include <stdio.h>
24 #include "system.h"
25 #include "error.h"
26 #include "version.h"
28 static double scan_double_arg ();
29 static int check_format ();
30 static char *get_width_format ();
31 static int print_numbers ();
33 /* If non-zero print all number with equal width. */
34 static int equal_width;
36 /* The printf(3) format used for output. */
37 static char *format_str;
39 /* The starting number. */
40 static double from;
42 /* The name that this program was run with. */
43 char *program_name;
45 /* The string used to separate two number. */
46 static char *separator;
48 /* If non-zero, display usage information and exit. */
49 static int show_help;
51 /* If non-zero, print the version on standard output and exit. */
52 static int show_version;
54 /* The increment. */
55 static double step;
57 /* The last number. */
58 static double last;
60 static struct option const long_options[] =
62 { "equal-width", no_argument, NULL, 'w'},
63 { "format", required_argument, NULL, 'f'},
64 { "help", no_argument, &show_help, 1},
65 { "separator", required_argument, NULL, 's'},
66 { "version", no_argument, &show_version, 1},
67 { NULL, 0, NULL, 0}
70 static void
71 usage (status)
72 int status;
74 if (status != 0)
75 (void) fprintf (stderr, "Try `%s --help' for more information.\n",
76 program_name);
77 else
79 (void) printf ("\
80 Usage: %s [OPTION]... [from [step]] to\n\
81 ", program_name);
82 (void) printf ("\
83 \n\
84 -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
85 --help display this help and exit\n\
86 -s, --separator STRING use STRING for separating numbers (default: \\n)\n\
87 --version output version information and exit\n\
88 -w, --equal-width equalize width by padding with leading zeroes\n\
89 \n\
90 FROM, STEP, TO are interpreted as floating point. STEP should be > 0 if\n\
91 FROM is smaller than TO and vice versa. When given, the FORMAT argument\n\
92 must contain exactly one of the float output formats %%e, %%f, or %%g.\n\
93 ");
95 exit (status);
98 void
99 main (argc, argv)
100 int argc;
101 char **argv;
103 int errs;
104 int optc;
105 int step_is_set;
107 program_name = argv[0];
108 equal_width = 0;
109 format_str = NULL;
110 separator = "\n";
111 from = 1.0;
112 step_is_set = 0;
114 /* We have to handle negative numbers in the command line but this
115 conflicts with the command line arguments. So the getopt mode is
116 REQUIRE_ORDER (the '+' in the format string) and it abort on the
117 first non-option or negative number. */
118 while ((optc = getopt_long (argc, argv, "+0123456789f:s:w", long_options,
119 (int *) 0)) != EOF)
121 if ('0' <= optc && optc <= '9')
123 /* means negative number */
124 break;
127 switch (optc)
129 case 0:
130 break;
132 case 'f':
133 format_str = optarg;
134 break;
136 case 's':
137 separator = optarg;
138 break;
140 case 'w':
141 equal_width = 1;
142 break;
144 default:
145 usage (1);
146 /* NOTREACHED */
150 if (show_version)
152 (void) printf ("seq - %s\n", version_string);
155 if (show_help)
157 usage (0);
158 /* NOTREACHED */
161 if (optind >= argc)
163 error (0, 0, "too few arguments");
164 usage (1);
165 /* NOTREACHED */
167 last = scan_double_arg (argv[optind++]);
169 if (optind < argc)
171 from = last;
172 last = scan_double_arg (argv[optind++]);
174 if (optind < argc)
176 step = last;
177 step_is_set = 1;
178 last = scan_double_arg (argv[optind++]);
180 if (optind < argc)
182 usage (1);
183 /* NOTREACHED */
188 if (format_str != NULL && equal_width)
190 error (0, 0,
191 "format string may not be specified when printing equal width strings");
192 usage (1);
195 if (!step_is_set)
197 step = from <= last ? 1.0 : -1.0;
200 if (format_str != NULL)
202 if (!check_format (format_str))
204 error (0, 0, "invalid format string: `%s'", format_str);
205 usage (1);
208 else
210 if (equal_width)
211 format_str = get_width_format ();
212 else
213 format_str = "%g";
216 errs = print_numbers (format_str);
218 exit (errs);
219 /* NOTREACHED */
222 /* Read a double value from the command line.
223 Return if the string is correct else signal error. */
225 static double
226 scan_double_arg (arg)
227 char *arg;
229 char *end_ptr;
230 double ret_val;
232 /* FIXME: use xstrtod? At least set and check errno. */
233 ret_val = strtod (arg, &end_ptr);
234 if (end_ptr == arg || *end_ptr != '\0')
236 error (0, 0, "invalid float argument: %s", arg);
237 usage (1);
238 /* NOTREACHED */
241 return ret_val;
244 /* Check whether the format string is valid for a single double
245 argument.
246 Return 0 if not, 1 if correct. */
248 static int
249 check_format (format_string)
250 char *format_string;
252 while (*format_string != '\0')
254 if (*format_string == '%')
256 format_string++;
257 if (*format_string != '%')
258 break;
261 format_string++;
263 if (*format_string == '\0')
264 return 0;
266 format_string += strspn (format_string, "-+#0");
267 if (isdigit (*format_string))
269 format_string += strspn (format_string, "012345789");
271 if (*format_string == '.')
272 format_string += strspn (++format_string, "0123456789");
275 if (*format_string != 'e' && *format_string != 'f' &&
276 *format_string != 'g')
277 return 0;
279 format_string++;
280 while (*format_string != '\0')
282 if (*format_string == '%')
284 format_string++;
285 if (*format_string != '%')
286 return 0;
289 format_string++;
292 return 1;
295 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
297 /* Return a printf-style format string with which all selected numbers
298 will format to strings of the same width. */
300 static char *
301 get_width_format ()
303 static char buffer[256];
304 int full_width;
305 int frac_width;
306 int width1, width2;
307 double max_val;
308 double min_val;
309 double temp;
311 if (from > last)
313 min_val = from - step * floor ((from - last) / step);
314 max_val = from;
316 else
318 min_val = from;
319 max_val = from + step * floor ((last - from) / step);
322 (void) sprintf (buffer, "%g", rint (max_val));
323 if (buffer[strspn (buffer, "0123456789")] != '\0')
324 return "%g";
325 width1 = strlen (buffer);
327 if (min_val < 0.0)
329 (void) sprintf (buffer, "%g", rint (min_val));
330 if (buffer[strspn (buffer, "-0123456789")] != '\0')
331 return "%g";
332 width2 = strlen (buffer);
334 width1 = width1 > width2 ? width1 : width2;
336 full_width = width1;
338 (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
339 width1 = strlen (buffer);
340 if (width1 == 1)
341 width1 = 0;
342 else
344 if (buffer[0] != '1' || buffer[1] != '.' ||
345 buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
346 return "%g";
347 width1 -= 2;
350 (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
351 width2 = strlen (buffer);
352 if (width2 == 1)
353 width2 = 0;
354 else
356 if (buffer[0] != '1' || buffer[1] != '.' ||
357 buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
358 return "%g";
359 width2 -= 2;
361 frac_width = width1 > width2 ? width1 : width2;
363 if (frac_width)
364 (void) sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
365 else
366 (void) sprintf (buffer, "%%0%dg", full_width);
368 return buffer;
371 #else /* one of the math functions rint, modf, floor is missing. */
373 static char *
374 get_width_format ()
376 /* We cannot compute the needed information to determine the correct
377 answer. So we simply return a value that works for all cases. */
378 return "%g";
381 #endif
383 /* Actually print the sequence of numbers in the specified range, with the
384 given or default stepping and format. */
385 static int
386 print_numbers (format_str)
387 char *format_str;
389 if (from > last)
391 if (step >= 0)
393 error (0, 0, "invalid increment: %g", step);
394 usage (1);
395 /* NOTREACHED */
398 while (1)
400 (void) printf (format_str, from);
402 /* FIXME: don't increment!!! Use `first + i * step'. */
403 from += step;
404 if (from < last)
405 break;
407 (void) fputs (separator, stdout);
410 else
412 if (step <= 0)
414 error (0, 0, "invalid increment: %g", step);
415 usage (1);
416 /* NOTREACHED */
419 while (1)
421 (void) printf (format_str, from);
423 /* FIXME: don't increment!!! Use `first + i * step'. */
424 from += step;
425 if (from > last)
426 break;
428 (void) fputs (separator, stdout);
432 return 0;