Include "system.h".
[platform/upstream/coreutils.git] / src / seq.c
1 /* seq - print sequence of numbers to standard output.
2    Copyright (C) 1994 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 seperate two number. */
46 static char *seperator;
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   { "seperator", 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, --seperator STRING   use STRING for seperating 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   seperator = "\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           seperator = 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       /* FIXME Give reason for failure.  */
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 an 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   ret_val = strtod (arg, &end_ptr);
226   if (end_ptr == arg || *end_ptr != '\0')
227     {
228       error (0, 0, "invalid float argument: %s\n", arg);
229       usage (2);
230       /* NOTREACHED */
231     }
232
233   return ret_val;
234 }
235
236 /* Check whether the format string is valid for a single double
237    argument.
238    Return 0 if not, 1 if correct.  */
239
240 static int
241 check_format (format_string)
242      char *format_string;
243 {
244   while (*format_string != '\0')
245     {
246       if (*format_string == '%')
247         {
248           format_string++;
249           if (*format_string != '%')
250             break;
251         }
252
253       format_string++;
254     }
255   if (*format_string == '\0')
256     return 0;
257
258   format_string += strspn (format_string, "-+#0");
259   if (isdigit (*format_string))
260     {
261       format_string += strspn (format_string, "012345789");
262
263       if (*format_string == '.')
264         format_string += strspn (++format_string, "0123456789");
265     }
266
267   if (*format_string != 'e' && *format_string != 'f' &&
268       *format_string != 'g')
269     return 0;
270
271   format_string++;
272   while (*format_string != '\0')
273     {
274       if (*format_string == '%')
275         {
276           format_string++;
277           if (*format_string != '%')
278             return 0;
279         }
280
281       format_string++;
282     }
283
284   return 1;
285 }
286
287 /* Returns the format for that all printed numbers have the same width.  */
288 static char *
289 get_width_format ()
290 {
291   static char buffer[256];
292   int full_width;
293   int frac_width;
294   int width1, width2;
295   double max_val;
296   double min_val;
297   double temp;
298
299   if (from > last)
300     {
301       min_val = from - step * floor ((from - last) / step);
302       max_val = from;
303     }
304   else
305     {
306       min_val = from;
307       max_val = from + step * floor ((last - from) / step);
308     }
309
310   (void) sprintf (buffer, "%g", rint (max_val));
311   if (buffer[strspn (buffer, "0123456789")] != '\0')
312     return "%g";
313   width1 = strlen (buffer);
314
315   if (min_val < 0.0)
316     {
317       (void) sprintf (buffer, "%g", rint (min_val));
318       if (buffer[strspn (buffer, "-0123456789")] != '\0')
319         return "%g";
320       width2 = strlen (buffer);
321
322       width1 = width1 > width2 ? width1 : width2;
323     }
324   full_width = width1;
325
326   (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
327   width1 = strlen (buffer);
328   if (width1 == 1)
329     width1 = 0;
330   else
331     {
332       if (buffer[0] != '1' || buffer[1] != '.' ||
333           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
334         return "%g";
335       width1 -= 2;
336     }
337
338   (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
339   width2 = strlen (buffer);
340   if (width2 == 1)
341     width2 = 0;
342   else
343     {
344       if (buffer[0] != '1' || buffer[1] != '.' ||
345           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
346         return "%g";
347       width2 -= 2;
348     }
349   frac_width = width1 > width2 ? width1 : width2;
350
351   if (frac_width)
352     (void) sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
353   else
354     (void) sprintf (buffer, "%%0%dg", full_width);
355
356   return buffer;
357 }
358
359 /* Actually print the sequence of numbers in the specified range, with the
360    given or default stepping and format.  */
361 static int
362 print_numbers (format_str)
363      char *format_str;
364 {
365   if (from > last)
366     {
367       if (step >= 0)
368         {
369           error (0, 0, "invalid increment: %g\n", step);
370           usage (2);
371           /* NOTREACHED */
372         }
373
374       while (1)
375         {
376           (void) printf (format_str, from);
377
378           from += step;
379           if (from < last)
380             break;
381
382           (void) fputs (seperator, stdout);
383         }
384     }
385   else
386     {
387       if (step <= 0)
388         {
389           error (0, 0, "invalid increment: %g\n", step);
390           usage (2);
391           /* NOTREACHED */
392         }
393
394       while (1)
395         {
396           (void) printf (format_str, from);
397
398           from += step;
399           if (from > last)
400             break;
401
402           (void) fputs (seperator, stdout);
403         }
404     }
405
406   return 0;
407 }