expand.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
expand.c (2300B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <stdint.h> | |
3 #include <stdlib.h> | |
4 #include <string.h> | |
5 | |
6 #include "utf.h" | |
7 #include "util.h" | |
8 | |
9 static int iflag = 0; | |
10 static size_t *tablist = NULL; | |
11 static size_t tablistlen = 0; | |
12 | |
13 static size_t | |
14 parselist(const char *s) | |
15 { | |
16 size_t i; | |
17 char *p, *tmp; | |
18 | |
19 tmp = estrdup(s); | |
20 for (i = 0; (p = strsep(&tmp, " ,")); i++) { | |
21 if (*p == '\0') | |
22 eprintf("empty field in tablist\n"); | |
23 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist)… | |
24 tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX)); | |
25 if (i > 0 && tablist[i - 1] >= tablist[i]) | |
26 eprintf("tablist must be ascending\n"); | |
27 } | |
28 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist)); | |
29 /* tab length = 1 for the overflowing case later in the matcher … | |
30 tablist[i] = 1; | |
31 | |
32 return i; | |
33 } | |
34 | |
35 static int | |
36 expand(const char *file, FILE *fp) | |
37 { | |
38 size_t bol = 1, col = 0, i; | |
39 Rune r; | |
40 | |
41 while (efgetrune(&r, fp, file)) { | |
42 switch (r) { | |
43 case '\t': | |
44 if (tablistlen == 1) | |
45 i = 0; | |
46 else for (i = 0; i < tablistlen; i++) | |
47 if (col < tablist[i]) | |
48 break; | |
49 if (bol || !iflag) { | |
50 do { | |
51 col++; | |
52 putchar(' '); | |
53 } while (col % tablist[i]); | |
54 } else { | |
55 putchar('\t'); | |
56 col = tablist[i]; | |
57 } | |
58 break; | |
59 case '\b': | |
60 bol = 0; | |
61 if (col) | |
62 col--; | |
63 putchar('\b'); | |
64 break; | |
65 case '\n': | |
66 bol = 1; | |
67 col = 0; | |
68 putchar('\n'); | |
69 break; | |
70 default: | |
71 col++; | |
72 if (r != ' ') | |
73 bol = 0; | |
74 efputrune(&r, stdout, "<stdout>"); | |
75 break; | |
76 } | |
77 } | |
78 | |
79 return 0; | |
80 } | |
81 | |
82 static void | |
83 usage(void) | |
84 { | |
85 eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0); | |
86 } | |
87 | |
88 int | |
89 main(int argc, char *argv[]) | |
90 { | |
91 FILE *fp; | |
92 int ret = 0; | |
93 char *tl = "8"; | |
94 | |
95 ARGBEGIN { | |
96 case 'i': | |
97 iflag = 1; | |
98 break; | |
99 case 't': | |
100 tl = EARGF(usage()); | |
101 if (!*tl) | |
102 eprintf("tablist cannot be empty\n"); | |
103 break; | |
104 default: | |
105 usage(); | |
106 } ARGEND | |
107 | |
108 tablistlen = parselist(tl); | |
109 | |
110 if (!argc) { | |
111 expand("<stdin>", stdin); | |
112 } else { | |
113 for (; *argv; argc--, argv++) { | |
114 if (!strcmp(*argv, "-")) { | |
115 *argv = "<stdin>"; | |
116 fp = stdin; | |
117 } else if (!(fp = fopen(*argv, "r"))) { | |
118 weprintf("fopen %s:", *argv); | |
119 ret = 1; | |
120 continue; | |
121 } | |
122 expand(*argv, fp); | |
123 if (fp != stdin && fshut(fp, *argv)) | |
124 ret = 1; | |
125 } | |
126 } | |
127 | |
128 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"); | |
129 | |
130 return ret; | |
131 } |