1 /* seq.c - Count from first to last, by increment.
2  *
3  * Copyright 2006 Rob Landley <rob@landley.net>
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 
55 // fast integer conversion to decimal string
56 // TODO move to lib?
itoa(char * s,int i)57 static char *itoa(char *s, int i)
58 {
59   char buf[16], *ff = buf;
60   unsigned n = i;
61 
62   if (i<0) {
63     *s++ = '-';
64     n = -i;
65   }
66   do *ff++ = '0'+n%10; while ((n /= 10));
67   do *s++ = *--ff; while (ff>buf);
68   *s++ = '\n';
69 
70   return s;
71 }
72 
flush_toybuf(char * ss)73 static char *flush_toybuf(char *ss)
74 {
75   if (ss-toybuf<TT.buflen) return ss;
76   xwrite(1, toybuf, ss-toybuf);
77 
78   return toybuf;
79 }
80 
seq_main(void)81 void seq_main(void)
82 {
83   char fbuf[32], *ss;
84   double first = 1, increment = 1, last, dd;
85   int ii, inc = 1, len, slen;
86 
87   // parse arguments
88   if (!TT.s) TT.s = "\n";
89   switch (toys.optc) {
90     case 3: increment = parsef(toys.optargs[1]);
91     case 2: first = parsef(*toys.optargs);
92     default: last = parsef(toys.optargs[toys.optc-1]);
93   }
94 
95   // measure arguments
96   if (FLAG(f)) insanitize(TT.f);
97   for (ii = len = 0; ii<3; ii++) {
98     dd = (double []){first, increment, last}[ii];
99     len = maxof(len, snprintf(0, 0, "%.*f", TT.precision, fabs(dd)));
100     if (ii == 2) dd += increment;
101     slen = dd;
102     if (dd != slen) inc = 0;
103   }
104   if (!FLAG(f)) sprintf(TT.f = fbuf, "%%0%d.%df", len, TT.precision);
105   TT.buflen = sizeof(toybuf) - 32 - len - TT.precision - strlen(TT.s);
106   if (TT.buflen<0) error_exit("bad -s");
107 
108   // fast path: when everything fits in an int with no flags.
109   if (!toys.optflags && inc) {
110     ii = first;
111     len = last;
112     inc = increment;
113     ss = toybuf;
114     if (inc>0) for (; ii<=len; ii += inc)
115       ss = flush_toybuf(itoa(ss, ii));
116     else if (inc<0) for (; ii>=len; ii += inc)
117       ss = flush_toybuf(itoa(ss, ii));
118     if (ss != toybuf) xwrite(1, toybuf, ss-toybuf);
119 
120     return;
121   }
122 
123   // Other implementations output nothing if increment is 0 and first > last,
124   // but loop forever if first < last or even first == last. We output
125   // nothing for all three, if you want endless output use "yes".
126   if (!increment) return;
127 
128   // Slow path, floating point and fancy sprintf() patterns
129   for (ii = 0, ss = toybuf;; ii++) {
130     // Multiply to avoid accumulating rounding errors from increment.
131     dd = first+ii*increment;
132     if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
133     if (ii) ss = flush_toybuf(stpcpy(ss, TT.s));
134     ss += sprintf(ss, TT.f, dd);
135   }
136   *ss++ = '\n';
137   xwrite(1, toybuf, ss-toybuf);
138 }
139