]> git.ipfire.org Git - thirdparty/bash.git/blame_incremental - examples/loadables/seq.c
updated translations; remove unneeded files
[thirdparty/bash.git] / examples / loadables / seq.c
... / ...
CommitLineData
1/* seq - print sequence of numbers to standard output.
2 Copyright (C) 2018-2020 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 3 of the License, or
7 (at your option) 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, see <https://www.gnu.org/licenses/>. */
16
17/* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */
18
19#include <config.h>
20
21#include <sys/types.h>
22
23#ifdef HAVE_UNISTD_H
24# include <unistd.h>
25#endif
26
27#include <stdio.h>
28#include <errno.h>
29
30#include "bashansi.h"
31#include "loadables.h"
32#include "bashintl.h"
33
34#ifndef errno
35extern int errno;
36#endif
37
38#if defined (PRI_MACROS_BROKEN)
39# undef PRIdMAX
40#endif
41
42#if !defined (PRIdMAX)
43# if HAVE_LONG_LONG
44# define PRIdMAX "lld"
45# else
46# define PRIdMAX "ld"
47# endif
48#endif
49
50#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN)
51typedef 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
57#else
58typedef double floatmax_t;
59# define FLOATMAX_CONV ""
60# define strtofltmax strtod
61# define FLOATMAX_FMT "%g"
62# define FLOATMAX_WFMT "%0.f"
63#endif
64static floatmax_t getfloatmax PARAMS((const char *));
65static char *genformat PARAMS((floatmax_t, floatmax_t, floatmax_t));
66
67#define MAX(a, b) (((a) < (b))? (b) : (a))
68
69static int conversion_error = 0;
70
71/* If true print all number with equal width. */
72static int equal_width;
73
74/* The string used to separate two numbers. */
75static char const *separator;
76
77/* The string output after all numbers have been output. */
78static char const terminator[] = "\n";
79
80static char decimal_point;
81
82/* Pretty much the same as the version in builtins/printf.def */
83static floatmax_t
84getfloatmax (arg)
85 const char *arg;
86{
87 floatmax_t ret;
88 char *ep;
89
90 errno = 0;
91 ret = strtofltmax (arg, &ep);
92
93 if (*ep)
94 {
95 sh_invalidnum ((char *)arg);
96 conversion_error = 1;
97 }
98 else if (errno == ERANGE)
99 {
100 builtin_error ("warning: %s: %s", arg, strerror(ERANGE));
101 conversion_error = 1;
102 }
103
104 if (ret == -0.0)
105 ret = 0.0;
106
107 return (ret);
108}
109
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 */
113static char *
114long_double_format (char const *fmt)
115{
116 size_t i;
117 size_t length_modifier_offset;
118 int has_L;
119
120 for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
121 {
122 if (!fmt[i])
123 {
124 builtin_error ("format %s has no %% directive", fmt);
125 return 0;
126 }
127 }
128
129 i++;
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 */
133 {
134 i++;
135 i += strspn (fmt + i, "0123456789");
136 }
137
138 length_modifier_offset = i; /* optional length modifier */
139 /* we could ignore an 'l' length modifier here */
140 has_L = (fmt[i] == 'L');
141 i += has_L;
142 switch (fmt[i])
143 {
144 case '\0':
145 builtin_error ("format %s ends in %%", fmt);
146 return 0;
147 case 'A':
148 case 'a':
149 case 'e':
150 case 'E':
151 case 'f':
152 case 'F':
153 case 'g':
154 case 'G':
155 break;
156 default:
157 builtin_error ("format %s has unknown `%%%c' directive", fmt, fmt[i]);
158 return 0;
159 }
160 for (i++; ; i += (fmt[i] == '%') + 1)
161 if (fmt[i] == '%' && fmt[i + 1] != '%')
162 {
163 builtin_error ("format %s has too many %% directives", fmt);
164 return 0;
165 }
166 else if (fmt[i] == 0)
167 {
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);
175#else
176 strcpy (ldfmt + length_modifier_offset, fmt + length_modifier_offset);
177#endif
178 return ldfmt;
179 }
180}
181
182/* Return the number of digits following the decimal point in NUMBUF */
183static int
184getprec (numbuf)
185 const char *numbuf;
186{
187 int p;
188 char *dp;
189
190 if (dp = strchr (numbuf, decimal_point))
191 dp++; /* skip over decimal point */
192 for (p = 0; dp && *dp && ISDIGIT (*dp); dp++)
193 p++;
194 return p;
195}
196
197/* Return the default format given FIRST, INCR, and LAST. */
198static char *
199genformat (first, incr, last)
200 floatmax_t first, incr, last;
201{
202 static char buf[6 + 2 * INT_STRLEN_BOUND (int)];
203 int wfirst, wlast, width;
204 int iprec, fprec, lprec, prec;
205
206 if (equal_width == 0)
207 return (FLOATMAX_FMT);
208
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);
213
214 wfirst = snprintf (buf, sizeof (buf), FLOATMAX_FMT, first);
215 fprec = getprec (buf);
216
217 prec = MAX (fprec, iprec);
218
219 wlast = snprintf (buf, sizeof (buf), FLOATMAX_FMT, last);
220 lprec = getprec (buf);
221
222 /* increase first width by any increased precision in increment */
223 wfirst += (prec - fprec);
224
225 /* adjust last width to use precision from first/incr */
226 wlast += (prec - lprec);
227
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 */
234
235 width = MAX (wfirst, wlast);
236 if (width)
237 sprintf (buf, "%%0%d.%d%sf", width, prec, FLOATMAX_CONV);
238 else
239 sprintf (buf, "%%.%d%sf", prec, FLOATMAX_CONV);
240
241 return buf;
242}
243
244int
245print_fltseq (fmt, first, last, incr)
246 const char *fmt;
247 floatmax_t first, last, incr;
248{
249 int n;
250 floatmax_t next;
251 const char *s;
252
253 n = 0; /* iteration counter */
254 s = "";
255 for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr)
256 {
257 QUIT;
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));
262 s = separator;
263 n++;
264 }
265
266 if (n > 0 && fputs (terminator, stdout) == EOF)
267 return (sh_chkwrite (EXECUTION_FAILURE));
268 return (sh_chkwrite (EXECUTION_SUCCESS));
269}
270
271/* must be <= INT_STRLEN_BOUND(intmax_t) */
272int
273width_needed (num)
274 intmax_t num;
275{
276 int ret;
277
278 ret = num < 0; /* sign */
279 if (ret)
280 num = -num;
281 do
282 ret++;
283 while (num /= 10);
284 return ret;
285}
286
287int
288print_intseq (ifirst, ilast, iincr)
289 intmax_t ifirst, ilast, iincr;
290{
291 char intwfmt[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX)];
292 const char *s;
293 intmax_t i, next;
294
295 /* compute integer format string */
296 if (equal_width) /* -w supplied */
297 {
298 int wfirst, wlast, width;
299
300 wfirst = width_needed (ifirst);
301 wlast = width_needed (ilast);
302 width = MAX(wfirst, wlast);
303
304 /* The leading %s is for the separator */
305 snprintf (intwfmt, sizeof (intwfmt), "%%s%%0%u" PRIdMAX, width);
306 }
307
308 /* We could use braces.c:mkseq here but that allocates lots of memory */
309 s = "";
310 for (i = ifirst; (ifirst <= ilast) ? (i <= ilast) : (i >= ilast); i = next)
311 {
312 QUIT;
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));
316 s = separator;
317 next = i + iincr;
318 }
319
320 if (fputs (terminator, stdout) == EOF)
321 return (sh_chkwrite (EXECUTION_FAILURE));
322 return (sh_chkwrite (EXECUTION_SUCCESS));
323}
324
325int
326seq_builtin (list)
327 WORD_LIST *list;
328{
329 floatmax_t first, last, incr;
330 intmax_t ifirst, ilast, iincr;
331 WORD_LIST *l;
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. */
335
336 equal_width = 0;
337 separator = "\n";
338 fmtstr = NULL;
339
340 first = 1.0;
341 last = 0.0;
342 incr = 0.0; /* set later */
343 ifirst = ilast = iincr = 0;
344 first_str = incr_str = last_str = 0;
345
346 intseq = freefmt = 0;
347 opt = 0;
348
349 reset_internal_getopt ();
350 while (opt != -1)
351 {
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])))
355 {
356 loptend = l;
357 break; /* negative number */
358 }
359 if ((opt = internal_getopt (list, "f:s:w")) == -1)
360 break;
361
362 switch (opt)
363 {
364 case 'f':
365 fmtstr = list_optarg;
366 break;
367 case 's':
368 separator = list_optarg;
369 break;
370 case 'w':
371 equal_width = 1;
372 break;
373 CASE_HELPOPT;
374 default:
375 builtin_usage ();
376 return (EX_USAGE);
377 }
378 }
379 list = loptend;
380
381 if (list == 0)
382 {
383 builtin_usage ();
384 return (EXECUTION_FAILURE);
385 }
386
387 for (nargs = 1, l = list; l->next; l = l->next)
388 nargs++;
389 if (nargs > 3)
390 {
391 builtin_usage ();
392 return (EXECUTION_FAILURE);
393 }
394
395 /* LAST */
396 conversion_error = 0;
397 last = getfloatmax (last_str = l->word->word);
398 if (conversion_error)
399 return (EXECUTION_FAILURE);
400
401 /* FIRST LAST */
402 if (nargs > 1)
403 {
404 conversion_error = 0;
405 first = getfloatmax (first_str = list->word->word);
406 if (conversion_error)
407 return (EXECUTION_FAILURE);
408 }
409
410 /* FIRST INCR LAST */
411 if (nargs > 2)
412 {
413 conversion_error = 0;
414 incr = getfloatmax (incr_str = list->next->word->word);
415 if (conversion_error)
416 return (EXECUTION_FAILURE);
417 if (incr == 0.0)
418 {
419 builtin_error ("zero %screment", (first < last) ? "in" : "de");
420 return (EXECUTION_FAILURE);
421 }
422 }
423
424 /* Sanitize arguments */
425 if (incr == 0.0)
426 incr = (first <= last) ? 1.0 : -1.0;
427 if ((incr < 0.0 && first < last) || (incr > 0 && first > last))
428 {
429 builtin_error ("incorrect %screment", (first < last) ? "in" : "de");
430 return (EXECUTION_FAILURE);
431 }
432
433 /* validate format here */
434 if (fmtstr)
435 {
436 fmtstr = long_double_format (fmtstr);
437 freefmt = 1;
438 if (fmtstr == 0)
439 return (EXECUTION_FAILURE);
440 }
441
442 if (fmtstr != NULL && equal_width)
443 {
444 builtin_warning ("-w ignored when the format string is specified");
445 equal_width = 0;
446 }
447
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)) &&
452 fmtstr == NULL)
453 intseq = 1;
454
455 if (intseq)
456 {
457 ifirst = (intmax_t)first; /* truncation */
458 ilast = (intmax_t)last;
459 iincr = (intmax_t)incr;
460
461 return (print_intseq (ifirst, ilast, iincr));
462 }
463
464 decimal_point = locale_decpoint ();
465 if (fmtstr == NULL)
466 fmtstr = genformat (first, incr, last);
467
468 print_fltseq (fmtstr, first, last, incr);
469
470 if (freefmt)
471 free ((void *)fmtstr);
472 return sh_chkwrite (EXECUTION_SUCCESS);
473}
474
475/* Taken largely from GNU seq. */
476char *seq_doc[] = {
477 "Print numbers from FIRST to LAST, in steps of INCREMENT.",
478 "",
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",
482 "",
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.",
488 "",
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.",
492 (char *)NULL
493};
494
495struct builtin seq_struct = {
496 "seq",
497 seq_builtin,
498 BUILTIN_ENABLED,
499 seq_doc,
500 "seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST",
501 0
502};