.
[coreutils.git] / src / seq.c
blob0630222fae7452188e4705ca73d9432dcc2e5826
1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 94, 1995, 96 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 Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by Ulrich Drepper. */
20 #include <config.h>
21 #include <getopt.h>
22 #include <math.h>
23 #include <stdio.h>
25 #include "system.h"
26 #include "error.h"
27 #include "xstrtod.h"
29 static double scan_double_arg __P ((const char *arg));
30 static int check_format __P ((const char *format_string));
31 static char *get_width_format __P ((void));
32 static int print_numbers __P ((const char *format_str));
34 /* If nonzero print all number with equal width. */
35 static int equal_width;
37 /* The printf(3) format used for output. */
38 static char *format_str;
40 /* The starting number. */
41 static double first;
43 /* The name that this program was run with. */
44 char *program_name;
46 /* The string used to separate two numbers. */
47 static char *separator;
49 /* The string output after all numbers have been output.
50 Usually "\n" or "\0". */
51 /* FIXME: make this an option. */
52 static char *terminator = "\n";
54 /* If nonzero, display usage information and exit. */
55 static int show_help;
57 /* If nonzero, print the version on standard output and exit. */
58 static int show_version;
60 /* The increment. */
61 static double step;
63 /* The last number. */
64 static double last;
66 static struct option const long_options[] =
68 { "equal-width", no_argument, NULL, 'w'},
69 { "format", required_argument, NULL, 'f'},
70 { "help", no_argument, &show_help, 1},
71 { "separator", required_argument, NULL, 's'},
72 { "version", no_argument, &show_version, 1},
73 { NULL, 0, NULL, 0}
76 static void
77 usage (int status)
79 if (status != 0)
80 fprintf (stderr, _("Try `%s --help' for more information.\n"),
81 program_name);
82 else
84 printf (_("\
85 Usage: %s [OPTION]... LAST\n\
86 or: %s [OPTION]... FIRST LAST\n\
87 or: %s [OPTION]... FIRST INCREMENT LAST\n\
88 "), program_name, program_name, program_name);
89 printf (_("\
90 Print numbers from FIRST (default 1) to LAST, moving by STEP (default 1).\n\
91 \n\
92 -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
93 -s, --separator STRING use STRING for separating numbers (default: \\n)\n\
94 -w, --equal-width equalize width by padding with leading zeroes\n\
95 --help display this help and exit\n\
96 --version output version information and exit\n\
97 \n\
98 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
99 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
100 otherwise. When given, the FORMAT argument must contain exactly one of\n\
101 the printf-style, floating point output formats %%e, %%f, or %%g.\n\
102 "));
104 exit (status);
108 main (int argc, char **argv)
110 int errs;
111 int optc;
112 int step_is_set;
114 program_name = argv[0];
115 setlocale (LC_ALL, "");
116 bindtextdomain (PACKAGE, LOCALEDIR);
117 textdomain (PACKAGE);
119 equal_width = 0;
120 format_str = NULL;
121 separator = "\n";
122 first = 1.0;
123 step_is_set = 0;
125 /* We have to handle negative numbers in the command line but this
126 conflicts with the command line arguments. So the getopt mode is
127 REQUIRE_ORDER (the '+' in the format string) and it abort on the
128 first non-option or negative number. */
129 while ((optc = getopt_long (argc, argv, "+0123456789f:s:w", long_options,
130 (int *) 0)) != EOF)
132 if ('0' <= optc && optc <= '9')
134 /* means negative number */
135 break;
138 switch (optc)
140 case 0:
141 break;
143 case 'f':
144 format_str = optarg;
145 break;
147 case 's':
148 separator = optarg;
149 break;
151 case 'w':
152 equal_width = 1;
153 break;
155 default:
156 usage (1);
157 /* NOTREACHED */
161 if (show_version)
163 printf ("seq - %s\n", PACKAGE_VERSION);
164 exit (0);
167 if (show_help)
169 usage (0);
170 /* NOTREACHED */
173 if (optind >= argc)
175 error (0, 0, _("too few arguments"));
176 usage (1);
177 /* NOTREACHED */
179 last = scan_double_arg (argv[optind++]);
181 if (optind < argc)
183 first = last;
184 last = scan_double_arg (argv[optind++]);
186 if (optind < argc)
188 step = last;
189 step_is_set = 1;
190 last = scan_double_arg (argv[optind++]);
192 if (optind < argc)
194 usage (1);
195 /* NOTREACHED */
200 if (format_str != NULL && equal_width)
202 error (0, 0, _("\
203 format string may not be specified when printing equal width strings"));
204 usage (1);
207 if (!step_is_set)
209 step = first <= last ? 1.0 : -1.0;
212 if (format_str != NULL)
214 if (!check_format (format_str))
216 error (0, 0, _("invalid format string: `%s'"), format_str);
217 usage (1);
220 else
222 if (equal_width)
223 format_str = get_width_format ();
224 else
225 format_str = "%g";
228 errs = print_numbers (format_str);
230 exit (errs);
231 /* NOTREACHED */
234 /* Read a double value from the command line.
235 Return if the string is correct else signal error. */
237 static double
238 scan_double_arg (const char *arg)
240 double ret_val;
242 if (xstrtod (arg, NULL, &ret_val))
244 error (0, 0, _("invalid floating point argument: %s"), arg);
245 usage (1);
246 /* NOTREACHED */
249 return ret_val;
252 /* Check whether the format string is valid for a single double
253 argument.
254 Return 0 if not, 1 if correct. */
256 static int
257 check_format (const char *format_string)
259 while (*format_string != '\0')
261 if (*format_string == '%')
263 format_string++;
264 if (*format_string != '%')
265 break;
268 format_string++;
270 if (*format_string == '\0')
271 return 0;
273 format_string += strspn (format_string, "-+#0");
274 if (isdigit (*format_string))
276 format_string += strspn (format_string, "012345789");
278 if (*format_string == '.')
279 format_string += strspn (++format_string, "0123456789");
282 if (*format_string != 'e' && *format_string != 'f' &&
283 *format_string != 'g')
284 return 0;
286 format_string++;
287 while (*format_string != '\0')
289 if (*format_string == '%')
291 format_string++;
292 if (*format_string != '%')
293 return 0;
296 format_string++;
299 return 1;
302 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
304 /* Return a printf-style format string with which all selected numbers
305 will format to strings of the same width. */
307 static char *
308 get_width_format ()
310 static char buffer[256];
311 int full_width;
312 int frac_width;
313 int width1, width2;
314 double max_val;
315 double min_val;
316 double temp;
318 if (first > last)
320 min_val = first - step * floor ((first - last) / step);
321 max_val = first;
323 else
325 min_val = first;
326 max_val = first + step * floor ((last - first) / step);
329 sprintf (buffer, "%g", rint (max_val));
330 if (buffer[strspn (buffer, "0123456789")] != '\0')
331 return "%g";
332 width1 = strlen (buffer);
334 if (min_val < 0.0)
336 sprintf (buffer, "%g", rint (min_val));
337 if (buffer[strspn (buffer, "-0123456789")] != '\0')
338 return "%g";
339 width2 = strlen (buffer);
341 width1 = width1 > width2 ? width1 : width2;
343 full_width = width1;
345 sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
346 width1 = strlen (buffer);
347 if (width1 == 1)
348 width1 = 0;
349 else
351 if (buffer[0] != '1' || buffer[1] != '.' ||
352 buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
353 return "%g";
354 width1 -= 2;
357 sprintf (buffer, "%g", 1.0 + modf (step, &temp));
358 width2 = strlen (buffer);
359 if (width2 == 1)
360 width2 = 0;
361 else
363 if (buffer[0] != '1' || buffer[1] != '.' ||
364 buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
365 return "%g";
366 width2 -= 2;
368 frac_width = width1 > width2 ? width1 : width2;
370 if (frac_width)
371 sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
372 else
373 sprintf (buffer, "%%0%dg", full_width);
375 return buffer;
378 #else /* one of the math functions rint, modf, floor is missing. */
380 static char *
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. */
385 return "%g";
388 #endif
390 /* Actually print the sequence of numbers in the specified range, with the
391 given or default stepping and format. */
392 static int
393 print_numbers (const char *format_str)
395 if (first > last)
397 int i;
399 if (step >= 0)
401 error (0, 0,
402 _("when the starting value is larger than the limit,\n\
403 the increment must be negative"));
404 usage (1);
405 /* NOTREACHED */
408 printf (format_str, first);
409 for (i = 1; /* empty */; i++)
411 double x = first + i * step;
413 if (x < last)
414 break;
416 fputs (separator, stdout);
417 printf (format_str, x);
420 else
422 int i;
424 if (step <= 0)
426 error (0, 0,
427 _("when the starting value is smaller than the limit,\n\
428 the increment must be positive"));
429 usage (1);
430 /* NOTREACHED */
433 printf (format_str, first);
434 for (i = 1; /* empty */; i++)
436 double x = first + i * step;
438 if (x > last)
439 break;
441 fputs (separator, stdout);
442 printf (format_str, x);
445 fputs (terminator, stdout);
447 return 0;