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 
GLOBALS(char * sep;char * fmt;)28 GLOBALS(
29   char *sep;
30   char *fmt;
31 )
32 
33 // Ensure there's one %f escape with correct attributes
34 static void insanitize(char *f)
35 {
36   char *s = next_printf(f, 0);
37 
38   if (!s) error_exit("bad -f no %%f");
39   if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
40     // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
41     error_exit("bad -f '%s'@%ld", f, s-f+1);
42   }
43 }
44 
seq_main(void)45 void seq_main(void)
46 {
47   double first, increment, last, dd;
48   char *sep_str = "\n", *fmt_str = "%g";
49   int output = 0;
50 
51   // Parse command line arguments, with appropriate defaults.
52   // Note that any non-numeric arguments are treated as zero.
53   first = increment = 1;
54   switch (toys.optc) {
55     case 3: increment = atof(toys.optargs[1]);
56     case 2: first = atof(*toys.optargs);
57     default: last = atof(toys.optargs[toys.optc-1]);
58   }
59 
60   // Pad to largest width
61   if (toys.optflags & FLAG_w) {
62     char *s;
63     int i, len, dot, left = 0, right = 0;
64 
65     for (i=0; i<3; i++) {
66       dd = (double []){first, increment, last}[i];
67 
68       len = sprintf(toybuf, "%g", dd);
69       if ((s = strchr(toybuf, '.'))) {
70         dot = s-toybuf;
71         if (left<dot) left = dot;
72         dot = len-dot-1;
73         if (right<dot) right = dot;
74       } else if (len>left) left = len;
75     }
76 
77     sprintf(fmt_str = toybuf, "%%0%d.%df", left+right+!!right, right);
78   }
79   if (toys.optflags & FLAG_f) insanitize(fmt_str = TT.fmt);
80   if (toys.optflags & FLAG_s) sep_str = TT.sep;
81 
82   // Yes, we're looping on a double.  Yes rounding errors can accumulate if
83   // you use a non-integer increment.  Deal with it.
84   for (dd=first; (increment>0 && dd<=last) || (increment<0 && dd>=last);
85     dd+=increment)
86   {
87     if (dd != first) printf("%s", sep_str);
88     printf(fmt_str, dd);
89     output = 1;
90   }
91 
92   if (output) printf("\n");
93 }
94