cut.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
cut.c (4145B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "text.h"
7 #include "utf.h"
8 #include "util.h"
9
10 typedef struct Range {
11 size_t min, max;
12 struct Range *next;
13 } Range;
14
15 static Range *list = NULL;
16 static char mode = 0;
17 static char *delim = "\t";
18 static size_t delimlen = 1;
19 static int nflag = 0;
20 static int sflag = 0;
21
22 static void
23 insert(Range *r)
24 {
25 Range *l, *p, *t;
26
27 for (p = NULL, l = list; l; p = l, l = l->next) {
28 if (r->max && r->max + 1 < l->min) {
29 r->next = l;
30 break;
31 } else if (!l->max || r->min < l->max + 2) {
32 l->min = MIN(r->min, l->min);
33 for (p = l, t = l->next; t; p = t, t = t->next)
34 if (r->max && r->max + 1 < t->min)
35 break;
36 l->max = (p->max && r->max) ? MAX(p->max, r->max) : 0;
37 l->next = t;
38 return;
39 }
40 }
41 if (p)
42 p->next = r;
43 else
44 list = r;
45 }
46
47 static void
48 parselist(char *str)
49 {
50 char *s;
51 size_t n = 1;
52 Range *r;
53
54 if (!*str)
55 eprintf("empty list\n");
56 for (s = str; *s; s++) {
57 if (*s == ' ')
58 *s = ',';
59 if (*s == ',')
60 n++;
61 }
62 r = ereallocarray(NULL, n, sizeof(*r));
63 for (s = str; n; n--, s++) {
64 r->min = (*s == '-') ? 1 : strtoul(s, &s, 10);
65 r->max = (*s == '-') ? strtoul(s + 1, &s, 10) : r->min;
66 r->next = NULL;
67 if (!r->min || (r->max && r->max < r->min) || (*s && *s != ','))
68 eprintf("bad list value\n");
69 insert(r++);
70 }
71 }
72
73 static size_t
74 seek(struct line *s, size_t pos, size_t *prev, size_t count)
75 {
76 size_t n = pos - *prev, i, j;
77
78 if (mode == 'b') {
79 if (n >= s->len)
80 return s->len;
81 if (nflag)
82 while (n && !UTF8_POINT(s->data[n]))
83 n--;
84 *prev += n;
85 return n;
86 } else if (mode == 'c') {
87 for (n++, i = 0; i < s->len; i++)
88 if (UTF8_POINT(s->data[i]) && !--n)
89 break;
90 } else {
91 for (i = (count < delimlen + 1) ? 0 : delimlen; n && i < s->len; ) {
92 if ((s->len - i) >= delimlen &&
93 !memcmp(s->data + i, delim, delimlen)) {
94 if (!--n && count)
95 break;
96 i += delimlen;
97 continue;
98 }
99 for (j = 1; j + i <= s->len && !fullrune(s->data + i, j); j++);
100 i += j;
101 }
102 }
103 *prev = pos;
104
105 return i;
106 }
107
108 static void
109 cut(FILE *fp, const char *fname)
110 {
111 Range *r;
112 struct line s;
113 static struct line line;
114 static size_t size;
115 size_t i, n, p;
116 ssize_t len;
117
118 while ((len = getline(&line.data, &size, fp)) > 0) {
119 line.len = len;
120 if (line.data[line.len - 1] == '\n')
121 line.data[--line.len] = '\0';
122 if (mode == 'f' && !memmem(line.data, line.len, delim, delimlen)) {
123 if (!sflag) {
124 fwrite(line.data, 1, line.len, stdout);
125 fputc('\n', stdout);
126 }
127 continue;
128 }
129 for (i = 0, p = 1, s = line, r = list; r; r = r->next) {
130 n = seek(&s, r->min, &p, i);
131 s.data += n;
132 s.len -= n;
133 i += (mode == 'f') ? delimlen : 1;
134 if (!s.len)
135 break;
136 if (!r->max) {
137 fwrite(s.data, 1, s.len, stdout);
138 break;
139 }
140 n = seek(&s, r->max + 1, &p, i);
141 i += (mode == 'f') ? delimlen : 1;
142 if (fwrite(s.data, 1, n, stdout) != n)
143 eprintf("fwrite <stdout>:");
144 s.data += n;
145 s.len -= n;
146 }
147 putchar('\n');
148 }
149 if (ferror(fp))
150 eprintf("getline %s:", fname);
151 }
152
153 static void
154 usage(void)
155 {
156 eprintf("usage: %s -b list [-n] [file ...]\n"
157 " %s -c list [file ...]\n"
158 " %s -f list [-d delim] [-s] [file ...]\n",
159 argv0, argv0, argv0);
160 }
161
162 int
163 main(int argc, char *argv[])
164 {
165 FILE *fp;
166 int ret = 0;
167
168 ARGBEGIN {
169 case 'b':
170 case 'c':
171 case 'f':
172 mode = ARGC();
173 parselist(EARGF(usage()));
174 break;
175 case 'd':
176 delim = EARGF(usage());
177 if (!*delim)
178 eprintf("empty delimiter\n");
179 delimlen = unescape(delim);
180 break;
181 case 'n':
182 nflag = 1;
183 break;
184 case 's':
185 sflag = 1;
186 break;
187 default:
188 usage();
189 } ARGEND
190
191 if (!mode)
192 usage();
193
194 if (!argc)
195 cut(stdin, "<stdin>");
196 else {
197 for (; *argv; argc--, argv++) {
198 if (!strcmp(*argv, "-")) {
199 *argv = "<stdin>";
200 fp = stdin;
201 } else if (!(fp = fopen(*argv, "r"))) {
202 weprintf("fopen %s:", *argv);
203 ret = 1;
204 continue;
205 }
206 cut(fp, *argv);
207 if (fp != stdin && fshut(fp, *argv))
208 ret = 1;
209 }
210 }
211
212 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
213
214 return ret;
215 }