]>
git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/seq.c
f8eec5bbae65587560ea0a7aa57e75c31bdc8cdb
1 /* seq - print sequence of numbers to standard output.
2 Copyright (C) 2018-2020 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 3 of the License, or
7 (at your option) 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, see <https://www.gnu.org/licenses/>. */
17 /* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */
21 #include <sys/types.h>
31 #include "loadables.h"
38 #if defined (PRI_MACROS_BROKEN)
42 #if !defined (PRIdMAX)
44 # define PRIdMAX "lld"
50 #if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN)
51 typedef long double floatmax_t
;
52 # define FLOATMAX_CONV "L"
53 # define strtofltmax strtold
54 # define FLOATMAX_FMT "%Lg"
55 # define FLOATMAX_WFMT "%0.Lf"
56 # define USE_LONG_DOUBLE
58 typedef double floatmax_t
;
59 # define FLOATMAX_CONV ""
60 # define strtofltmax strtod
61 # define FLOATMAX_FMT "%g"
62 # define FLOATMAX_WFMT "%0.f"
64 static floatmax_t getfloatmax
PARAMS((const char *));
65 static char *genformat
PARAMS((floatmax_t
, floatmax_t
, floatmax_t
));
67 #define MAX(a, b) (((a) < (b))? (b) : (a))
69 static int conversion_error
= 0;
71 /* If true print all number with equal width. */
72 static int equal_width
;
74 /* The string used to separate two numbers. */
75 static char const *separator
;
77 /* The string output after all numbers have been output. */
78 static char const terminator
[] = "\n";
80 static char decimal_point
;
82 /* Pretty much the same as the version in builtins/printf.def */
91 ret
= strtofltmax (arg
, &ep
);
95 sh_invalidnum ((char *)arg
);
98 else if (errno
== ERANGE
)
100 builtin_error ("warning: %s: %s", arg
, strerror(ERANGE
));
101 conversion_error
= 1;
110 /* If FORMAT is a valid printf format for a double argument, return
111 its long double equivalent, allocated from dynamic storage. This
112 was written by Ulrich Drepper, taken from coreutils:seq.c */
114 long_double_format (char const *fmt
)
117 size_t length_modifier_offset
;
120 for (i
= 0; ! (fmt
[i
] == '%' && fmt
[i
+ 1] != '%'); i
+= (fmt
[i
] == '%') + 1)
124 builtin_error ("format %s has no %% directive", fmt
);
130 i
+= strspn (fmt
+ i
, "-+#0 '"); /* zero or more flags */
131 i
+= strspn (fmt
+ i
, "0123456789"); /* optional minimum field width */
132 if (fmt
[i
] == '.') /* optional precision */
135 i
+= strspn (fmt
+ i
, "0123456789");
138 length_modifier_offset
= i
; /* optional length modifier */
139 /* we could ignore an 'l' length modifier here */
140 has_L
= (fmt
[i
] == 'L');
145 builtin_error ("format %s ends in %%", fmt
);
157 builtin_error ("format %s has unknown `%%%c' directive", fmt
, fmt
[i
]);
160 for (i
++; ; i
+= (fmt
[i
] == '%') + 1)
161 if (fmt
[i
] == '%' && fmt
[i
+ 1] != '%')
163 builtin_error ("format %s has too many %% directives", fmt
);
166 else if (fmt
[i
] == 0)
168 size_t format_size
= i
+ 1;
169 char *ldfmt
= xmalloc (format_size
+ 1);
170 memcpy (ldfmt
, fmt
, length_modifier_offset
);
171 #ifdef USE_LONG_DOUBLE
172 ldfmt
[length_modifier_offset
] = 'L';
173 strcpy (ldfmt
+ length_modifier_offset
+ 1,
174 fmt
+ length_modifier_offset
+ has_L
);
176 strcpy (ldfmt
+ length_modifier_offset
, fmt
+ length_modifier_offset
);
182 /* Return the number of digits following the decimal point in NUMBUF */
190 if (dp
= strchr (numbuf
, decimal_point
))
191 dp
++; /* skip over decimal point */
192 for (p
= 0; dp
&& *dp
&& ISDIGIT (*dp
); dp
++)
197 /* Return the default format given FIRST, INCR, and LAST. */
199 genformat (first
, incr
, last
)
200 floatmax_t first
, incr
, last
;
202 static char buf
[6 + 2 * INT_STRLEN_BOUND (int)];
203 int wfirst
, wlast
, width
;
204 int iprec
, fprec
, lprec
, prec
;
206 if (equal_width
== 0)
207 return (FLOATMAX_FMT
);
209 /* OK, we have to figure out the largest number of decimal places. This is
210 a little more expensive than using the original strings. */
211 snprintf (buf
, sizeof (buf
), FLOATMAX_FMT
, incr
);
212 iprec
= getprec (buf
);
214 wfirst
= snprintf (buf
, sizeof (buf
), FLOATMAX_FMT
, first
);
215 fprec
= getprec (buf
);
217 prec
= MAX (fprec
, iprec
);
219 wlast
= snprintf (buf
, sizeof (buf
), FLOATMAX_FMT
, last
);
220 lprec
= getprec (buf
);
222 /* increase first width by any increased precision in increment */
223 wfirst
+= (prec
- fprec
);
225 /* adjust last width to use precision from first/incr */
226 wlast
+= (prec
- lprec
);
228 if (lprec
&& prec
== 0)
229 wlast
--; /* no decimal point */
230 if (lprec
== 0 && prec
)
231 wlast
++; /* include decimal point */
232 if (fprec
== 0 && prec
)
233 wfirst
++; /* include decimal point */
235 width
= MAX (wfirst
, wlast
);
237 sprintf (buf
, "%%0%d.%d%sf", width
, prec
, FLOATMAX_CONV
);
239 sprintf (buf
, "%%.%d%sf", prec
, FLOATMAX_CONV
);
245 print_fltseq (fmt
, first
, last
, incr
)
247 floatmax_t first
, last
, incr
;
253 n
= 0; /* iteration counter */
255 for (next
= first
; incr
>= 0 ? (next
<= last
) : (next
>= last
); next
= first
+ n
* incr
)
258 if (*s
&& fputs (s
, stdout
) == EOF
)
259 return (sh_chkwrite (EXECUTION_FAILURE
));
260 if (printf (fmt
, next
) < 0)
261 return (sh_chkwrite (EXECUTION_FAILURE
));
266 if (n
> 0 && fputs (terminator
, stdout
) == EOF
)
267 return (sh_chkwrite (EXECUTION_FAILURE
));
268 return (sh_chkwrite (EXECUTION_SUCCESS
));
271 /* must be <= INT_STRLEN_BOUND(intmax_t) */
278 ret
= num
< 0; /* sign */
288 print_intseq (ifirst
, ilast
, iincr
)
289 intmax_t ifirst
, ilast
, iincr
;
291 char intwfmt
[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX
)];
295 /* compute integer format string */
296 if (equal_width
) /* -w supplied */
298 int wfirst
, wlast
, width
;
300 wfirst
= width_needed (ifirst
);
301 wlast
= width_needed (ilast
);
302 width
= MAX(wfirst
, wlast
);
304 /* The leading %s is for the separator */
305 snprintf (intwfmt
, sizeof (intwfmt
), "%%s%%0%u" PRIdMAX
, width
);
308 /* We could use braces.c:mkseq here but that allocates lots of memory */
310 for (i
= ifirst
; (ifirst
<= ilast
) ? (i
<= ilast
) : (i
>= ilast
); i
= next
)
313 /* The leading %s is for the separator */
314 if (printf (equal_width
? intwfmt
: "%s%" PRIdMAX
, s
, i
) < 0)
315 return (sh_chkwrite (EXECUTION_FAILURE
));
320 if (fputs (terminator
, stdout
) == EOF
)
321 return (sh_chkwrite (EXECUTION_FAILURE
));
322 return (sh_chkwrite (EXECUTION_SUCCESS
));
329 floatmax_t first
, last
, incr
;
330 intmax_t ifirst
, ilast
, iincr
;
332 int opt
, nargs
, intseq
, freefmt
;
333 char *first_str
, *incr_str
, *last_str
;
334 char const *fmtstr
; /* The printf(3) format used for output. */
342 incr
= 0.0; /* set later */
343 ifirst
= ilast
= iincr
= 0;
344 first_str
= incr_str
= last_str
= 0;
346 intseq
= freefmt
= 0;
349 reset_internal_getopt ();
352 l
= lcurrent
? lcurrent
: list
;
353 if (l
&& l
->word
&& l
->word
->word
&& l
->word
->word
[0] == '-' &&
354 (l
->word
->word
[1] == '.' || DIGIT (l
->word
->word
[1])))
357 break; /* negative number */
359 if ((opt
= internal_getopt (list
, "f:s:w")) == -1)
365 fmtstr
= list_optarg
;
368 separator
= list_optarg
;
384 return (EXECUTION_FAILURE
);
387 for (nargs
= 1, l
= list
; l
->next
; l
= l
->next
)
392 return (EXECUTION_FAILURE
);
396 conversion_error
= 0;
397 last
= getfloatmax (last_str
= l
->word
->word
);
398 if (conversion_error
)
399 return (EXECUTION_FAILURE
);
404 conversion_error
= 0;
405 first
= getfloatmax (first_str
= list
->word
->word
);
406 if (conversion_error
)
407 return (EXECUTION_FAILURE
);
410 /* FIRST INCR LAST */
413 conversion_error
= 0;
414 incr
= getfloatmax (incr_str
= list
->next
->word
->word
);
415 if (conversion_error
)
416 return (EXECUTION_FAILURE
);
419 builtin_error ("zero %screment", (first
< last
) ? "in" : "de");
420 return (EXECUTION_FAILURE
);
424 /* Sanitize arguments */
426 incr
= (first
<= last
) ? 1.0 : -1.0;
427 if ((incr
< 0.0 && first
< last
) || (incr
> 0 && first
> last
))
429 builtin_error ("incorrect %screment", (first
< last
) ? "in" : "de");
430 return (EXECUTION_FAILURE
);
433 /* validate format here */
436 fmtstr
= long_double_format (fmtstr
);
439 return (EXECUTION_FAILURE
);
442 if (fmtstr
!= NULL
&& equal_width
)
444 builtin_warning ("-w ignored when the format string is specified");
448 /* Placeholder for later additional conditions */
449 if (last_str
&& all_digits (last_str
) &&
450 (first_str
== 0 || all_digits (first_str
)) &&
451 (incr_str
== 0 || all_digits (incr_str
)) &&
457 ifirst
= (intmax_t)first
; /* truncation */
458 ilast
= (intmax_t)last
;
459 iincr
= (intmax_t)incr
;
461 return (print_intseq (ifirst
, ilast
, iincr
));
464 decimal_point
= locale_decpoint ();
466 fmtstr
= genformat (first
, incr
, last
);
468 print_fltseq (fmtstr
, first
, last
, incr
);
471 free ((void *)fmtstr
);
472 return sh_chkwrite (EXECUTION_SUCCESS
);
475 /* Taken largely from GNU seq. */
477 "Print numbers from FIRST to LAST, in steps of INCREMENT.",
479 "-f FORMAT use printf style floating-point FORMAT",
480 "-s STRING use STRING to separate numbers (default: \\n)",
481 "-w equalize width by padding with leading zeroes",
483 "If FIRST or INCREMENT is omitted, it defaults to 1. However, an",
484 "omitted INCREMENT defaults to -1 when LAST is smaller than FIRST.",
485 "The sequence of numbers ends when the sum of the current number and",
486 "INCREMENT would become greater than LAST.",
487 "FIRST, INCREMENT, and LAST are interpreted as floating point values.",
489 "FORMAT must be suitable for printing one argument of type 'double';",
490 "it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point",
491 "decimal numbers with maximum precision PREC, and to %g otherwise.",
495 struct builtin seq_struct
= {
500 "seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST",