> 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(&reg, "\\([^%]|%%\\)*%[0-9]*\\.[0-9]*[fFgG]\\([^%]|%%\\)*", REG_NOSUB);
	int ret = regexec(&reg, fmt, 0, NULL, 0);
	regfree(&reg);
	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;
}

Reply via email to