*** empty log message ***
[coreutils.git] / src / seq.c
blob613627dd03ac1ed8414fc19618f95efee05d6d30
1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 1994-1999 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>
24 #include <sys/types.h>
26 #include "system.h"
27 #include "error.h"
28 #include "xstrtod.h"
30 /* The official name of this program (e.g., no `g' prefix). */
31 #define PROGRAM_NAME "seq"
33 #define AUTHORS "Ulrich Drepper"
35 static double scan_double_arg PARAMS ((const char *arg));
36 static int check_format PARAMS ((const char *format_string));
37 static char *get_width_format PARAMS ((void));
38 static int print_numbers PARAMS ((const char *format_str));
40 /* If nonzero print all number with equal width. */
41 static int equal_width;
43 /* The printf(3) format used for output. */
44 static char *format_str;
46 /* The starting number. */
47 static double first;
49 /* The name that this program was run with. */
50 char *program_name;
52 /* The string used to separate two numbers. */
53 static char *separator;
55 /* The string output after all numbers have been output.
56 Usually "\n" or "\0". */
57 /* FIXME: make this an option. */
58 static char *terminator = "\n";
60 /* The representation of the decimal point in the current locale.
61 Always "." if the localeconv function is not supported. */
62 static char *decimal_point = ".";
64 /* The increment. */
65 static double step;
67 /* The last number. */
68 static double last;
70 static struct option const long_options[] =
72 { "equal-width", no_argument, NULL, 'w'},
73 { "format", required_argument, NULL, 'f'},
74 { "separator", required_argument, NULL, 's'},
75 {GETOPT_HELP_OPTION_DECL},
76 {GETOPT_VERSION_OPTION_DECL},
77 { NULL, 0, NULL, 0}
80 void
81 usage (int status)
83 if (status != 0)
84 fprintf (stderr, _("Try `%s --help' for more information.\n"),
85 program_name);
86 else
88 printf (_("\
89 Usage: %s [OPTION]... LAST\n\
90 or: %s [OPTION]... FIRST LAST\n\
91 or: %s [OPTION]... FIRST INCREMENT LAST\n\
92 "), program_name, program_name, program_name);
93 printf (_("\
94 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
95 \n\
96 -f, --format FORMAT use printf(3) style FORMAT (default: %%g)\n\
97 -s, --separator STRING use STRING to separate numbers (default: \\n)\n\
98 -w, --equal-width equalize width by padding with leading zeroes\n\
99 --help display this help and exit\n\
100 --version output version information and exit\n\
102 If FIRST or INCREMENT is omitted, it defaults to 1.\n\
103 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
104 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
105 otherwise. When given, the FORMAT argument must contain exactly one of\n\
106 the printf-style, floating point output formats %%e, %%f, or %%g.\n\
107 "));
108 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
110 exit (status);
114 main (int argc, char **argv)
116 int errs;
117 int optc;
118 int step_is_set;
120 program_name = argv[0];
121 setlocale (LC_ALL, "");
122 bindtextdomain (PACKAGE, LOCALEDIR);
123 textdomain (PACKAGE);
125 equal_width = 0;
126 format_str = NULL;
127 separator = "\n";
128 first = 1.0;
129 step_is_set = 0;
131 /* Figure out the locale's idea of a decimal point. */
132 #ifdef HAVE_LOCALECONV
134 struct lconv *locale;
136 locale = localeconv ();
137 /* Paranoia. */
138 if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
139 decimal_point = locale->decimal_point;
141 #endif
143 /* We have to handle negative numbers in the command line but this
144 conflicts with the command line arguments. So explicitly check first
145 whether the next argument looks like a negative number. */
146 while (optind < argc)
148 if (argv[optind][0] == '-'
149 && ((optc = argv[optind][1]) == decimal_point[0]
150 || ISDIGIT (optc)))
152 /* means negative number */
153 break;
156 optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
157 if (optc == -1)
158 break;
160 switch (optc)
162 case 0:
163 break;
165 case 'f':
166 format_str = optarg;
167 break;
169 case 's':
170 separator = optarg;
171 break;
173 case 'w':
174 equal_width = 1;
175 break;
177 case_GETOPT_HELP_CHAR;
179 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
181 default:
182 usage (1);
183 /* NOTREACHED */
187 if (optind >= argc)
189 error (0, 0, _("too few arguments"));
190 usage (1);
191 /* NOTREACHED */
193 last = scan_double_arg (argv[optind++]);
195 if (optind < argc)
197 first = last;
198 last = scan_double_arg (argv[optind++]);
200 if (optind < argc)
202 step = last;
203 step_is_set = 1;
204 last = scan_double_arg (argv[optind++]);
206 if (optind < argc)
208 usage (1);
209 /* NOTREACHED */
214 if (format_str != NULL && equal_width)
216 error (0, 0, _("\
217 format string may not be specified when printing equal width strings"));
218 usage (1);
221 if (!step_is_set)
223 step = first <= last ? 1.0 : -1.0;
226 if (format_str != NULL)
228 if (!check_format (format_str))
230 error (0, 0, _("invalid format string: `%s'"), format_str);
231 usage (1);
234 else
236 if (equal_width)
237 format_str = get_width_format ();
238 else
239 format_str = "%g";
242 errs = print_numbers (format_str);
244 exit (errs);
245 /* NOTREACHED */
248 /* Read a double value from the command line.
249 Return if the string is correct else signal error. */
251 static double
252 scan_double_arg (const char *arg)
254 double ret_val;
256 if (xstrtod (arg, NULL, &ret_val))
258 error (0, 0, _("invalid floating point argument: %s"), arg);
259 usage (1);
260 /* NOTREACHED */
263 return ret_val;
266 /* Check whether the format string is valid for a single double
267 argument.
268 Return 0 if not, 1 if correct. */
270 static int
271 check_format (const char *fmt)
273 while (*fmt != '\0')
275 if (*fmt == '%')
277 fmt++;
278 if (*fmt != '%')
279 break;
282 fmt++;
284 if (*fmt == '\0')
285 return 0;
287 fmt += strspn (fmt, "-+#0");
288 if (ISDIGIT (*fmt))
290 fmt += strspn (fmt, "0123456789");
292 if (*fmt == '.')
293 fmt += strspn (++fmt, "0123456789");
296 if (*fmt != 'e' && *fmt != 'f' && *fmt != 'g')
297 return 0;
299 fmt++;
300 while (*fmt != '\0')
302 if (*fmt == '%')
304 fmt++;
305 if (*fmt != '%')
306 return 0;
309 fmt++;
312 return 1;
315 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
317 /* Return a printf-style format string with which all selected numbers
318 will format to strings of the same width. */
320 static char *
321 get_width_format ()
323 static char buffer[256];
324 int full_width;
325 int frac_width;
326 int width1, width2;
327 double max_val;
328 double min_val;
329 double temp;
331 if (first > last)
333 min_val = first - step * floor ((first - last) / step);
334 max_val = first;
336 else
338 min_val = first;
339 max_val = first + step * floor ((last - first) / step);
342 sprintf (buffer, "%g", rint (max_val));
343 if (buffer[strspn (buffer, "0123456789")] != '\0')
344 return "%g";
345 width1 = strlen (buffer);
347 if (min_val < 0.0)
349 sprintf (buffer, "%g", rint (min_val));
350 if (buffer[strspn (buffer, "-0123456789")] != '\0')
351 return "%g";
352 width2 = strlen (buffer);
354 width1 = width1 > width2 ? width1 : width2;
356 full_width = width1;
358 sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
359 width1 = strlen (buffer);
360 if (width1 == 1)
361 width1 = 0;
362 else
364 if (buffer[0] != '1'
365 /* FIXME: assumes that decimal_point is a single character
366 string. */
367 || buffer[1] != decimal_point[0]
368 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
369 return "%g";
370 width1 -= 2;
373 sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
374 width2 = strlen (buffer);
375 if (width2 == 1)
376 width2 = 0;
377 else
379 if (buffer[0] != '1'
380 /* FIXME: assumes that decimal_point is a single character
381 string. */
382 || buffer[1] != decimal_point[0]
383 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
384 return "%g";
385 width2 -= 2;
387 frac_width = width1 > width2 ? width1 : width2;
389 if (frac_width)
390 sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
391 else
392 sprintf (buffer, "%%0%dg", full_width);
394 return buffer;
397 #else /* one of the math functions rint, modf, floor is missing. */
399 static char *
400 get_width_format (void)
402 /* We cannot compute the needed information to determine the correct
403 answer. So we simply return a value that works for all cases. */
404 return "%g";
407 #endif
409 /* Actually print the sequence of numbers in the specified range, with the
410 given or default stepping and format. */
411 static int
412 print_numbers (const char *fmt)
414 if (first > last)
416 int i;
418 if (step >= 0)
420 error (0, 0,
421 _("when the starting value is larger than the limit,\n\
422 the increment must be negative"));
423 usage (1);
424 /* NOTREACHED */
427 printf (fmt, first);
428 for (i = 1; /* empty */; i++)
430 double x = first + i * step;
432 if (x < last)
433 break;
435 fputs (separator, stdout);
436 printf (fmt, x);
439 else
441 int i;
443 if (step <= 0)
445 error (0, 0,
446 _("when the starting value is smaller than the limit,\n\
447 the increment must be positive"));
448 usage (1);
449 /* NOTREACHED */
452 printf (fmt, first);
453 for (i = 1; /* empty */; i++)
455 double x = first + i * step;
457 if (x > last)
458 break;
460 fputs (separator, stdout);
461 printf (format_str, x);
464 fputs (terminator, stdout);
466 return 0;