Improve the check for departures from C89, and fix the departures
[platform/upstream/coreutils.git] / src / seq.c
1 /* seq - print sequence of numbers to standard output.
2    Copyright (C) 1994-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by Ulrich Drepper.  */
19
20 #include <config.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24
25 #include "system.h"
26 #include "c-strtod.h"
27 #include "error.h"
28 #include "quote.h"
29 #include "xstrtod.h"
30
31 /* Roll our own isfinite rather than using <math.h>, so that we don't
32    have to worry about linking -lm just for isfinite.  */
33 #ifndef isfinite
34 # define isfinite(x) ((x) * 0 == 0)
35 #endif
36
37 /* The official name of this program (e.g., no `g' prefix).  */
38 #define PROGRAM_NAME "seq"
39
40 #define AUTHORS "Ulrich Drepper"
41
42 /* If true print all number with equal width.  */
43 static bool equal_width;
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 const *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 const terminator[] = "\n";
55
56 static struct option const long_options[] =
57 {
58   { "equal-width", no_argument, NULL, 'w'},
59   { "format", required_argument, NULL, 'f'},
60   { "separator", required_argument, NULL, 's'},
61   {GETOPT_HELP_OPTION_DECL},
62   {GETOPT_VERSION_OPTION_DECL},
63   { NULL, 0, NULL, 0}
64 };
65
66 void
67 usage (int status)
68 {
69   if (status != EXIT_SUCCESS)
70     fprintf (stderr, _("Try `%s --help' for more information.\n"),
71              program_name);
72   else
73     {
74       printf (_("\
75 Usage: %s [OPTION]... LAST\n\
76   or:  %s [OPTION]... FIRST LAST\n\
77   or:  %s [OPTION]... FIRST INCREMENT LAST\n\
78 "), program_name, program_name, program_name);
79       fputs (_("\
80 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
81 \n\
82   -f, --format=FORMAT      use printf style floating-point FORMAT\n\
83   -s, --separator=STRING   use STRING to separate numbers (default: \\n)\n\
84   -w, --equal-width        equalize width by padding with leading zeroes\n\
85 "), stdout);
86       fputs (HELP_OPTION_DESCRIPTION, stdout);
87       fputs (VERSION_OPTION_DESCRIPTION, stdout);
88       fputs (_("\
89 \n\
90 If FIRST or INCREMENT is omitted, it defaults to 1.  That is, an\n\
91 omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.\n\
92 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
93 INCREMENT is usually positive if FIRST is smaller than LAST, and\n\
94 INCREMENT is usually negative if FIRST is greater than LAST.\n\
95 "), stdout);
96       fputs (_("\
97 FORMAT must be suitable for printing one argument of type `double';\n\
98 it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point\n\
99 decimal numbers with maximum precision PREC, and to %g otherwise.\n\
100 "), stdout);
101       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
102     }
103   exit (status);
104 }
105
106 /* A command-line operand.  */
107 struct operand
108 {
109   /* Its value, converted to 'long double'.  */
110   long double value;
111
112   /* Its print width, if it were printed out in a form similar to its
113      input form.  An input like "-.1" is treated like "-0.1", and an
114      input like "1." is treated like "1", but otherwise widths are
115      left alone.  */
116   size_t width;
117
118   /* Number of digits after the decimal point, or INT_MAX if the
119      number can't easily be expressed as a fixed-point number.  */
120   int precision;
121 };
122 typedef struct operand operand;
123
124 /* Read a long double value from the command line.
125    Return if the string is correct else signal error.  */
126
127 static operand
128 scan_arg (const char *arg)
129 {
130   operand ret;
131
132   if (! xstrtold (arg, NULL, &ret.value, c_strtold))
133     {
134       error (0, 0, _("invalid floating point argument: %s"), arg);
135       usage (EXIT_FAILURE);
136     }
137
138   ret.width = strlen (arg);
139   ret.precision = INT_MAX;
140
141   if (! arg[strcspn (arg, "eExX")] && isfinite (ret.value))
142     {
143       char const *decimal_point = strchr (arg, '.');
144       if (! decimal_point)
145         ret.precision = 0;
146       else
147         {
148           size_t fraction_len = strlen (decimal_point + 1);
149           if (fraction_len <= INT_MAX)
150             ret.precision = fraction_len;
151           ret.width += (fraction_len == 0
152                         ? -1
153                         : (decimal_point == arg
154                            || ! ISDIGIT (decimal_point[-1])));
155         }
156     }
157
158   return ret;
159 }
160
161 /* If FORMAT is a valid printf format for a double argument, return
162    its long double equivalent, possibly allocated from dynamic
163    storage; otherwise, return NULL.  */
164
165 static char const *
166 long_double_format (char const *fmt)
167 {
168   size_t i;
169   size_t prefix_len;
170   bool has_L;
171
172   for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
173     if (! fmt[i])
174       return NULL;
175
176   i++;
177   i += strspn (fmt + i, "-+#0 '");
178   i += strspn (fmt + i, "0123456789");
179   if (fmt[i] == '.')
180     {
181       i++;
182       i += strspn (fmt + i, "0123456789");
183     }
184
185   prefix_len = i;
186   has_L = (fmt[i] == 'L');
187   i += has_L;
188   if (! strchr ("efgaEFGA", fmt[i]))
189     return NULL;
190
191   for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
192     if (! fmt[i])
193       {
194         size_t format_size = i + 1;
195         char *ldfmt = xmalloc (format_size + 1);
196         memcpy (ldfmt, fmt, prefix_len);
197         ldfmt[prefix_len] = 'L';
198         strcpy (ldfmt + prefix_len + 1, fmt + prefix_len + has_L);
199         return ldfmt;
200       }
201
202   return NULL;
203 }
204
205 /* Actually print the sequence of numbers in the specified range, with the
206    given or default stepping and format.  */
207
208 static void
209 print_numbers (char const *fmt,
210                long double first, long double step, long double last)
211 {
212   long double i;
213
214   for (i = 0; /* empty */; i++)
215     {
216       long double x = first + i * step;
217       if (step < 0 ? x < last : last < x)
218         break;
219       if (i)
220         fputs (separator, stdout);
221       printf (fmt, x);
222     }
223
224   if (i)
225     fputs (terminator, stdout);
226 }
227
228 /* Return the default format given FIRST, STEP, and LAST.  */
229 static char const *
230 get_default_format (operand first, operand step, operand last)
231 {
232   static char format_buf[sizeof "%0.Lf" + 2 * INT_STRLEN_BOUND (int)];
233
234   int prec = MAX (first.precision, step.precision);
235
236   if (prec != INT_MAX && last.precision != INT_MAX)
237     {
238       if (equal_width)
239         {
240           size_t first_width = first.width + (prec - first.precision);
241           size_t last_width = last.width + (prec - last.precision);
242           if (first.width <= first_width
243               && (last.width < last_width) == (prec < last.precision))
244             {
245               size_t width = MAX (first_width, last_width);
246               if (width <= INT_MAX)
247                 {
248                   int w = width;
249                   sprintf (format_buf, "%%0%d.%dLf", w, prec);
250                   return format_buf;
251                 }
252             }
253         }
254       else
255         {
256           sprintf (format_buf, "%%.%dLf", prec);
257           return format_buf;
258         }
259     }
260
261   return "%Lg";
262 }
263
264 int
265 main (int argc, char **argv)
266 {
267   int optc;
268   operand first = { 1, 1, 0 };
269   operand step = { 1, 1, 0 };
270   operand last;
271
272   /* The printf(3) format used for output.  */
273   char const *format_str = NULL;
274
275   initialize_main (&argc, &argv);
276   program_name = argv[0];
277   setlocale (LC_ALL, "");
278   bindtextdomain (PACKAGE, LOCALEDIR);
279   textdomain (PACKAGE);
280
281   atexit (close_stdout);
282
283   equal_width = false;
284   separator = "\n";
285
286   /* We have to handle negative numbers in the command line but this
287      conflicts with the command line arguments.  So explicitly check first
288      whether the next argument looks like a negative number.  */
289   while (optind < argc)
290     {
291       if (argv[optind][0] == '-'
292           && ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
293         {
294           /* means negative number */
295           break;
296         }
297
298       optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
299       if (optc == -1)
300         break;
301
302       switch (optc)
303         {
304         case 'f':
305           format_str = optarg;
306           break;
307
308         case 's':
309           separator = optarg;
310           break;
311
312         case 'w':
313           equal_width = true;
314           break;
315
316         case_GETOPT_HELP_CHAR;
317
318         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
319
320         default:
321           usage (EXIT_FAILURE);
322         }
323     }
324
325   if (argc - optind < 1)
326     {
327       error (0, 0, _("missing operand"));
328       usage (EXIT_FAILURE);
329     }
330
331   if (3 < argc - optind)
332     {
333       error (0, 0, _("extra operand %s"), quote (argv[optind + 3]));
334       usage (EXIT_FAILURE);
335     }
336
337   if (format_str)
338     {
339       char const *f = long_double_format (format_str);
340       if (! f)
341         {
342           error (0, 0, _("invalid format string: %s"), quote (format_str));
343           usage (EXIT_FAILURE);
344         }
345       format_str = f;
346     }
347
348   last = scan_arg (argv[optind++]);
349
350   if (optind < argc)
351     {
352       first = last;
353       last = scan_arg (argv[optind++]);
354
355       if (optind < argc)
356         {
357           step = last;
358           last = scan_arg (argv[optind++]);
359         }
360     }
361
362   if (format_str != NULL && equal_width)
363     {
364       error (0, 0, _("\
365 format string may not be specified when printing equal width strings"));
366       usage (EXIT_FAILURE);
367     }
368
369   if (format_str == NULL)
370     format_str = get_default_format (first, step, last);
371
372   print_numbers (format_str, first.value, step.value, last.value);
373
374   exit (EXIT_SUCCESS);
375 }