Topologically sort the functions and remove fwd declarations.
[platform/upstream/coreutils.git] / src / seq.c
1 /* seq - print sequence of numbers to standard output.
2    Copyright (C) 1994-2000 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 "xstrtol.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 /* The C type of value to be printed with format_str.  */
37 enum Format_type
38 {
39   FT_DOUBLE,
40   FT_INT
41 };
42 typedef enum Format_type Format_type;
43
44 #define DO_printf(Format, Value)                \
45   do                                            \
46     {                                           \
47       if (format_type == FT_DOUBLE)             \
48         printf ((Format), (Value));             \
49       else                                      \
50         printf ((Format), (int) (Value));       \
51     }                                           \
52   while (0)
53
54 /* If nonzero print all number with equal width.  */
55 static int equal_width;
56
57 /* The name that this program was run with.  */
58 char *program_name;
59
60 /* The string used to separate two numbers.  */
61 static char *separator;
62
63 /* The string output after all numbers have been output.
64    Usually "\n" or "\0".  */
65 /* FIXME: make this an option.  */
66 static char *terminator = "\n";
67
68 /* The representation of the decimal point in the current locale.
69    Always "." if the localeconv function is not supported.  */
70 static char *decimal_point = ".";
71
72 /* The C type of value to be printed with format_str.  */
73 static Format_type format_type = FT_DOUBLE;
74
75 /* The starting number.  */
76 static double first;
77
78 /* The increment.  */
79 static double step;
80
81 /* The last number.  */
82 static double last;
83
84 static struct option const long_options[] =
85 {
86   { "equal-width", no_argument, NULL, 'w'},
87   { "format", required_argument, NULL, 'f'},
88   { "separator", required_argument, NULL, 's'},
89   {GETOPT_HELP_OPTION_DECL},
90   {GETOPT_VERSION_OPTION_DECL},
91   { NULL, 0, NULL, 0}
92 };
93
94 void
95 usage (int status)
96 {
97   if (status != 0)
98     fprintf (stderr, _("Try `%s --help' for more information.\n"),
99              program_name);
100   else
101     {
102       printf (_("\
103 Usage: %s [OPTION]... LAST\n\
104   or:  %s [OPTION]... FIRST LAST\n\
105   or:  %s [OPTION]... FIRST INCREMENT LAST\n\
106 "), program_name, program_name, program_name);
107       printf (_("\
108 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
109 \n\
110   -f, --format FORMAT      use printf(3) style FORMAT (default: %%g)\n\
111   -s, --separator STRING   use STRING to separate numbers (default: \\n)\n\
112   -w, --equal-width        equalize width by padding with leading zeroes\n\
113       --help               display this help and exit\n\
114       --version            output version information and exit\n\
115 \n\
116 If FIRST or INCREMENT is omitted, it defaults to 1.\n\
117 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
118 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
119 otherwise.  When given, the FORMAT argument must contain exactly one of\n\
120 the printf-style, floating point output formats %%e, %%f, %%g, or\n\
121 integer output formats %%d, %%u, %%o, %%x, %%X.\n\
122 "));
123       puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
124     }
125   exit (status);
126 }
127
128 /* Read a double value from the command line.
129    Return if the string is correct else signal error.  */
130
131 static double
132 scan_double_arg (const char *arg)
133 {
134   double ret_val;
135
136   if (xstrtod (arg, NULL, &ret_val))
137     {
138       error (0, 0, _("invalid floating point argument: %s"), arg);
139       usage (1);
140       /* NOTREACHED */
141     }
142
143   return ret_val;
144 }
145
146 /* Read an int value from the command line.
147    Return if the string is correct else signal error.  */
148
149 static int
150 scan_int_arg (const char *arg)
151 {
152   long int ret_val;
153
154   if (xstrtol (arg, NULL, 10, &ret_val, "") != LONGINT_OK
155       || ret_val < INT_MIN || ret_val > INT_MAX)
156     {
157       error (0, 0, _("invalid integer argument: %s"), arg);
158       usage (1);
159       /* NOTREACHED */
160     }
161
162   return ret_val;
163 }
164
165 /* Read a double value from the command line.
166    Return if the string is correct else signal error.  */
167
168 static double
169 scan_arg (const char *arg)
170 {
171   switch (format_type)
172     {
173     case FT_INT:
174       return (double) scan_int_arg (arg);
175     case FT_DOUBLE:
176       return scan_double_arg (arg);
177     default:
178       abort ();
179     }
180 }
181
182 /* Check whether the format string is valid for a single `double'
183    argument or a single `int' argument.  Return 0 if not, 1 if correct.
184    Set *INTCONV to non-zero if the conversion specifier is valid
185    for a single `int' argument, otherwise to zero.  */
186
187 static int
188 check_format (const char *fmt, Format_type *format_type_ptr)
189 {
190   *format_type_ptr = FT_DOUBLE;
191
192   while (*fmt != '\0')
193     {
194       if (*fmt == '%')
195         {
196           fmt++;
197           if (*fmt != '%')
198             break;
199         }
200
201       fmt++;
202     }
203   if (*fmt == '\0')
204     return 0;
205
206   fmt += strspn (fmt, "-+#0");
207   if (ISDIGIT (*fmt))
208     {
209       fmt += strspn (fmt, "0123456789");
210
211       if (*fmt == '.')
212         fmt += strspn (++fmt, "0123456789");
213     }
214
215   if (*fmt == 'd' || *fmt == 'u' || *fmt == 'o' || *fmt == 'x' || *fmt == 'X')
216     *format_type_ptr = FT_INT;
217   else if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
218     return 0;
219
220   fmt++;
221   while (*fmt != '\0')
222     {
223       if (*fmt == '%')
224         {
225           fmt++;
226           if (*fmt != '%')
227             return 0;
228         }
229
230       fmt++;
231     }
232
233   return 1;
234 }
235
236 /* Actually print the sequence of numbers in the specified range, with the
237    given or default stepping and format.  */
238 static int
239 print_numbers (const char *fmt)
240 {
241   if (first > last)
242     {
243       int i;
244
245       if (step >= 0)
246         {
247           error (0, 0,
248                  _("when the starting value is larger than the limit,\n\
249 the increment must be negative"));
250           usage (1);
251           /* NOTREACHED */
252         }
253
254       DO_printf (fmt, first);
255       for (i = 1; /* empty */; i++)
256         {
257           double x = first + i * step;
258
259           if (x < last)
260             break;
261
262           fputs (separator, stdout);
263           DO_printf (fmt, x);
264         }
265     }
266   else
267     {
268       int i;
269
270       if (step <= 0)
271         {
272           error (0, 0,
273                  _("when the starting value is smaller than the limit,\n\
274 the increment must be positive"));
275           usage (1);
276           /* NOTREACHED */
277         }
278
279       DO_printf (fmt, first);
280       for (i = 1; /* empty */; i++)
281         {
282           double x = first + i * step;
283
284           if (x > last)
285             break;
286
287           fputs (separator, stdout);
288           DO_printf (fmt, x);
289         }
290     }
291   fputs (terminator, stdout);
292
293   return 0;
294 }
295
296 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
297
298 /* Return a printf-style format string with which all selected numbers
299    will format to strings of the same width.  */
300
301 static char *
302 get_width_format ()
303 {
304   static char buffer[256];
305   int full_width;
306   int frac_width;
307   int width1, width2;
308   double max_val;
309   double min_val;
310   double temp;
311
312   if (first > last)
313     {
314       min_val = first - step * floor ((first - last) / step);
315       max_val = first;
316     }
317   else
318     {
319       min_val = first;
320       max_val = first + step * floor ((last - first) / step);
321     }
322
323   sprintf (buffer, "%g", rint (max_val));
324   if (buffer[strspn (buffer, "0123456789")] != '\0')
325     return "%g";
326   width1 = strlen (buffer);
327
328   if (min_val < 0.0)
329     {
330       sprintf (buffer, "%g", rint (min_val));
331       if (buffer[strspn (buffer, "-0123456789")] != '\0')
332         return "%g";
333       width2 = strlen (buffer);
334
335       width1 = width1 > width2 ? width1 : width2;
336     }
337   full_width = width1;
338
339   sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
340   width1 = strlen (buffer);
341   if (width1 == 1)
342     width1 = 0;
343   else
344     {
345       if (buffer[0] != '1'
346           /* FIXME: assumes that decimal_point is a single character
347              string.  */
348           || buffer[1] != decimal_point[0]
349           || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
350         return "%g";
351       width1 -= 2;
352     }
353
354   sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
355   width2 = strlen (buffer);
356   if (width2 == 1)
357     width2 = 0;
358   else
359     {
360       if (buffer[0] != '1'
361           /* FIXME: assumes that decimal_point is a single character
362              string.  */
363           || buffer[1] != decimal_point[0]
364           || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
365         return "%g";
366       width2 -= 2;
367     }
368   frac_width = width1 > width2 ? width1 : width2;
369
370   if (frac_width)
371     sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
372   else
373     sprintf (buffer, "%%0%dg", full_width);
374
375   return buffer;
376 }
377
378 #else   /* one of the math functions rint, modf, floor is missing.  */
379
380 static char *
381 get_width_format (void)
382 {
383   /* We cannot compute the needed information to determine the correct
384      answer.  So we simply return a value that works for all cases.  */
385   return "%g";
386 }
387
388 #endif
389
390 int
391 main (int argc, char **argv)
392 {
393   int errs;
394   int optc;
395   int step_is_set;
396   int format_ok;
397
398   /* The printf(3) format used for output.  */
399   char *format_str = NULL;
400
401   program_name = argv[0];
402   setlocale (LC_ALL, "");
403   bindtextdomain (PACKAGE, LOCALEDIR);
404   textdomain (PACKAGE);
405
406   equal_width = 0;
407   separator = "\n";
408   first = 1.0;
409   step_is_set = 0;
410
411   /* Figure out the locale's idea of a decimal point.  */
412 #ifdef HAVE_LOCALECONV
413   {
414     struct lconv *locale;
415
416     locale = localeconv ();
417     /* Paranoia.  */
418     if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
419       decimal_point = locale->decimal_point;
420   }
421 #endif
422
423   /* We have to handle negative numbers in the command line but this
424      conflicts with the command line arguments.  So explicitly check first
425      whether the next argument looks like a negative number.  */
426   while (optind < argc)
427     {
428       if (argv[optind][0] == '-'
429           && ((optc = argv[optind][1]) == decimal_point[0]
430               || ISDIGIT (optc)))
431         {
432           /* means negative number */
433           break;
434         }
435
436       optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
437       if (optc == -1)
438         break;
439
440       switch (optc)
441         {
442         case 0:
443           break;
444
445         case 'f':
446           format_str = optarg;
447           break;
448
449         case 's':
450           separator = optarg;
451           break;
452
453         case 'w':
454           equal_width = 1;
455           break;
456
457         case_GETOPT_HELP_CHAR;
458
459         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
460
461         default:
462           usage (1);
463           /* NOTREACHED */
464         }
465     }
466
467   /* Set format_type before calling scan_arg.  */
468   if (format_str != NULL)
469     format_ok = check_format (format_str, &format_type);
470   else
471     {
472       format_ok = 1;
473       format_type = FT_DOUBLE;
474     }
475
476   if (optind >= argc)
477     {
478       error (0, 0, _("too few arguments"));
479       usage (1);
480       /* NOTREACHED */
481     }
482   last = scan_arg (argv[optind++]);
483
484   if (optind < argc)
485     {
486       first = last;
487       last = scan_arg (argv[optind++]);
488
489       if (optind < argc)
490         {
491           step = last;
492           step_is_set = 1;
493           last = scan_arg (argv[optind++]);
494
495           if (optind < argc)
496             {
497               usage (1);
498               /* NOTREACHED */
499             }
500         }
501     }
502
503   if (format_str != NULL && equal_width)
504     {
505       error (0, 0, _("\
506 format string may not be specified when printing equal width strings"));
507       usage (1);
508     }
509
510   if (!step_is_set)
511     {
512       step = first <= last ? 1.0 : -1.0;
513     }
514
515   if (format_str != NULL)
516     {
517       if (!format_ok)
518         {
519           error (0, 0, _("invalid format string: `%s'"), format_str);
520           usage (1);
521         }
522     }
523   else
524     {
525       if (equal_width)
526         format_str = get_width_format ();
527       else
528         format_str = "%g";
529     }
530
531   errs = print_numbers (format_str);
532
533   exit (errs);
534   /* NOTREACHED */
535 }