sbase/seq.c

155 lines
2.6 KiB
C
Raw Normal View History

2012-06-09 13:53:05 -04:00
/* See LICENSE file for copyright and license details. */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
2012-06-09 13:53:05 -04:00
#include "util.h"
static int digitsleft(const char *);
static int digitsright(const char *);
static bool validfmt(const char *);
2013-06-14 14:20:47 -04:00
static void
usage(void)
{
2013-07-01 13:25:41 -04:00
eprintf("usage: %s [-f fmt] [-s separator] [-w width] [start"
" [step]] end\n", argv0);
2013-06-14 14:20:47 -04:00
}
2012-06-09 13:53:05 -04:00
int
main(int argc, char *argv[])
{
const char *starts = "1", *steps = "1", *ends = "1", *sep = "\n";
2012-06-09 13:53:39 -04:00
bool wflag = false;
2013-07-01 13:25:41 -04:00
char *tmp, ftmp[BUFSIZ], *fmt = ftmp;
2012-06-09 13:53:05 -04:00
double start, step, end, out, dir;
2013-07-01 13:25:41 -04:00
int left, right;
2012-06-09 13:53:05 -04:00
2013-06-14 14:20:47 -04:00
ARGBEGIN {
case 'f':
2013-07-01 13:25:41 -04:00
if(!validfmt(tmp=EARGF(usage())))
eprintf("%s: invalid format\n", tmp);
fmt = tmp;
2013-06-14 14:20:47 -04:00
break;
case 's':
sep = EARGF(usage());
break;
case 'w':
wflag = true;
break;
default:
usage();
} ARGEND;
switch (argc) {
2012-06-09 13:53:05 -04:00
case 3:
2013-07-01 13:25:41 -04:00
steps = argv[1];
argv[1] = argv[2];
/* fallthrough */
2012-06-09 13:53:05 -04:00
case 2:
2013-06-14 14:20:47 -04:00
starts = argv[0];
argv++;
2012-06-09 13:53:05 -04:00
/* fallthrough */
case 1:
2013-06-14 14:20:47 -04:00
ends = argv[0];
2012-06-09 13:53:05 -04:00
break;
default:
2013-06-14 14:20:47 -04:00
usage();
2012-06-09 13:53:05 -04:00
}
start = estrtod(starts);
step = estrtod(steps);
end = estrtod(ends);
dir = (step > 0) ? 1.0 : -1.0;
if (step == 0 || start * dir > end * dir)
2014-10-02 18:46:04 -04:00
return 1;
2012-06-09 13:53:05 -04:00
if (fmt == ftmp) {
2013-07-01 13:25:41 -04:00
right = MAX(digitsright(starts),
2012-06-09 13:53:05 -04:00
MAX(digitsright(ends),
digitsright(steps)));
if (wflag) {
2013-07-01 13:25:41 -04:00
left = MAX(digitsleft(starts), digitsleft(ends));
2012-06-09 13:53:05 -04:00
2013-06-14 14:20:47 -04:00
snprintf(ftmp, sizeof ftmp, "%%0%d.%df",
right + left + (right != 0), right);
2013-06-14 14:20:47 -04:00
} else
2012-06-09 13:53:05 -04:00
snprintf(ftmp, sizeof ftmp, "%%.%df", right);
}
for (out = start; out * dir <= end * dir; out += step) {
if (out != start)
2012-06-09 13:53:05 -04:00
fputs(sep, stdout);
printf(fmt, out);
}
printf("\n");
2014-10-02 18:46:04 -04:00
return 0;
2012-06-09 13:53:05 -04:00
}
static int
2012-06-09 13:53:05 -04:00
digitsleft(const char *d)
{
char *exp;
int shift;
if (*d == '+')
2012-06-09 13:53:05 -04:00
d++;
exp = strpbrk(d, "eE");
2013-10-27 05:46:21 -04:00
shift = exp ? estrtol(&exp[1], 10) : 0;
2012-06-09 13:53:05 -04:00
return MAX(0, strspn(d, "-0123456789") + shift);
2012-06-09 13:53:05 -04:00
}
static int
2012-06-09 13:53:05 -04:00
digitsright(const char *d)
{
char *exp;
int shift, after;
exp = strpbrk(d, "eE");
2013-10-27 05:46:21 -04:00
shift = exp ? estrtol(&exp[1], 10) : 0;
2012-06-09 13:53:05 -04:00
after = (d = strchr(d, '.')) ? strspn(&d[1], "0123456789") : 0;
return MAX(0, after - shift);
2012-06-09 13:53:05 -04:00
}
static bool
2012-06-09 13:53:05 -04:00
validfmt(const char *fmt)
{
int occur = 0;
literal:
while (*fmt)
if (*fmt++ == '%')
2012-06-09 13:53:05 -04:00
goto format;
return occur == 1;
format:
if (*fmt == '%') {
2012-06-09 13:53:05 -04:00
fmt++;
goto literal;
}
fmt += strspn(fmt, "-+#0 '");
fmt += strspn(fmt, "0123456789");
if (*fmt == '.') {
2012-06-09 13:53:05 -04:00
fmt++;
fmt += strspn(fmt, "0123456789");
}
if (*fmt == 'L')
2012-06-09 13:53:05 -04:00
fmt++;
switch (*fmt) {
2012-06-09 13:53:05 -04:00
case 'f': case 'F':
case 'g': case 'G':
case 'e': case 'E':
case 'a': case 'A':
occur++;
goto literal;
default:
return false;
}
}