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 }