1/* See LICENSE file for copyright and license details. */
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7#include "util.h"
8
9static int
10digitsleft(const char *d)
11{
12 int shift;
13 const char *exp;
14
15 if (*d == '+')
16 d++;
17 exp = strpbrk(d, "eE");
18 shift = exp ? estrtonum(exp + 1, INT_MIN, INT_MAX) : 0;
19
20 return MAX(0, (int)strspn(d, "-0123456789") + shift);
21}
22
23static int
24digitsright(const char *d)
25{
26 int shift, after;
27 const char *exp;
28
29 exp = strpbrk(d, "eE");
30 shift = exp ? estrtonum(&exp[1], INT_MIN, INT_MAX) : 0;
31 after = (d = strchr(d, '.')) ? strspn(&d[1], "0123456789") : 0;
32
33 return MAX(0, after - shift);
34}
35
36static int
37validfmt(const char *fmt)
38{
39 int occur = 0;
40
41literal:
42 while (*fmt)
43 if (*fmt++ == '%')
44 goto format;
45 return occur == 1;
46
47format:
48 if (*fmt == '%') {
49 fmt++;
50 goto literal;
51 }
52 fmt += strspn(fmt, "-+#0 '");
53 fmt += strspn(fmt, "0123456789");
54 if (*fmt == '.') {
55 fmt++;
56 fmt += strspn(fmt, "0123456789");
57 }
58 if (*fmt == 'L')
59 fmt++;
60
61 switch (*fmt) {
62 // ?man -f: force the operation
63 case 'f':
64 case 'F':
65 case 'g':
66 case 'G':
67 case 'e':
68 case 'E':
69 case 'a':
70 case 'A':
71 occur++;
72 goto literal;
73 default:
74 return 0;
75 }
76}
77
78static void
79usage(void)
80{
81 eprintf(
82 "usage: %s [-f fmt] [-s sep] [-w] "
83 "[startnum [step]] endnum\n",
84 argv0
85 );
86}
87
88// ?man seq: print sequence of numbers
89// ?man print a sequence of numbers from start to end
90int
91main(int argc, char *argv[])
92{
93 double start, step, end, out, dir;
94 int wflag = 0, left, right;
95 char *tmp, ftmp[BUFSIZ], *fmt = ftmp;
96 const char *starts = "1", *steps = "1", *ends = "1", *sep = "\n";
97
98 ARGBEGIN
99 {
100 // ?man -f:str: force the operation
101 case 'f':
102 if (!validfmt(tmp = EARGF(usage())))
103 eprintf("%s: invalid format\n", tmp);
104 fmt = tmp;
105 break;
106 // ?man -s:str: silent mode or print summary
107 case 's':
108 sep = EARGF(usage());
109 break;
110 // ?man -w: wait for completion
111 case 'w':
112 wflag = 1;
113 break;
114 default:
115 usage();
116 }
117 ARGEND
118
119 switch (argc) {
120 case 3:
121 steps = argv[1];
122 argv[1] = argv[2];
123 /* fallthrough */
124 case 2:
125 starts = argv[0];
126 argv++;
127 /* fallthrough */
128 case 1:
129 ends = argv[0];
130 break;
131 default:
132 usage();
133 }
134 start = estrtod(starts);
135 step = estrtod(steps);
136 end = estrtod(ends);
137
138 dir = (step > 0) ? 1.0 : -1.0;
139 if (step == 0 || start * dir > end * dir)
140 return 1;
141
142 if (fmt == ftmp) {
143 right = MAX(digitsright(starts), MAX(digitsright(ends), digitsright(steps)));
144
145 if (wflag) {
146 left = MAX(digitsleft(starts), digitsleft(ends));
147
148 snprintf(ftmp, sizeof ftmp, "%%0%d.%df", right + left + (right != 0), right);
149 } else
150 snprintf(ftmp, sizeof ftmp, "%%.%df", right);
151 }
152 for (out = start; out * dir <= end * dir; out += step) {
153 if (out != start)
154 fputs(sep, stdout);
155 printf(fmt, out);
156 }
157 putchar('\n');
158
159 return fshut(stdout, "<stdout>");
160}