> Have you tried the fmtvalid() function? It seems like there is a typo
> where it is used. Such a function would still be useful in the now ex‐
> isting seq.c.
I hadn't tested it when I sent that. I've attached a version with a tested
fmtvalid function, proper support for scientific notation, validation
of the user-inputted numbers, and corrected `seq 1.0 10` behavior.
It's almost 60 lines shorter than the one in tip.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <regex.h>
#include "util.h"
#define MAX(a, b) (((a)>(b))?(a):(b))
static void
validatefloat(char *fmt)
{
char*end;
fmt+=strspn(fmt, " ");
strtod(fmt, &end);
if(fmt==end || end!=fmt+strlen(fmt))
eprintf("invalid float: %s\n", fmt);
}
static int
digsbefore(char* d)
{
validatefloat(d);
if(d[0] == '-')
d++;
char *exp = strpbrk(d, "eE");
int shift = exp?atoi(exp+1):0;
return MAX(0, strspn(d, "0123456789")+shift);
}
static int
digsafter(char *d)
{
validatefloat(d);
char *exp = strpbrk(d, "eE");
int shift = exp?atoi(exp+1):0;
int after = (d=strchr(d, '.'))?strspn(d+1, "0123456789"):0;
return MAX(0, after-shift);
}
static int
fmtvalid(char * fmt)
{
regex_t reg;
regcomp(®, "\\([^%]|%%\\)*%[0-9]*\\.[0-9]*[fFgG]\\([^%]|%%\\)*", REG_NOSUB);
int ret = regexec(®, fmt, 0, NULL, 0);
regfree(®);
return ret==0;
}
int
main(int argc, char *argv[])
{
char c;
char fmtbuf[4096];
char *fmt = NULL;
char *sep = "\n";
bool wflag = false;
char* end = "1";
char* start = "1";
char* step = "1";
double out;
while((c = getopt(argc, argv, "f:s:w")) != -1)
switch(c) {
case 'f':
if(!fmtvalid(optarg))
eprintf("invalid format.\n");
fmt = optarg;
break;
case 's':
sep = optarg;
break;
case 'w':
wflag = true;
break;
}
switch(argc-optind) {
case 3:
start=argv[optind++];
step=argv[optind++];
end=argv[optind];
break;
case 2:
start=argv[optind++];
end=argv[optind];
break;
case 1:
end=argv[optind];
break;
default:
eprintf("usage: seq [-f'fmt'] [-s'separator'] [-w] [start [step]] end\n");
}
int before = MAX(digsbefore(start), digsbefore(end));
int after = MAX(digsafter(start), digsafter(step));
if (wflag){
sprintf(fmtbuf, "%%0%d.%df", after+before+(after!=0), after);
fmt = fmtbuf;
} else if(fmt == NULL){
sprintf(fmtbuf, "%%.%df", after);
fmt = fmtbuf;
}
double dstart = atof(start), dend=atof(end),dstep=atof(step);
for (out = dstart; out <= dend; out += dstep) {
printf(fmt, out);
printf("%s", sep);
}
return EXIT_SUCCESS;
}