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