1 /* seq.c - Count from first to last, by increment.
2 *
3 * Copyright 2006 Rob Landley <[email protected]>
4 *
5 * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/seq.html
6
7 USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
8
9 config SEQ
10 bool "seq"
11 depends on TOYBOX_FLOAT
12 default y
13 help
14 usage: seq [-w|-f fmt_str] [-s sep_str] [first] [increment] last
15
16 Count from first to last, by increment. Omitted arguments default
17 to 1. Two arguments are used as first and last. Arguments can be
18 negative or floating point.
19
20 -f Use fmt_str as a printf-style floating point format string
21 -s Use sep_str as separator, default is a newline character
22 -w Pad to equal width with leading zeroes
23 */
24
25 #define FOR_seq
26 #include "toys.h"
27
28 GLOBALS(
29 char *s, *f;
30
31 int precision, buflen;
32 )
33
34 // Ensure there's one %f escape with correct attributes
insanitize(char * f)35 static void insanitize(char *f)
36 {
37 char *s = next_printf(f, 0);
38
39 if (!s) error_exit("bad -f no %%f");
40 if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0)))
41 error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
42 }
43
44 // Parse a numeric argument setting *prec to the precision of this argument.
45 // This reproduces the "1.234e5" precision bug from upstream.
parsef(char * s)46 static double parsef(char *s)
47 {
48 char *dp = strchr(s, '.');
49
50 if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE"));
51
52 return xstrtod(s);
53 }
54
flush_toybuf(char * ss)55 static char *flush_toybuf(char *ss)
56 {
57 if (ss-toybuf<TT.buflen) return ss;
58 xwrite(1, toybuf, ss-toybuf);
59
60 return toybuf;
61 }
62
seq_main(void)63 void seq_main(void)
64 {
65 char fbuf[64], *ss;
66 double first = 1, increment = 1, last, dd;
67 long ii, inc = 1, len, slen;
68
69 // parse arguments
70 if (!TT.s) TT.s = "\n";
71 switch (toys.optc) {
72 case 3: increment = parsef(toys.optargs[1]);
73 case 2: first = parsef(*toys.optargs);
74 default: last = parsef(toys.optargs[toys.optc-1]);
75 }
76
77 // measure arguments
78 if (FLAG(f)) insanitize(TT.f);
79 for (ii = len = 0; ii<3; ii++) {
80 dd = (double []){first, increment, last}[ii];
81 len = maxof(len, snprintf(0, 0, "%.*f", TT.precision, fabs(dd)));
82 if (ii == 2) dd += increment;
83 slen = dd;
84 if (dd != slen) inc = 0;
85 }
86 if (!FLAG(f)) sprintf(TT.f = fbuf, "%%0%ld.%df", len, TT.precision);
87 TT.buflen = sizeof(toybuf)-sizeof(fbuf)-len-TT.precision-strlen(TT.s);
88 if (TT.buflen<0) error_exit("bad -s");
89
90 // fast path: when everything fits in a long with no flags.
91 if (!toys.optflags && inc) {
92 ii = first;
93 len = last;
94 inc = increment;
95 ss = toybuf;
96 if (inc>0) for (; ii<=len; ii += inc)
97 ss = flush_toybuf(ss+sprintf(ss, "%ld\n", ii));
98 else if (inc<0) for (; ii>=len; ii += inc)
99 ss = flush_toybuf(ss+sprintf(ss, "%ld\n", ii));
100 if (ss != toybuf) xwrite(1, toybuf, ss-toybuf);
101
102 return;
103 }
104
105 // Other implementations output nothing if increment is 0 and first > last,
106 // but loop forever if first < last or even first == last. We output
107 // nothing for all three, if you want endless output use "yes".
108 if (!increment) return;
109
110 // Slow path, floating point and fancy sprintf() patterns
111 for (ii = 0, ss = toybuf;; ii++) {
112 // Multiply to avoid accumulating rounding errors from increment.
113 dd = first+ii*increment;
114 if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
115 if (ii) ss = flush_toybuf(stpcpy(ss, TT.s));
116 ss += sprintf(ss, TT.f, dd);
117 }
118 *ss++ = '\n';
119 xwrite(1, toybuf, ss-toybuf);
120 }
121