Fix typo: seperator -> separator.
[platform/upstream/coreutils.git] / src / seq.c
1 /* seq - print sequence of numbers to standard output.
2    Copyright (C) 1994, 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 /* Ulrich Drepper */
19
20 #include <getopt.h>
21 #include <math.h>
22 #include <stdio.h>
23
24 #include "system.h"
25 #include "error.h"
26 #include "version.h"
27
28 static double scan_double_arg ();
29 static int check_format ();
30 static char *get_width_format ();
31 static int print_numbers ();
32
33 /* If non-zero 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 non-zero, display usage information and exit.  */
49 static int show_help;
50
51 /* If non-zero, 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 (status)
72      int status;
73 {
74   if (status != 0)
75     (void) fprintf (stderr, "Try `%s --help' for more information.\n",
76                     program_name);
77   else
78     {
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 ");
94     }
95   exit (status);
96 }
97
98 void
99 main (argc, argv)
100      int argc;
101      char **argv;
102 {
103   int errs;
104   int optc;
105   int step_is_set;
106
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;
113
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)
120     {
121       if ('0' <= optc && optc <= '9')
122         {
123           /* means negative number */
124           break;
125         }
126
127       switch (optc)
128         {
129         case 0:
130           break;
131
132         case 'f':
133           format_str = optarg;
134           break;
135
136         case 's':
137           separator = optarg;
138           break;
139
140         case 'w':
141           equal_width = 1;
142           break;
143
144         default:
145           usage (1);
146           /* NOTREACHED */
147         }
148     }
149
150   if (show_version)
151     {
152       (void) printf ("seq - %s\n", version_string);
153     }
154
155   if (show_help)
156     {
157       usage (0);
158       /* NOTREACHED */
159     }
160
161   if (optind >= argc)
162     {
163       error (0, 0, "too few arguments");
164       usage (2);
165       /* NOTREACHED */
166     }
167   last = scan_double_arg (argv[optind++]);
168
169   if (optind < argc)
170     {
171       from = last;
172       last = scan_double_arg (argv[optind++]);
173
174       if (optind < argc)
175         {
176           step = last;
177           step_is_set = 1;
178           last = scan_double_arg (argv[optind++]);
179
180           if (optind < argc)
181             {
182               usage (2);
183               /* NOTREACHED */
184             }
185         }
186     }
187
188   if (!step_is_set)
189     {
190       step = from < last ? 1.0 : -1.0;
191     }
192
193   if (format_str != NULL)
194     {
195       if (!check_format (format_str))
196         {
197           error (0, 0, "invalid format string: `%s'", format_str);
198           usage (4);
199         }
200     }
201   else
202     {
203       if (equal_width)
204         format_str = get_width_format ();
205       else
206         format_str = "%g";
207     }
208
209   errs = print_numbers (format_str);
210
211   exit (errs);
212   /* NOTREACHED */
213 }
214
215 /* Read a double value from the command line.
216    Return if the string is correct else signal error.  */
217
218 static double
219 scan_double_arg (arg)
220      char *arg;
221 {
222   char *end_ptr;
223   double ret_val;
224
225   /* FIXME: use xstrtod?  At least set and check errno.  */
226   ret_val = strtod (arg, &end_ptr);
227   if (end_ptr == arg || *end_ptr != '\0')
228     {
229       error (0, 0, "invalid float argument: %s\n", arg);
230       usage (2);
231       /* NOTREACHED */
232     }
233
234   return ret_val;
235 }
236
237 /* Check whether the format string is valid for a single double
238    argument.
239    Return 0 if not, 1 if correct.  */
240
241 static int
242 check_format (format_string)
243      char *format_string;
244 {
245   while (*format_string != '\0')
246     {
247       if (*format_string == '%')
248         {
249           format_string++;
250           if (*format_string != '%')
251             break;
252         }
253
254       format_string++;
255     }
256   if (*format_string == '\0')
257     return 0;
258
259   format_string += strspn (format_string, "-+#0");
260   if (isdigit (*format_string))
261     {
262       format_string += strspn (format_string, "012345789");
263
264       if (*format_string == '.')
265         format_string += strspn (++format_string, "0123456789");
266     }
267
268   if (*format_string != 'e' && *format_string != 'f' &&
269       *format_string != 'g')
270     return 0;
271
272   format_string++;
273   while (*format_string != '\0')
274     {
275       if (*format_string == '%')
276         {
277           format_string++;
278           if (*format_string != '%')
279             return 0;
280         }
281
282       format_string++;
283     }
284
285   return 1;
286 }
287
288 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
289
290 /* Return a printf-style format string with which all selected numbers
291    will format to strings of the same width.  */
292
293 static char *
294 get_width_format ()
295 {
296   static char buffer[256];
297   int full_width;
298   int frac_width;
299   int width1, width2;
300   double max_val;
301   double min_val;
302   double temp;
303
304   if (from > last)
305     {
306       min_val = from - step * floor ((from - last) / step);
307       max_val = from;
308     }
309   else
310     {
311       min_val = from;
312       max_val = from + step * floor ((last - from) / step);
313     }
314
315   (void) sprintf (buffer, "%g", rint (max_val));
316   if (buffer[strspn (buffer, "0123456789")] != '\0')
317     return "%g";
318   width1 = strlen (buffer);
319
320   if (min_val < 0.0)
321     {
322       (void) sprintf (buffer, "%g", rint (min_val));
323       if (buffer[strspn (buffer, "-0123456789")] != '\0')
324         return "%g";
325       width2 = strlen (buffer);
326
327       width1 = width1 > width2 ? width1 : width2;
328     }
329   full_width = width1;
330
331   (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
332   width1 = strlen (buffer);
333   if (width1 == 1)
334     width1 = 0;
335   else
336     {
337       if (buffer[0] != '1' || buffer[1] != '.' ||
338           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
339         return "%g";
340       width1 -= 2;
341     }
342
343   (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
344   width2 = strlen (buffer);
345   if (width2 == 1)
346     width2 = 0;
347   else
348     {
349       if (buffer[0] != '1' || buffer[1] != '.' ||
350           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
351         return "%g";
352       width2 -= 2;
353     }
354   frac_width = width1 > width2 ? width1 : width2;
355
356   if (frac_width)
357     (void) sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
358   else
359     (void) sprintf (buffer, "%%0%dg", full_width);
360
361   return buffer;
362 }
363
364 #else   /* one of the math functions rint, modf, floor is missing.  */
365
366 static char *
367 get_width_format ()
368 {
369   /* We cannot compute the needed information to determine the correct
370      answer.  So we simply return a value that works for all cases.  */
371   return "%g";
372 }
373
374 #endif
375
376 /* Actually print the sequence of numbers in the specified range, with the
377    given or default stepping and format.  */
378 static int
379 print_numbers (format_str)
380      char *format_str;
381 {
382   if (from > last)
383     {
384       if (step >= 0)
385         {
386           error (0, 0, "invalid increment: %g\n", step);
387           usage (2);
388           /* NOTREACHED */
389         }
390
391       while (1)
392         {
393           (void) printf (format_str, from);
394
395           /* FIXME: don't increment!!!  Use `first + i * step'.  */
396           from += step;
397           if (from < last)
398             break;
399
400           (void) fputs (separator, stdout);
401         }
402     }
403   else
404     {
405       if (step <= 0)
406         {
407           error (0, 0, "invalid increment: %g\n", step);
408           usage (2);
409           /* NOTREACHED */
410         }
411
412       while (1)
413         {
414           (void) printf (format_str, from);
415
416           /* FIXME: don't increment!!!  Use `first + i * step'.  */
417           from += step;
418           if (from > last)
419             break;
420
421           (void) fputs (separator, stdout);
422         }
423     }
424
425   return 0;
426 }