seq: some cleanup

This commit is contained in:
Connor Lane Smith 2012-05-10 19:20:16 +01:00
parent c018f86fc7
commit 6e0f5c1409
4 changed files with 122 additions and 148 deletions

18
seq.1
View File

@ -9,20 +9,22 @@ seq \- print a sequence of numbers
.RB [ \-s .RB [ \-s
.IR separator ] .IR separator ]
.RI [ start .RI [ start
.RI [step] ] .RI [ step ]]
.RI end .RI end
.SH DESCRIPTION .SH DESCRIPTION
.B seq .B seq
will print numbers from will print a sequence of numbers from
.I START .I start
to (default 1) to
.I END, .IR end ,
in steps of in
.I STEP. .IR step
intervals (default 1).
.SH OPTIONS .SH OPTIONS
.TP .TP
.BI \-f " format" .BI \-f " format"
specifies the printf style for the output lines specifies the format used for output lines, as per
.IR printf (3).
.TP .TP
.BI \-s " separator" .BI \-s " separator"
specifies the separator to print between output lines specifies the separator to print between output lines

245
seq.c
View File

@ -1,113 +1,29 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdbool.h>
#include <libgen.h>
#include "util.h" #include "util.h"
#define MAX(a, b) (((a) > (b))? (a):(b)) static int digitsleft(const char *);
static int digitsright(const char *);
int static double estrtod(const char *);
validfloat(char *fmt) static bool validfmt(const char *);
{
char *end;
fmt += strspn(fmt, " ");
strtod(fmt, &end);
if (fmt == end || end != fmt + strlen(fmt))
return 0;
return 1;
}
int
digitsleft(char *d)
{
char *exp;
int shift;
if (d[0] == '+')
d++;
exp = strpbrk(d, "eE");
shift = exp? atoi(exp+1) : 0;
return MAX(0, strspn(d, "-0123456789")+shift);
}
int
digitsright(char *d)
{
char *exp;
int shift, after;
exp = strpbrk(d, "eE");
shift = exp ? atoi(exp+1) : 0;
after = (d = strchr(d, '.'))? strspn(d+1, "0123456789") : 0;
return MAX(0, after-shift);
}
int
validfmt(char *fmt)
{
int occur;
occur = 0;
NonFormat:
while(*fmt) {
if (*fmt++ == '%')
goto Format;
}
return (occur == 1);
Format:
if (*fmt == '%') {
fmt++;
goto NonFormat;
}
fmt += strspn(fmt, "-+#0 '");
fmt += strspn(fmt, "0123456789");
if (*fmt == '.') {
fmt ++;
fmt += strspn(fmt, "0123456789");
}
if (*fmt == 'L')
fmt++;
if (*fmt == '\0')
return 0;
if (strchr("fFgGeEaA", *fmt)) {
occur++;
goto NonFormat;
}
return 0;
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char c, *fmt, ftmp[4096], *sep, *starts, *steps, *ends; const char *starts = "1", *steps = "1", *ends = "1", *sep = "\n";
bool wflag, fflag; bool fflag = false, wflag = false;
char c, ftmp[BUFSIZ], *fmt = ftmp;
double start, step, end, out, dir; double start, step, end, out, dir;
int left, right;
sep = "\n"; while((c = getopt(argc, argv, "f:s:w")) != -1)
fmt = ftmp;
wflag = false;
fflag = false;
starts = "1";
steps = "1";
ends = "1";
while((c = getopt(argc, argv, "f:s:w")) != -1) {
switch(c) { switch(c) {
case 'f': case 'f':
if (!validfmt(optarg)) if(!validfmt(optarg))
eprintf("invalid format.\n"); eprintf("%s: invalid format\n", optarg);
fmt = optarg; fmt = optarg;
fflag = true; fflag = true;
break; break;
@ -118,10 +34,6 @@ main(int argc, char *argv[])
wflag = true; wflag = true;
break; break;
} }
}
if (wflag && fflag)
eprintf("-f and -w cannot be combined.\n");
switch(argc-optind) { switch(argc-optind) {
case 3: case 3:
@ -131,59 +43,116 @@ main(int argc, char *argv[])
break; break;
case 2: case 2:
starts = argv[optind++]; starts = argv[optind++];
ends = argv[optind++]; /* fallthrough */
break;
case 1: case 1:
ends = argv[optind++]; ends = argv[optind++];
break; break;
default: default:
eprintf("usage: %s [-w] [-f fmt] [-s separator] " eprintf("usage: %s [-w] [-f fmt] [-s separator] [start [step]] end\n", argv[0]);
"[start [step]] end\n",
basename(argv[0]));
} }
start = estrtod(starts);
step = estrtod(steps);
end = estrtod(ends);
if (!validfloat(starts)) dir = (step > 0) ? 1.0 : -1.0;
eprintf("start is not a valid float.\n"); if(step == 0 || start * dir > end * dir)
if (!validfloat(steps))
eprintf("step is not a valid float.\n");
if (!validfloat(ends))
eprintf("end is not a valid float.\n");
start = atof(starts);
step = atof(steps);
end = atof(ends);
dir = (step > 0)? 1.0 : -1.0;
if (step == 0)
return EXIT_FAILURE;
if (start * dir > end * dir)
return EXIT_FAILURE; return EXIT_FAILURE;
right = MAX(digitsright(starts), if(fmt == ftmp) {
MAX(digitsright(ends), int right = MAX(digitsright(starts),
digitsright(steps))); MAX(digitsright(ends),
if (wflag) { digitsright(steps)));
left = MAX(digitsleft(starts), digitsleft(ends));
snprintf(ftmp, sizeof(ftmp), "%%0%d.%df", if(wflag) {
right+left+(right != 0), int left = MAX(digitsleft(starts), digitsleft(ends));
right);
} else if (fmt == ftmp) {
snprintf(ftmp, sizeof(ftmp), "%%.%df", right);
}
for (out = start;;) { snprintf(ftmp, sizeof ftmp, "%%0%d.%df", right+left+(right != 0), right);
printf(fmt, out);
out += step;
if (out * dir <= end * dir) {
printf("%s", sep);
} else {
break;
} }
else
snprintf(ftmp, sizeof ftmp, "%%.%df", right);
}
for(out = start; out * dir <= end * dir; out += step) {
if(out != start)
fputs(sep, stdout);
printf(fmt, out);
} }
printf("\n"); printf("\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int
digitsleft(const char *d)
{
char *exp;
int shift;
if(*d == '+')
d++;
exp = strpbrk(d, "eE");
shift = exp ? atoi(&exp[1]) : 0;
return MAX(0, strspn(d, "-0123456789")+shift);
}
int
digitsright(const char *d)
{
char *exp;
int shift, after;
exp = strpbrk(d, "eE");
shift = exp ? atoi(&exp[1]) : 0;
after = (d = strchr(d, '.')) ? strspn(&d[1], "0123456789") : 0;
return MAX(0, after-shift);
}
double
estrtod(const char *s)
{
char *end;
double d;
d = strtod(s, &end);
if(end == s || *end != '\0')
eprintf("%s: not a real number\n", s);
return d;
}
bool
validfmt(const char *fmt)
{
int occur = 0;
literal:
while(*fmt)
if(*fmt++ == '%')
goto format;
return occur == 1;
format:
if(*fmt == '%') {
fmt++;
goto literal;
}
fmt += strspn(fmt, "-+#0 '");
fmt += strspn(fmt, "0123456789");
if(*fmt == '.') {
fmt++;
fmt += strspn(fmt, "0123456789");
}
if(*fmt == 'L')
fmt++;
switch(*fmt) {
case 'f': case 'F':
case 'g': case 'G':
case 'e': case 'E':
case 'a': case 'A':
occur++;
goto literal;
default:
return false;
}
}

3
util.h
View File

@ -2,6 +2,9 @@
#define UTF8_POINT(c) (((c) & 0xc0) != 0x80) #define UTF8_POINT(c) (((c) & 0xc0) != 0x80)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
char *agetcwd(void); char *agetcwd(void);
void apathmax(char **, long *); void apathmax(char **, long *);
void enmasse(int, char **, int (*)(const char *, const char *)); void enmasse(int, char **, int (*)(const char *, const char *));

View File

@ -12,9 +12,9 @@ estrtol(const char *s, int base)
n = strtol(s, &end, base); n = strtol(s, &end, base);
if(*end != '\0') { if(*end != '\0') {
if(base == 0) if(base == 0)
eprintf("%s: not a number\n", s); eprintf("%s: not an integer\n", s);
else else
eprintf("%s: not a base %d number\n", s, base); eprintf("%s: not a base %d integer\n", s, base);
} }
return n; return n;
} }