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