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