(TESTS): Add no-x.
[coreutils.git] / src / seq.c
blob58bb59b4a0f6eb31af9420daea541ad1a15c298e
1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 1994-2002 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Written by Ulrich Drepper. */
20 #include <config.h>
21 #include <getopt.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <sys/types.h>
26 #include "system.h"
27 #include "closeout.h"
28 #include "error.h"
29 #include "xstrtol.h"
30 #include "xstrtod.h"
32 /* The official name of this program (e.g., no `g' prefix). */
33 #define PROGRAM_NAME "seq"
35 #define AUTHORS "Ulrich Drepper"
37 /* If nonzero print all number with equal width. */
38 static int equal_width;
40 /* The name that this program was run with. */
41 char *program_name;
43 /* The string used to separate two numbers. */
44 static char *separator;
46 /* The string output after all numbers have been output.
47 Usually "\n" or "\0". */
48 /* FIXME: make this an option. */
49 static char *terminator = "\n";
51 /* The representation of the decimal point in the current locale.
52 Always "." if the localeconv function is not supported. */
53 static char *decimal_point = ".";
55 /* The starting number. */
56 static double first;
58 /* The increment. */
59 static double step;
61 /* The last number. */
62 static double last;
64 static struct option const long_options[] =
66 { "equal-width", no_argument, NULL, 'w'},
67 { "format", required_argument, NULL, 'f'},
68 { "separator", required_argument, NULL, 's'},
69 {GETOPT_HELP_OPTION_DECL},
70 {GETOPT_VERSION_OPTION_DECL},
71 { NULL, 0, NULL, 0}
74 void
75 usage (int status)
77 if (status != 0)
78 fprintf (stderr, _("Try `%s --help' for more information.\n"),
79 program_name);
80 else
82 printf (_("\
83 Usage: %s [OPTION]... LAST\n\
84 or: %s [OPTION]... FIRST LAST\n\
85 or: %s [OPTION]... FIRST INCREMENT LAST\n\
86 "), program_name, program_name, program_name);
87 fputs (_("\
88 Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
89 \n\
90 -f, --format=FORMAT use printf style floating-point FORMAT (default: %g)\n\
91 -s, --separator=STRING use STRING to separate numbers (default: \\n)\n\
92 -w, --equal-width equalize width by padding with leading zeroes\n\
93 "), stdout);
94 fputs (HELP_OPTION_DESCRIPTION, stdout);
95 fputs (VERSION_OPTION_DESCRIPTION, stdout);
96 fputs (_("\
97 \n\
98 If FIRST or INCREMENT is omitted, it defaults to 1.\n\
99 FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
100 INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
101 otherwise. When given, the FORMAT argument must contain exactly one of\n\
102 the printf-style, floating point output formats %e, %f, %g\n\
103 "), stdout);
104 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
106 exit (status);
109 /* Read a double value from the command line.
110 Return if the string is correct else signal error. */
112 static double
113 scan_double_arg (const char *arg)
115 double ret_val;
117 if (xstrtod (arg, NULL, &ret_val))
119 error (0, 0, _("invalid floating point argument: %s"), arg);
120 usage (EXIT_FAILURE);
123 return ret_val;
126 /* Check whether the format string is valid for a single `double'
127 argument. Return 0 if not, 1 if correct. */
129 static int
130 valid_format (const char *fmt)
132 while (*fmt != '\0')
134 if (*fmt == '%')
136 fmt++;
137 if (*fmt != '%')
138 break;
141 fmt++;
143 if (*fmt == '\0')
144 return 0;
146 fmt += strspn (fmt, "-+#0");
147 if (ISDIGIT (*fmt))
149 fmt += strspn (fmt, "0123456789");
151 if (*fmt == '.')
153 ++fmt;
154 fmt += strspn (fmt, "0123456789");
158 if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
159 return 0;
161 fmt++;
162 while (*fmt != '\0')
164 if (*fmt == '%')
166 fmt++;
167 if (*fmt != '%')
168 return 0;
171 fmt++;
174 return 1;
177 /* Actually print the sequence of numbers in the specified range, with the
178 given or default stepping and format. */
179 static int
180 print_numbers (const char *fmt)
182 if (first > last)
184 int i;
186 if (step >= 0)
188 error (0, 0,
189 _("when the starting value is larger than the limit,\n\
190 the increment must be negative"));
191 usage (EXIT_FAILURE);
194 printf (fmt, first);
195 for (i = 1; /* empty */; i++)
197 double x = first + i * step;
199 if (x < last)
200 break;
202 fputs (separator, stdout);
203 printf (fmt, x);
206 else
208 int i;
210 if (step <= 0)
212 error (0, 0,
213 _("when the starting value is smaller than the limit,\n\
214 the increment must be positive"));
215 usage (EXIT_FAILURE);
218 printf (fmt, first);
219 for (i = 1; /* empty */; i++)
221 double x = first + i * step;
223 if (x > last)
224 break;
226 fputs (separator, stdout);
227 printf (fmt, x);
230 fputs (terminator, stdout);
232 return 0;
235 #if HAVE_RINT && HAVE_MODF && HAVE_FLOOR
237 /* Return a printf-style format string with which all selected numbers
238 will format to strings of the same width. */
240 static char *
241 get_width_format ()
243 static char buffer[256];
244 int full_width;
245 int frac_width;
246 int width1, width2;
247 double max_val;
248 double min_val;
249 double temp;
251 if (first > last)
253 min_val = first - step * floor ((first - last) / step);
254 max_val = first;
256 else
258 min_val = first;
259 max_val = first + step * floor ((last - first) / step);
262 sprintf (buffer, "%g", rint (max_val));
263 if (buffer[strspn (buffer, "0123456789")] != '\0')
264 return "%g";
265 width1 = strlen (buffer);
267 if (min_val < 0.0)
269 double int_min_val = rint (min_val);
270 sprintf (buffer, "%g", int_min_val);
271 if (buffer[strspn (buffer, "-0123456789")] != '\0')
272 return "%g";
273 /* On some systems, `seq -w -.1 .1 .1' results in buffer being `-0'.
274 On others, it is just `0'. The former results in better output. */
275 width2 = (int_min_val == 0 ? 2 : strlen (buffer));
277 width1 = width1 > width2 ? width1 : width2;
279 full_width = width1;
281 sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
282 width1 = strlen (buffer);
283 if (width1 == 1)
284 width1 = 0;
285 else
287 if (buffer[0] != '1'
288 /* FIXME: assumes that decimal_point is a single character
289 string. */
290 || buffer[1] != decimal_point[0]
291 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
292 return "%g";
293 width1 -= 2;
296 sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
297 width2 = strlen (buffer);
298 if (width2 == 1)
299 width2 = 0;
300 else
302 if (buffer[0] != '1'
303 /* FIXME: assumes that decimal_point is a single character
304 string. */
305 || buffer[1] != decimal_point[0]
306 || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
307 return "%g";
308 width2 -= 2;
310 frac_width = width1 > width2 ? width1 : width2;
312 if (frac_width)
313 sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
314 else
315 sprintf (buffer, "%%0%dg", full_width);
317 return buffer;
320 #else /* one of the math functions rint, modf, floor is missing. */
322 static char *
323 get_width_format (void)
325 /* We cannot compute the needed information to determine the correct
326 answer. So we simply return a value that works for all cases. */
327 return "%g";
330 #endif
333 main (int argc, char **argv)
335 int errs;
336 int optc;
337 int step_is_set;
339 /* The printf(3) format used for output. */
340 char *format_str = NULL;
342 program_name = argv[0];
343 setlocale (LC_ALL, "");
344 bindtextdomain (PACKAGE, LOCALEDIR);
345 textdomain (PACKAGE);
347 atexit (close_stdout);
349 equal_width = 0;
350 separator = "\n";
351 first = 1.0;
352 step_is_set = 0;
354 /* Figure out the locale's idea of a decimal point. */
355 #if HAVE_LOCALECONV
357 struct lconv *locale;
359 locale = localeconv ();
360 /* Paranoia. */
361 if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
362 decimal_point = locale->decimal_point;
364 #endif
366 /* We have to handle negative numbers in the command line but this
367 conflicts with the command line arguments. So explicitly check first
368 whether the next argument looks like a negative number. */
369 while (optind < argc)
371 if (argv[optind][0] == '-'
372 && ((optc = argv[optind][1]) == decimal_point[0]
373 || ISDIGIT (optc)))
375 /* means negative number */
376 break;
379 optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
380 if (optc == -1)
381 break;
383 switch (optc)
385 case 0:
386 break;
388 case 'f':
389 format_str = optarg;
390 break;
392 case 's':
393 separator = optarg;
394 break;
396 case 'w':
397 equal_width = 1;
398 break;
400 case_GETOPT_HELP_CHAR;
402 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
404 default:
405 usage (EXIT_FAILURE);
409 if (argc - optind < 1)
411 error (0, 0, _("too few arguments"));
412 usage (EXIT_FAILURE);
415 if (3 < argc - optind)
417 error (0, 0, _("too many arguments"));
418 usage (EXIT_FAILURE);
421 if (format_str && !valid_format (format_str))
423 error (0, 0, _("invalid format string: `%s'"), format_str);
424 usage (EXIT_FAILURE);
427 last = scan_double_arg (argv[optind++]);
429 if (optind < argc)
431 first = last;
432 last = scan_double_arg (argv[optind++]);
434 if (optind < argc)
436 step = last;
437 step_is_set = 1;
438 last = scan_double_arg (argv[optind++]);
443 if (format_str != NULL && equal_width)
445 error (0, 0, _("\
446 format string may not be specified when printing equal width strings"));
447 usage (EXIT_FAILURE);
450 if (!step_is_set)
452 step = first <= last ? 1.0 : -1.0;
455 if (format_str == NULL)
457 if (equal_width)
458 format_str = get_width_format ();
459 else
460 format_str = "%g";
463 errs = print_numbers (format_str);
465 exit (errs);