(main): Declare to be of type int, not void.
[platform/upstream/coreutils.git] / src / seq.c
1 /* seq - print sequence of numbers to standard output.
2    Copyright (C) 94, 1995 Free Software Foundation, Inc.
3
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.
8
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.
13
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.  */
17
18 /* Written by Ulrich Drepper.  */
19
20 #include <config.h>
21 #include <getopt.h>
22 #include <math.h>
23 #include <stdio.h>
24
25 #include "system.h"
26 #include "error.h"
27
28 static double scan_double_arg __P ((char *arg));
29 static int check_format __P ((char *format_string));
30 static char *get_width_format __P ((void));
31 static int print_numbers __P ((char *format_str));
32
33 /* If nonzero print all number with equal width.  */
34 static int equal_width;
35
36 /* The printf(3) format used for output.  */
37 static char *format_str;
38
39 /* The starting number.  */
40 static double from;
41
42 /* The name that this program was run with.  */
43 char *program_name;
44
45 /* The string used to separate two number.  */
46 static char *separator;
47
48 /* If nonzero, display usage information and exit.  */
49 static int show_help;
50
51 /* If nonzero, print the version on standard output and exit.  */
52 static int show_version;
53
54 /* The increment.  */
55 static double step;
56
57 /* The last number.  */
58 static double last;
59
60 static struct option const long_options[] =
61 {
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}
68 };
69
70 static void
71 usage (int status)
72 {
73   if (status != 0)
74     (void) fprintf (stderr, _("Try `%s --help' for more information.\n"),
75                     program_name);
76   else
77     {
78       (void) printf (_("\
79 Usage: %s [OPTION]... [from [step]] to\n\
80 "), program_name);
81       (void) printf (_("\
82 \n\
83   -f, --format FORMAT      use printf(3) style FORMAT (default: %%g)\n\
84       --help               display this help and exit\n\
85   -s, --separator STRING   use STRING for separating numbers (default: \\n)\n\
86       --version            output version information and exit\n\
87   -w, --equal-width        equalize width by padding with leading zeroes\n\
88 \n\
89   FROM, STEP, TO are interpreted as floating point.  STEP should be > 0 if\n\
90   FROM is smaller than TO and vice versa.  When given, the FORMAT argument\n\
91   must contain exactly one of the float output formats %%e, %%f, or %%g.\n\
92 "));
93     }
94   exit (status);
95 }
96
97 int
98 main (int argc, char **argv)
99 {
100   int errs;
101   int optc;
102   int step_is_set;
103
104   program_name = argv[0];
105   setlocale (LC_ALL, "");
106   bindtextdomain (PACKAGE, LOCALEDIR);
107   textdomain (PACKAGE);
108
109   equal_width = 0;
110   format_str = NULL;
111   separator = "\n";
112   from = 1.0;
113   step_is_set = 0;
114
115   /* We have to handle negative numbers in the command line but this
116      conflicts with the command line arguments.  So the getopt mode is
117      REQUIRE_ORDER (the '+' in the format string) and it abort on the
118      first non-option or negative number.  */
119   while ((optc = getopt_long (argc, argv, "+0123456789f:s:w", long_options,
120                               (int *) 0)) != EOF)
121     {
122       if ('0' <= optc && optc <= '9')
123         {
124           /* means negative number */
125           break;
126         }
127
128       switch (optc)
129         {
130         case 0:
131           break;
132
133         case 'f':
134           format_str = optarg;
135           break;
136
137         case 's':
138           separator = optarg;
139           break;
140
141         case 'w':
142           equal_width = 1;
143           break;
144
145         default:
146           usage (1);
147           /* NOTREACHED */
148         }
149     }
150
151   if (show_version)
152     {
153       (void) printf ("seq - %s\n", PACKAGE_VERSION);
154       exit (0);
155     }
156
157   if (show_help)
158     {
159       usage (0);
160       /* NOTREACHED */
161     }
162
163   if (optind >= argc)
164     {
165       error (0, 0, _("too few arguments"));
166       usage (1);
167       /* NOTREACHED */
168     }
169   last = scan_double_arg (argv[optind++]);
170
171   if (optind < argc)
172     {
173       from = last;
174       last = scan_double_arg (argv[optind++]);
175
176       if (optind < argc)
177         {
178           step = last;
179           step_is_set = 1;
180           last = scan_double_arg (argv[optind++]);
181
182           if (optind < argc)
183             {
184               usage (1);
185               /* NOTREACHED */
186             }
187         }
188     }
189
190   if (format_str != NULL && equal_width)
191     {
192       error (0, 0, _("\
193 format string may not be specified when printing equal width strings"));
194       usage (1);
195     }
196
197   if (!step_is_set)
198     {
199       step = from <= last ? 1.0 : -1.0;
200     }
201
202   if (format_str != NULL)
203     {
204       if (!check_format (format_str))
205         {
206           error (0, 0, _("invalid format string: `%s'"), format_str);
207           usage (1);
208         }
209     }
210   else
211     {
212       if (equal_width)
213         format_str = get_width_format ();
214       else
215         format_str = "%g";
216     }
217
218   errs = print_numbers (format_str);
219
220   exit (errs);
221   /* NOTREACHED */
222 }
223
224 /* Read a double value from the command line.
225    Return if the string is correct else signal error.  */
226
227 static double
228 scan_double_arg (char *arg)
229 {
230   char *end_ptr;
231   double ret_val;
232
233   /* FIXME: use xstrtod?  At least set and check errno.  */
234   ret_val = strtod (arg, &end_ptr);
235   if (end_ptr == arg || *end_ptr != '\0')
236     {
237       error (0, 0, _("invalid float argument: %s"), arg);
238       usage (1);
239       /* NOTREACHED */
240     }
241
242   return ret_val;
243 }
244
245 /* Check whether the format string is valid for a single double
246    argument.
247    Return 0 if not, 1 if correct.  */
248
249 static int
250 check_format (char *format_string)
251 {
252   while (*format_string != '\0')
253     {
254       if (*format_string == '%')
255         {
256           format_string++;
257           if (*format_string != '%')
258             break;
259         }
260
261       format_string++;
262     }
263   if (*format_string == '\0')
264     return 0;
265
266   format_string += strspn (format_string, "-+#0");
267   if (isdigit (*format_string))
268     {
269       format_string += strspn (format_string, "012345789");
270
271       if (*format_string == '.')
272         format_string += strspn (++format_string, "0123456789");
273     }
274
275   if (*format_string != 'e' && *format_string != 'f' &&
276       *format_string != 'g')
277     return 0;
278
279   format_string++;
280   while (*format_string != '\0')
281     {
282       if (*format_string == '%')
283         {
284           format_string++;
285           if (*format_string != '%')
286             return 0;
287         }
288
289       format_string++;
290     }
291
292   return 1;
293 }
294
295 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
296
297 /* Return a printf-style format string with which all selected numbers
298    will format to strings of the same width.  */
299
300 static char *
301 get_width_format ()
302 {
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;
310
311   if (from > last)
312     {
313       min_val = from - step * floor ((from - last) / step);
314       max_val = from;
315     }
316   else
317     {
318       min_val = from;
319       max_val = from + step * floor ((last - from) / step);
320     }
321
322   (void) sprintf (buffer, "%g", rint (max_val));
323   if (buffer[strspn (buffer, "0123456789")] != '\0')
324     return "%g";
325   width1 = strlen (buffer);
326
327   if (min_val < 0.0)
328     {
329       (void) sprintf (buffer, "%g", rint (min_val));
330       if (buffer[strspn (buffer, "-0123456789")] != '\0')
331         return "%g";
332       width2 = strlen (buffer);
333
334       width1 = width1 > width2 ? width1 : width2;
335     }
336   full_width = width1;
337
338   (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
339   width1 = strlen (buffer);
340   if (width1 == 1)
341     width1 = 0;
342   else
343     {
344       if (buffer[0] != '1' || buffer[1] != '.' ||
345           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
346         return "%g";
347       width1 -= 2;
348     }
349
350   (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
351   width2 = strlen (buffer);
352   if (width2 == 1)
353     width2 = 0;
354   else
355     {
356       if (buffer[0] != '1' || buffer[1] != '.' ||
357           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
358         return "%g";
359       width2 -= 2;
360     }
361   frac_width = width1 > width2 ? width1 : width2;
362
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);
367
368   return buffer;
369 }
370
371 #else   /* one of the math functions rint, modf, floor is missing.  */
372
373 static char *
374 get_width_format (void)
375 {
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";
379 }
380
381 #endif
382
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 (char *format_str)
387 {
388   if (from > last)
389     {
390       if (step >= 0)
391         {
392           error (0, 0, _("invalid increment: %g"), step);
393           usage (1);
394           /* NOTREACHED */
395         }
396
397       while (1)
398         {
399           (void) printf (format_str, from);
400
401           /* FIXME: don't increment!!!  Use `first + i * step'.  */
402           from += step;
403           if (from < last)
404             break;
405
406           (void) fputs (separator, stdout);
407         }
408     }
409   else
410     {
411       if (step <= 0)
412         {
413           error (0, 0, _("invalid increment: %g"), step);
414           usage (1);
415           /* NOTREACHED */
416         }
417
418       while (1)
419         {
420           (void) printf (format_str, from);
421
422           /* FIXME: don't increment!!!  Use `first + i * step'.  */
423           from += step;
424           if (from > last)
425             break;
426
427           (void) fputs (separator, stdout);
428         }
429     }
430
431   return 0;
432 }