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