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