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