(usage): Include one- or two-line synopsis in --help output.
[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 /* Ulrich Drepper */
19
20 #include <getopt.h>
21 #include <math.h>
22 #include <stdio.h>
23
24 #include "system.h"
25 #include "error.h"
26 #include "version.h"
27
28 static double scan_double_arg ();
29 static int check_format ();
30 static char *get_width_format ();
31 static int print_numbers ();
32
33 /* If non-zero print all number with equal width. */
34 static int equal_width;
35
36 /* The printf(3) format used for output. */
37 static char *format_str;
38
39 /* The starting number. */
40 static double from;
41
42 /* The name that this program was run with. */
43 char *program_name;
44
45 /* The string used to separate two number. */
46 static char *separator;
47
48 /* If non-zero, display usage information and exit.  */
49 static int show_help;
50
51 /* If non-zero, print the version on standard output and exit.  */
52 static int show_version;
53
54 /* The increment. */
55 static double step;
56
57 /* The last number. */
58 static double last;
59
60 static struct option const long_options[] =
61 {
62   { "equal-width", no_argument, NULL, 'w'},
63   { "format", required_argument, NULL, 'f'},
64   { "help", no_argument, &show_help, 1},
65   { "separator", required_argument, NULL, 's'},
66   { "version", no_argument, &show_version, 1},
67   { NULL, 0, NULL, 0}
68 };
69
70 static void
71 usage (status)
72      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 (argc, argv)
100      int argc;
101      char **argv;
102 {
103   int errs;
104   int optc;
105   int step_is_set;
106
107   program_name = argv[0];
108   equal_width = 0;
109   format_str = NULL;
110   separator = "\n";
111   from = 1.0;
112   step_is_set = 0;
113
114   /* We have to handle negative numbers in the command line but this 
115      conflicts with the command line arguments.  So the getopt mode is
116      REQUIRE_ORDER (the '+' in the format string) and it abort on the
117      first non-option or negative number.  */
118   while ((optc = getopt_long (argc, argv, "+0123456789f:s:w", long_options,
119                               (int *) 0)) != EOF)
120     {
121       if ('0' <= optc && optc <= '9')
122         {
123           /* means negative number */
124           break;
125         }
126
127       switch (optc)
128         {
129         case 0:
130           break;
131
132         case 'f':
133           format_str = optarg;
134           break;
135
136         case 's':
137           separator = optarg;
138           break;
139
140         case 'w':
141           equal_width = 1;
142           break;
143
144         default:
145           usage (1);
146           /* NOTREACHED */
147         }
148     }
149
150   if (show_version)
151     {
152       (void) printf ("seq - %s\n", version_string);
153     }
154
155   if (show_help)
156     {
157       usage (0);
158       /* NOTREACHED */
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       from = 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 = from <= 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 (arg)
227      char *arg;
228 {
229   char *end_ptr;
230   double ret_val;
231
232   /* FIXME: use xstrtod?  At least set and check errno.  */
233   ret_val = strtod (arg, &end_ptr);
234   if (end_ptr == arg || *end_ptr != '\0')
235     {
236       error (0, 0, "invalid float argument: %s", arg);
237       usage (1);
238       /* NOTREACHED */
239     }
240
241   return ret_val;
242 }
243
244 /* Check whether the format string is valid for a single double
245    argument.
246    Return 0 if not, 1 if correct.  */
247
248 static int
249 check_format (format_string)
250      char *format_string;
251 {
252   while (*format_string != '\0')
253     {
254       if (*format_string == '%')
255         {
256           format_string++;
257           if (*format_string != '%')
258             break;
259         }
260
261       format_string++;
262     }
263   if (*format_string == '\0')
264     return 0;
265
266   format_string += strspn (format_string, "-+#0");
267   if (isdigit (*format_string))
268     {
269       format_string += strspn (format_string, "012345789");
270
271       if (*format_string == '.')
272         format_string += strspn (++format_string, "0123456789");
273     }
274
275   if (*format_string != 'e' && *format_string != 'f' &&
276       *format_string != 'g')
277     return 0;
278
279   format_string++;
280   while (*format_string != '\0')
281     {
282       if (*format_string == '%')
283         {
284           format_string++;
285           if (*format_string != '%')
286             return 0;
287         }
288
289       format_string++;
290     }
291
292   return 1;
293 }
294
295 #if defined (HAVE_RINT) && defined (HAVE_MODF) && defined (HAVE_FLOOR)
296
297 /* Return a printf-style format string with which all selected numbers
298    will format to strings of the same width.  */
299
300 static char *
301 get_width_format ()
302 {
303   static char buffer[256];
304   int full_width;
305   int frac_width;
306   int width1, width2;
307   double max_val;
308   double min_val;
309   double temp;
310
311   if (from > last)
312     {
313       min_val = from - step * floor ((from - last) / step);
314       max_val = from;
315     }
316   else
317     {
318       min_val = from;
319       max_val = from + step * floor ((last - from) / step);
320     }
321
322   (void) sprintf (buffer, "%g", rint (max_val));
323   if (buffer[strspn (buffer, "0123456789")] != '\0')
324     return "%g";
325   width1 = strlen (buffer);
326
327   if (min_val < 0.0)
328     {
329       (void) sprintf (buffer, "%g", rint (min_val));
330       if (buffer[strspn (buffer, "-0123456789")] != '\0')
331         return "%g";
332       width2 = strlen (buffer);
333
334       width1 = width1 > width2 ? width1 : width2;
335     }
336   full_width = width1;
337
338   (void) sprintf (buffer, "%g", 1.0 + modf (min_val, &temp));
339   width1 = strlen (buffer);
340   if (width1 == 1)
341     width1 = 0;
342   else
343     {
344       if (buffer[0] != '1' || buffer[1] != '.' ||
345           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
346         return "%g";
347       width1 -= 2;
348     }
349
350   (void) sprintf (buffer, "%g", 1.0 + modf (step, &temp));
351   width2 = strlen (buffer);
352   if (width2 == 1)
353     width2 = 0;
354   else
355     {
356       if (buffer[0] != '1' || buffer[1] != '.' ||
357           buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
358         return "%g";
359       width2 -= 2;
360     }
361   frac_width = width1 > width2 ? width1 : width2;
362
363   if (frac_width)
364     (void) sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
365   else
366     (void) sprintf (buffer, "%%0%dg", full_width);
367
368   return buffer;
369 }
370
371 #else   /* one of the math functions rint, modf, floor is missing.  */
372
373 static char *
374 get_width_format ()
375 {
376   /* We cannot compute the needed information to determine the correct
377      answer.  So we simply return a value that works for all cases.  */
378   return "%g";
379 }
380
381 #endif
382
383 /* Actually print the sequence of numbers in the specified range, with the
384    given or default stepping and format.  */
385 static int
386 print_numbers (format_str)
387      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 }