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