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