1
2 #include "ex_utils.h"
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <getopt.h>
8
9 #include "opt.h"
10
11 #include "bag.h"
12
13 static Bag *filters = NULL;
14
15 #define CSTREQ(x, y) (strcmp(x, y) == 0)
16
17
18 #define FILTER_MATCH(name, cmp) \
19 (CSTREQ(obj->key, name) && cmp (s2, vpos, vlen, obj->val))
20
vprefix(Vstr_base * s1,size_t pos,size_t len,const char * s2)21 static int vprefix(Vstr_base *s1, size_t pos, size_t len, const char *s2)
22 {
23 size_t clen = strlen(s2);
24
25 if (clen > len)
26 return (FALSE);
27
28 return (vstr_cmp_bod_buf_eq(s1, pos, len, s2, clen));
29 }
30
vsuffix(Vstr_base * s1,size_t pos,size_t len,const char * s2)31 static int vsuffix(Vstr_base *s1, size_t pos, size_t len, const char *s2)
32 {
33 size_t clen = strlen(s2);
34
35 if (clen > len)
36 return (FALSE);
37
38 return (vstr_cmp_eod_buf_eq(s1, pos, len, s2, clen));
39 }
40
vany(Vstr_base * s1,size_t pos,size_t len,const char * s2)41 static int vany(Vstr_base *s1, size_t pos, size_t len, const char *s2)
42 {
43 size_t clen = strlen(s2);
44
45 if (clen > len)
46 return (FALSE);
47
48 return (vstr_srch_buf_fwd(s1, pos, len, s2, clen) != 0);
49 }
50
ex_dir_filter_process(Vstr_base * s1,Vstr_base * s2,int * parsed_header)51 static int ex_dir_filter_process(Vstr_base *s1, Vstr_base *s2,
52 int *parsed_header)
53 {
54 size_t pos = 0;
55 size_t len = 0;
56 size_t ns1 = vstr_parse_netstr(s2, 1, s2->len, &pos, &len);
57
58 if (!ns1)
59 {
60 if ((len > EX_MAX_R_DATA_INCORE) ||
61 (s2->len > EX_MAX_R_DATA_INCORE))
62 errx(EXIT_FAILURE, "bad input");
63
64 return (FALSE);
65 }
66
67 if (!*parsed_header)
68 {
69 size_t vpos = 0;
70 size_t vlen = 0;
71 size_t nst = 0;
72
73 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
74 errx(EXIT_FAILURE, "bad input");
75 pos += nst; len -= nst;
76 if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "version"))
77 errx(EXIT_FAILURE, "bad input");
78 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
79 errx(EXIT_FAILURE, "bad input");
80 pos += nst; len -= nst;
81 if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "1"))
82 errx(EXIT_FAILURE, "Unsupported version");
83 *parsed_header = TRUE;
84 len = 0;
85 }
86
87 while (len)
88 {
89 size_t kpos = 0;
90 size_t klen = 0;
91 size_t vpos = 0;
92 size_t vlen = 0;
93 size_t nst = 0;
94 Bag_iter iter[1];
95 const Bag_obj *obj = NULL;
96
97 if (!(nst = vstr_parse_netstr(s2, pos, len, &kpos, &klen)))
98 errx(EXIT_FAILURE, "bad input");
99 pos += nst; len -= nst;
100
101 if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen)))
102 errx(EXIT_FAILURE, "bad input");
103 pos += nst; len -= nst;
104
105 if (!vstr_cmp_cstr_eq(s2, kpos, klen, "name"))
106 continue; /* only names atm. */
107
108 if (!(obj = bag_iter_beg(filters, iter)))
109 break;
110
111 do
112 {
113 int keep = TRUE;
114
115 if (0) { /* do nothing */ }
116 else if (FILTER_MATCH("acpt-name-eq", vstr_cmp_cstr_eq)) keep = TRUE;
117 else if (FILTER_MATCH("deny-name-eq", vstr_cmp_cstr_eq)) keep = FALSE;
118 else if (FILTER_MATCH("acpt-name-beg", vprefix)) keep = TRUE;
119 else if (FILTER_MATCH("deny-name-beg", vprefix)) keep = FALSE;
120 else if (FILTER_MATCH("acpt-name-end", vsuffix)) keep = TRUE;
121 else if (FILTER_MATCH("deny-name-end", vsuffix)) keep = FALSE;
122 else if (FILTER_MATCH("acpt-name-any", vany)) keep = TRUE;
123 else if (FILTER_MATCH("deny-name-any", vany)) keep = FALSE;
124 else if (CSTREQ(obj->key, "acpt-all")) keep = TRUE;
125 else if (CSTREQ(obj->key, "deny-all")) keep = FALSE;
126 else
127 continue;
128
129 if (keep)
130 break;
131 else
132 { /* skip */
133 vstr_del(s2, 1, ns1);
134 return (TRUE);
135 }
136 } while ((obj = bag_iter_nxt(iter)));
137 }
138
139 vstr_add_vstr(s1, s1->len, s2, 1, ns1, 0);
140 vstr_del(s2, 1, ns1);
141
142 return (TRUE);
143 }
144
ex_dir_filter_process_limit(Vstr_base * s1,Vstr_base * s2,int * parsed_header)145 static void ex_dir_filter_process_limit(Vstr_base *s1, Vstr_base *s2,
146 int *parsed_header)
147 {
148 while (s2->len)
149 { /* Finish processing read data (try writing if we need memory) */
150 int proc_data = ex_dir_filter_process(s1, s2, parsed_header);
151
152 if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
153 io_block(-1, STDOUT_FILENO);
154 }
155 }
156
ex_dir_filter_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)157 static void ex_dir_filter_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2,
158 int fd)
159 {
160 int parsed_header[1] = {FALSE};
161
162 while (TRUE)
163 {
164 int io_w_state = IO_OK;
165 int io_r_state = io_get(s2, fd);
166
167 if (io_r_state == IO_EOF)
168 break;
169
170 ex_dir_filter_process(s1, s2, parsed_header);
171
172 io_w_state = io_put(s1, STDOUT_FILENO);
173
174 io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
175 }
176
177 ex_dir_filter_process_limit(s1, s2, parsed_header);
178 }
179
ex_dir_filter_init(Vstr_base * s1)180 static void ex_dir_filter_init(Vstr_base *s1)
181 {
182 if (!vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') ||
183 !vstr_sc_fmt_add_all(s1->conf))
184 errno = ENOMEM, err(EXIT_FAILURE, "init");
185 }
186
usage(const char * program_name,int ret,const char * prefix)187 static void usage(const char *program_name, int ret, const char *prefix)
188 {
189 Vstr_base *out = vstr_make_base(NULL);
190
191 if (!out)
192 errno = ENOMEM, err(EXIT_FAILURE, "usage");
193
194 vstr_add_fmt(out, 0, "%s\n"
195 "Usage: %s [-hV] [FILES]\n"
196 " or: %s OPTION\n"
197 " --accept-name-eq\n"
198 " --acpt-name-eq -A - Accept names equal to argument.\n"
199 " --deny-name-eq -D - Deny names equal to argument.\n"
200 " --accept-name-beg\n"
201 " --acpt-name-beg - Accept names begining with the argument.\n"
202 " --deny-name-beg - Deny names begining with the argument.\n"
203 " --accept-name-end\n"
204 " --acpt-name-end - Accept names ending with the argument.\n"
205 " --deny-name-end - Deny names ending with the argument.\n"
206 " --accept-name-any\n"
207 " --acpt-name-any - Accept names with the argument.\n"
208 " --deny-name-any - Deny names with the argument.\n"
209 " --accept-all\n"
210 " --acpt-all - Accept everything.\n"
211 " --deny-all - Deny everything.\n"
212 " --help -h - Print this message.\n"
213 " --version -V - Print the version string.\n",
214 prefix, program_name, program_name);
215
216 if (io_put_all(out, ret ? STDERR_FILENO : STDOUT_FILENO) == IO_FAIL)
217 err(EXIT_FAILURE, "write");
218
219 exit (ret);
220 }
221
ex_dir_filter_cmd_line(int * passed_argc,char ** passed_argv[])222 static void ex_dir_filter_cmd_line(int *passed_argc, char **passed_argv[])
223 {
224 int argc = *passed_argc;
225 char **argv = *passed_argv;
226 char optchar = 0;
227 const char *program_name = NULL;
228 struct option long_options[] =
229 {
230 /* allow filtering on size etc. */
231 {"help", no_argument, NULL, 'h'},
232
233 {"accept-name-eq", required_argument, NULL, 'A'},
234 {"acpt-name-eq", required_argument, NULL, 'A'},
235 {"deny-name-eq", required_argument, NULL, 'D'},
236
237 {"accept-name-beg", required_argument, NULL, 1},
238 {"acpt-name-beg", required_argument, NULL, 1},
239 {"deny-name-beg", required_argument, NULL, 2},
240
241 {"accept-name-end", required_argument, NULL, 3},
242 {"acpt-name-end", required_argument, NULL, 3},
243 {"deny-name-end", required_argument, NULL, 4},
244
245 {"accept-name-any", required_argument, NULL, 5},
246 {"acpt-name-any", required_argument, NULL, 5},
247 {"deny-name-any", required_argument, NULL, 6},
248
249 {"accept-all", no_argument, NULL, 7},
250 {"acpt-all", no_argument, NULL, 7},
251 {"deny-all", no_argument, NULL, 8},
252
253 {"version", no_argument, NULL, 'V'},
254 {NULL, 0, NULL, 0}
255 };
256 Vstr_base *out = vstr_make_base(NULL);
257
258 if (!out)
259 errno = ENOMEM, err(EXIT_FAILURE, "command line");
260
261 program_name = opt_program_name(argv[0], "jdir_filter");
262
263 if (!(filters = bag_make(argc, bag_cb_free_nothing, bag_cb_free_nothing)))
264 errno = ENOMEM, err(EXIT_FAILURE, "init");
265
266 while ((optchar = getopt_long(argc, argv, "A:D:hV",
267 long_options, NULL)) != -1)
268 {
269 switch (optchar)
270 {
271 case '?': usage(program_name, EXIT_FAILURE, "");
272 case 'h': usage(program_name, EXIT_SUCCESS, "");
273
274 case 'V':
275 vstr_add_fmt(out, 0,"\
276 %s version 1.0.0, compiled on %s.\n\
277 Written by James Antill\n\
278 \n\
279 Uses Vstr string library.\n\
280 ",
281 program_name, __DATE__);
282
283 if (io_put_all(out, STDOUT_FILENO) == IO_FAIL)
284 err(EXIT_FAILURE, "write");
285
286 exit (EXIT_SUCCESS);
287
288 case 'A': bag_add_cstr(filters, "acpt-name-eq", optarg); break;
289 case 'D': bag_add_cstr(filters, "deny-name-eq", optarg); break;
290 case 1: bag_add_cstr(filters, "acpt-name-beg", optarg); break;
291 case 2: bag_add_cstr(filters, "deny-name-beg", optarg); break;
292 case 3: bag_add_cstr(filters, "acpt-name-end", optarg); break;
293 case 4: bag_add_cstr(filters, "deny-name-end", optarg); break;
294 case 5: bag_add_cstr(filters, "acpt-name-any", optarg); break;
295 case 6: bag_add_cstr(filters, "deny-name-any", optarg); break;
296 case 7: bag_add_cstr(filters, "acpt-all", NULL); break;
297 case 8: bag_add_cstr(filters, "deny-all", NULL); break;
298
299 default:
300 ASSERT(FALSE);
301 }
302 }
303 vstr_free_base(out); out = NULL;
304
305 argc -= optind;
306 argv += optind;
307
308 *passed_argc = argc;
309 *passed_argv = argv;
310 }
311
main(int argc,char * argv[])312 int main(int argc, char *argv[])
313 {
314 Vstr_base *s1 = NULL;
315 Vstr_base *s2 = ex_init(&s1); /* init the library etc. */
316 int count = 0;
317
318 ex_dir_filter_init(s1);
319
320 ex_dir_filter_cmd_line(&argc, &argv);
321
322 /* if no arguments are given just do stdin to stdout */
323 if (count >= argc)
324 {
325 io_fd_set_o_nonblock(STDIN_FILENO);
326 ex_dir_filter_read_fd_write_stdout(s1, s2, STDIN_FILENO);
327 }
328
329 /* loop through all arguments, open the dir specified
330 * and do the read/write loop */
331 while (count < argc)
332 {
333 int fd = io_open(argv[count]);
334
335 ex_dir_filter_read_fd_write_stdout(s1, s2, fd);
336
337 if (close(fd) == -1)
338 warn("close(%s)", argv[count]);
339
340 ++count;
341 }
342
343 bag_free(filters);
344
345 /* output all remaining data */
346 io_put_all(s1, STDOUT_FILENO);
347
348 exit (ex_exit(s1, s2));
349 }
350