sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

seq.c (2581B)


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