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