comparison toys/pending/find.c @ 848:feea7a8ecbb1

Redo find's indenting from tabs to two spaces.
author Rob Landley <rob@landley.net>
date Wed, 10 Apr 2013 19:58:21 -0500
parents 2506cbdccd20
children 917d6e051b82
comparison
equal deleted inserted replaced
847:2506cbdccd20 848:feea7a8ecbb1
7 * See http://opengroup.org/onlinepubs/9699919799/utilities/find.html 7 * See http://opengroup.org/onlinepubs/9699919799/utilities/find.html
8 8
9 USE_FIND(NEWTOY(find, "?", TOYFLAG_USR|TOYFLAG_BIN)) 9 USE_FIND(NEWTOY(find, "?", TOYFLAG_USR|TOYFLAG_BIN))
10 10
11 config FIND 11 config FIND
12 bool "find" 12 bool "find"
13 default n 13 default n
14 help 14 help
15 usage: find [<dir>] [<options] 15 usage: find [<dir>] [<options]
16 16
17 -name <pattern> match pattern 17 -name <pattern> match pattern
18 -type [fcdl] match file type 18 -type [fcdl] match file type
19 !, -a, -o not, and , or 19 !, -a, -o not, and , or
20 (, ) group expressions 20 (, ) group expressions
21 -mtime [-+]n match file modification time (to within 24 hours) 21 -mtime [-+]n match file modification time (to within 24 hours)
22 \t\t +=greater (older) than, -=less (younger) than 22 \t\t +=greater (older) than, -=less (younger) than
23 */ 23 */
24 24
25 /* To Do: 25 /* To Do:
26 * -exec action 26 * -exec action
27 */ 27 */
31 #define SECONDS_PER_DAY (24*60*60) 31 #define SECONDS_PER_DAY (24*60*60)
32 32
33 int have_action; 33 int have_action;
34 34
35 struct filter_node { 35 struct filter_node {
36 struct filter_node *next; 36 struct filter_node *next;
37 int op; 37 int op;
38 union { 38 union {
39 char *name_regex; 39 char *name_regex;
40 struct { 40 struct {
41 char time_op; 41 char time_op;
42 time_t time; 42 time_t time;
43 } t; 43 } t;
44 mode_t type; 44 mode_t type;
45 struct { 45 struct {
46 int arg_path_index; 46 int arg_path_index;
47 char **exec_args; 47 char **exec_args;
48 } e; 48 } e;
49 } data; 49 } data;
50 }; 50 };
51 51
52 static struct filter_node *filter_root; 52 static struct filter_node *filter_root;
53 53
54 /* filter operation types */ 54 /* filter operation types */
75 /* executes the command for a filter node 75 /* executes the command for a filter node
76 * return the program return value (0=success) 76 * return the program return value (0=success)
77 */ 77 */
78 static int do_exec(struct filter_node *filter, struct dirtree *node) 78 static int do_exec(struct filter_node *filter, struct dirtree *node)
79 { 79 {
80 char *path; 80 char *path;
81 int plen; 81 int plen;
82 char **arg_array; 82 char **arg_array;
83 pid_t pid; 83 pid_t pid;
84 int ccode; 84 int ccode;
85 int status; 85 int status;
86 86
87 arg_array = filter->data.e.exec_args; 87 arg_array = filter->data.e.exec_args;
88 if (filter->data.e.arg_path_index) { 88 if (filter->data.e.arg_path_index) {
89 path = dirtree_path(node, &plen); 89 path = dirtree_path(node, &plen);
90 arg_array[filter->data.e.arg_path_index] = path; 90 arg_array[filter->data.e.arg_path_index] = path;
91 } else { 91 } else {
92 path = NULL; 92 path = NULL;
93 } 93 }
94 94
95 pid = fork(); 95 pid = fork();
96 if (!pid) { 96 if (!pid) {
97 /* child */ 97 /* child */
98 xexec(arg_array); 98 xexec(arg_array);
99 } else { 99 } else {
100 /* parent */ 100 /* parent */
101 /* wait for child */ 101 /* wait for child */
102 waitpid(pid, &status, 0); 102 waitpid(pid, &status, 0);
103 ccode = WEXITSTATUS(status); 103 ccode = WEXITSTATUS(status);
104 } 104 }
105 free(path); 105 free(path);
106 106
107 return ccode; 107 return ccode;
108 } 108 }
109 109
110 /* prefix evaluator */ 110 /* prefix evaluator */
111 /* returns 0 for failure or 1 for success */ 111 /* returns 0 for failure or 1 for success */
112 static int evaluate(struct filter_node *filter, struct dirtree *node, 112 static int evaluate(struct filter_node *filter, struct dirtree *node,
113 struct filter_node **fnext) 113 struct filter_node **fnext)
114 { 114 {
115 int result; 115 int result;
116 char *path; 116 char *path;
117 int plen = 0; 117 int plen = 0;
118 struct stat st_buf; 118 struct stat st_buf;
119 time_t node_time; 119 time_t node_time;
120 char terminator; 120 char terminator;
121 121
122 /* if no filters, success */ 122 /* if no filters, success */
123 if (!filter) { 123 if (!filter) {
124 *fnext = NULL; 124 *fnext = NULL;
125 return 1; 125 return 1;
126 } 126 }
127 127
128 if (filter->op==OP_NOT) { 128 if (filter->op==OP_NOT) {
129 return !evaluate(filter->next, node, fnext); 129 return !evaluate(filter->next, node, fnext);
130 } 130 }
131 if (filter->op==OP_OR) { 131 if (filter->op==OP_OR) {
132 return evaluate(filter->next, node, fnext) || evaluate(*fnext, node, fnext); 132 return evaluate(filter->next, node, fnext) || evaluate(*fnext, node, fnext);
133 } 133 }
134 if (filter->op==OP_AND) { 134 if (filter->op==OP_AND) {
135 return evaluate(filter->next, node, fnext) && evaluate(*fnext, node, fnext); 135 return evaluate(filter->next, node, fnext) && evaluate(*fnext, node, fnext);
136 } 136 }
137 /* we're down to single operations, mark our position */ 137 /* we're down to single operations, mark our position */
138 *fnext = filter->next; 138 *fnext = filter->next;
139 if (filter->op==CHECK_NAME) { 139 if (filter->op==CHECK_NAME) {
140 // TODO: Do a regex comparison 140 // TODO: Do a regex comparison
141 return !strcmp(filter->data.name_regex, node->name); 141 return !strcmp(filter->data.name_regex, node->name);
142 } 142 }
143 if (filter->op==CHECK_MTIME) { 143 if (filter->op==CHECK_MTIME) {
144 /* do mtime check */ 144 /* do mtime check */
145 path = dirtree_path(node, &plen); 145 path = dirtree_path(node, &plen);
146 result = stat(path, &st_buf); 146 result = stat(path, &st_buf);
147 free(path); 147 free(path);
148 if (result<0) { 148 if (result<0) {
149 return 0; 149 return 0;
150 } 150 }
151 node_time = st_buf.st_mtime/SECONDS_PER_DAY; 151 node_time = st_buf.st_mtime/SECONDS_PER_DAY;
152 result = 1; 152 result = 1;
153 switch (filter->data.t.time_op) { 153 switch (filter->data.t.time_op) {
154 /* do time compare here */ 154 /* do time compare here */
155 case TEST_LT: 155 case TEST_LT:
156 /* FIXTHIS - should result be < or <= ?*/ 156 /* FIXTHIS - should result be < or <= ?*/
157 if (node_time > filter->data.t.time) 157 if (node_time > filter->data.t.time)
158 result = 0; 158 result = 0;
159 break; 159 break;
160 case TEST_GT: 160 case TEST_GT:
161 if (node_time < filter->data.t.time) 161 if (node_time < filter->data.t.time)
162 result = 0; 162 result = 0;
163 break; 163 break;
164 case TEST_EQ: 164 case TEST_EQ:
165 if (node_time != filter->data.t.time) 165 if (node_time != filter->data.t.time)
166 result = 0; 166 result = 0;
167 break; 167 break;
168 default: 168 default:
169 /* how'd I get here? */ 169 /* how'd I get here? */
170 result = 0; 170 result = 0;
171 break; 171 break;
172 } 172 }
173 return result; 173 return result;
174 174
175 } 175 }
176 if (filter->op==CHECK_TYPE) { 176 if (filter->op==CHECK_TYPE) {
177 path = dirtree_path(node, &plen); 177 path = dirtree_path(node, &plen);
178 result = lstat(path, &st_buf); 178 result = lstat(path, &st_buf);
179 free(path); 179 free(path);
180 if (result<0) { 180 if (result<0) {
181 return 0; 181 return 0;
182 } 182 }
183 return (st_buf.st_mode & S_IFMT) == filter->data.type; 183 return (st_buf.st_mode & S_IFMT) == filter->data.type;
184 } 184 }
185 185
186 186
187 if (filter->op==ACTION_PRINT || filter->op==ACTION_PRINT0) { 187 if (filter->op==ACTION_PRINT || filter->op==ACTION_PRINT0) {
188 if (filter->op==ACTION_PRINT) { 188 if (filter->op==ACTION_PRINT) {
189 terminator = '\n'; 189 terminator = '\n';
190 } else { 190 } else {
191 terminator = 0; 191 terminator = 0;
192 } 192 }
193 path = dirtree_path(node, &plen); 193 path = dirtree_path(node, &plen);
194 printf("%s%c", path, terminator); 194 printf("%s%c", path, terminator);
195 free(path); 195 free(path);
196 return 1; 196 return 1;
197 } 197 }
198 198
199 if (filter->op==ACTION_EXEC) { 199 if (filter->op==ACTION_EXEC) {
200 return !do_exec(filter, node); 200 return !do_exec(filter, node);
201 } 201 }
202 error_msg("Ran out of operations in filter list!"); 202 error_msg("Ran out of operations in filter list!");
203 return 1; 203 return 1;
204 } 204 }
205 205
206 static int check_node_callback(struct dirtree *node) 206 static int check_node_callback(struct dirtree *node)
207 { 207 {
208 char *path; 208 char *path;
209 int plen = 0; 209 int plen = 0;
210 int result; 210 int result;
211 struct filter_node *junk; 211 struct filter_node *junk;
212 212
213 /* only recurse on "." at the root */ 213 /* only recurse on "." at the root */
214 /* so, don't recurse on 1) non-root "." and 2) any ".." */ 214 /* so, don't recurse on 1) non-root "." and 2) any ".." */
215 215
216 if (node->name[0] == '.' && 216 if (node->name[0] == '.' &&
217 ((!node->name[1] && node->parent) || 217 ((!node->name[1] && node->parent) ||
218 (node->name[1]=='.' && !node->name[2]))) 218 (node->name[1]=='.' && !node->name[2])))
219 return 0; 219 return 0;
220 220
221 result = evaluate(filter_root, node, &junk); 221 result = evaluate(filter_root, node, &junk);
222 if (result & !have_action) { 222 if (result & !have_action) {
223 /* default action is just print the path */ 223 /* default action is just print the path */
224 path = dirtree_path(node, &plen); 224 path = dirtree_path(node, &plen);
225 printf("%s\n", path); 225 printf("%s\n", path);
226 free(path); 226 free(path);
227 } 227 }
228 return DIRTREE_RECURSE; 228 return DIRTREE_RECURSE;
229 } 229 }
230 230
231 231
232 static void build_filter_list(void) 232 static void build_filter_list(void)
233 { 233 {
234 struct filter_node *node_list; 234 struct filter_node *node_list;
235 struct filter_node *op_stack; 235 struct filter_node *op_stack;
236 struct filter_node *node, *op_node; 236 struct filter_node *node, *op_node;
237 struct filter_node *next; 237 struct filter_node *next;
238 238
239 char *arg, **arg_array; 239 char *arg, **arg_array;
240 int i, j; 240 int i, j;
241 241
242 /* part optargs here and build a filter list in prefix format */ 242 /* part optargs here and build a filter list in prefix format */
243 243
244 node_list = NULL; 244 node_list = NULL;
245 toybuf[0] = 0; 245 toybuf[0] = 0;
246 have_action = 0; 246 have_action = 0;
247 for(i=0; toys.optargs[i]; i++) { 247 for(i=0; toys.optargs[i]; i++) {
248 node = (struct filter_node *) 248 node = (struct filter_node *)
249 xmalloc(sizeof(struct filter_node)); 249 xmalloc(sizeof(struct filter_node));
250 node->op = OP_UNKNOWN; 250 node->op = OP_UNKNOWN;
251 arg = toys.optargs[i]; 251 arg = toys.optargs[i];
252 if (!strcmp(arg, "-o")) { 252 if (!strcmp(arg, "-o")) {
253 node->op = OP_OR; 253 node->op = OP_OR;
254 } 254 }
255 if (!strcmp(arg, "-a")) { 255 if (!strcmp(arg, "-a")) {
256 node->op = OP_AND; 256 node->op = OP_AND;
257 } 257 }
258 if (!strcmp(arg, "!")) { 258 if (!strcmp(arg, "!")) {
259 node->op = OP_NOT; 259 node->op = OP_NOT;
260 } 260 }
261 if (!strcmp(arg, "(")) { 261 if (!strcmp(arg, "(")) {
262 node->op = LPAREN; 262 node->op = LPAREN;
263 } 263 }
264 if (!strcmp(arg, ")")) { 264 if (!strcmp(arg, ")")) {
265 node->op = RPAREN; 265 node->op = RPAREN;
266 } 266 }
267 267
268 if (!strcmp(arg, "-name")) { 268 if (!strcmp(arg, "-name")) {
269 node->op = CHECK_NAME; 269 node->op = CHECK_NAME;
270 arg = toys.optargs[++i]; 270 arg = toys.optargs[++i];
271 if (!arg) error_exit("Missing argument to -name\n"); 271 if (!arg) error_exit("Missing argument to -name\n");
272 node->data.name_regex = arg; 272 node->data.name_regex = arg;
273 } 273 }
274 274
275 if (!strcmp(arg, "-mtime")) { 275 if (!strcmp(arg, "-mtime")) {
276 node->op = CHECK_MTIME; 276 node->op = CHECK_MTIME;
277 arg = toys.optargs[++i]; 277 arg = toys.optargs[++i];
278 if (!arg) error_exit("Missing argument to -mtime\n"); 278 if (!arg) error_exit("Missing argument to -mtime\n");
279 switch(arg[0]) { 279 switch(arg[0]) {
280 case '+': 280 case '+':
281 node->data.t.time_op=TEST_GT; 281 node->data.t.time_op=TEST_GT;
282 arg++; 282 arg++;
283 break; 283 break;
284 case '-': 284 case '-':
285 node->data.t.time_op=TEST_LT; 285 node->data.t.time_op=TEST_LT;
286 arg++; 286 arg++;
287 break; 287 break;
288 default: 288 default:
289 node->data.t.time_op=TEST_EQ; 289 node->data.t.time_op=TEST_EQ;
290 break; 290 break;
291 } 291 }
292 /* convert to days (very crudely) */ 292 /* convert to days (very crudely) */
293 node->data.t.time = atoi(arg)/SECONDS_PER_DAY; 293 node->data.t.time = atoi(arg)/SECONDS_PER_DAY;
294 } 294 }
295 295
296 if (!strcmp(arg, "-type")) { 296 if (!strcmp(arg, "-type")) {
297 node->op = CHECK_TYPE; 297 node->op = CHECK_TYPE;
298 arg = toys.optargs[++i]; 298 arg = toys.optargs[++i];
299 if (!arg) error_exit("Missing argument to -type\n"); 299 if (!arg) error_exit("Missing argument to -type\n");
300 switch(arg[0]) { 300 switch(arg[0]) {
301 case 'f': 301 case 'f':
302 node->data.type = S_IFREG; 302 node->data.type = S_IFREG;
303 break; 303 break;
304 case 'd': 304 case 'd':
305 node->data.type = S_IFDIR; 305 node->data.type = S_IFDIR;
306 break; 306 break;
307 case 'c': 307 case 'c':
308 node->data.type = S_IFCHR; 308 node->data.type = S_IFCHR;
309 break; 309 break;
310 case 'b': 310 case 'b':
311 node->data.type = S_IFBLK; 311 node->data.type = S_IFBLK;
312 break; 312 break;
313 case 'l': 313 case 'l':
314 node->data.type = S_IFLNK; 314 node->data.type = S_IFLNK;
315 break; 315 break;
316 case 's': 316 case 's':
317 node->data.type = S_IFSOCK; 317 node->data.type = S_IFSOCK;
318 break; 318 break;
319 case 'p': 319 case 'p':
320 /* named pipe */ 320 /* named pipe */
321 node->data.type = S_IFIFO; 321 node->data.type = S_IFIFO;
322 break; 322 break;
323 default: 323 default:
324 error_exit("Unknown file type '%s'\n", arg); 324 error_exit("Unknown file type '%s'\n", arg);
325 } 325 }
326 } 326 }
327 if (!strcmp(arg, "-print")) { 327 if (!strcmp(arg, "-print")) {
328 node->op = ACTION_PRINT; 328 node->op = ACTION_PRINT;
329 have_action = 1; 329 have_action = 1;
330 } 330 }
331 if (!strcmp(arg, "-print0")) { 331 if (!strcmp(arg, "-print0")) {
332 node->op = ACTION_PRINT0; 332 node->op = ACTION_PRINT0;
333 have_action = 1; 333 have_action = 1;
334 } 334 }
335 if (!strcmp(arg, "-exec")) { 335 if (!strcmp(arg, "-exec")) {
336 node->op = ACTION_EXEC; 336 node->op = ACTION_EXEC;
337 have_action = 1; 337 have_action = 1;
338 arg_array = xmalloc(sizeof(char *)); 338 arg_array = xmalloc(sizeof(char *));
339 arg = toys.optargs[++i]; 339 arg = toys.optargs[++i];
340 for (j = 0; arg && strcmp(arg, ";"); j++) { 340 for (j = 0; arg && strcmp(arg, ";"); j++) {
341 /* new method */ 341 /* new method */
342 arg_array = xrealloc(arg_array, sizeof(char *) * (j+2)); 342 arg_array = xrealloc(arg_array, sizeof(char *) * (j+2));
343 arg_array[j] = arg; 343 arg_array[j] = arg;
344 if (!strcmp(arg, "{}")) { 344 if (!strcmp(arg, "{}")) {
345 node->data.e.arg_path_index = j; 345 node->data.e.arg_path_index = j;
346 } 346 }
347 347
348 arg = toys.optargs[++i]; 348 arg = toys.optargs[++i];
349 } 349 }
350 if (!arg) error_exit("Missing ';' in exec command\n"); 350 if (!arg) error_exit("Missing ';' in exec command\n");
351 arg_array[j] = 0; 351 arg_array[j] = 0;
352 node->data.e.exec_args = arg_array; 352 node->data.e.exec_args = arg_array;
353 } 353 }
354 if (node->op == OP_UNKNOWN) { 354 if (node->op == OP_UNKNOWN) {
355 if (arg[0] == '-') error_exit("Unknown option '%s'\n", arg); 355 if (arg[0] == '-') error_exit("Unknown option '%s'\n", arg);
356 else { 356 else {
357 /* use toybuf as start directory */ 357 /* use toybuf as start directory */
358 strcpy(toybuf, arg); 358 strcpy(toybuf, arg);
359 } 359 }
360 } else { 360 } else {
361 /* push node */ 361 /* push node */
362 node->next = node_list;; 362 node->next = node_list;;
363 node_list = node; 363 node_list = node;
364 } 364 }
365 365
366 } 366 }
367 367
368 /* now convert from infix to prefix */ 368 /* now convert from infix to prefix */
369 filter_root = NULL; 369 filter_root = NULL;
370 op_stack = NULL; 370 op_stack = NULL;
371 node = node_list; 371 node = node_list;
372 while( node ) { 372 while( node ) {
373 next = node->next; 373 next = node->next;
374 switch( node->op ) { 374 switch( node->op ) {
375 case OP_AND: 375 case OP_AND:
376 case OP_OR: 376 case OP_OR:
377 case OP_NOT: 377 case OP_NOT:
378 case RPAREN: 378 case RPAREN:
379 /* push to opstack */ 379 /* push to opstack */
380 node->next = op_stack; 380 node->next = op_stack;
381 op_stack = node; 381 op_stack = node;
382 break; 382 break;
383 case LPAREN: 383 case LPAREN:
384 free(node); 384 free(node);
385 /* pop opstack to output (up to rparen) */ 385 /* pop opstack to output (up to rparen) */
386 op_node = op_stack; 386 op_node = op_stack;
387 while (op_node && op_node->op != RPAREN) { 387 while (op_node && op_node->op != RPAREN) {
388 /* remove from op_stack */ 388 /* remove from op_stack */
389 op_stack = op_node->next; 389 op_stack = op_node->next;
390 /* push to output */ 390 /* push to output */
391 op_node->next = filter_root; 391 op_node->next = filter_root;
392 filter_root = op_node; 392 filter_root = op_node;
393 /* get next node */ 393 /* get next node */
394 op_node = op_stack; 394 op_node = op_stack;
395 } 395 }
396 /* rparen should be on op_stack */ 396 /* rparen should be on op_stack */
397 if (!op_stack) error_exit("Missing right paren\n"); 397 if (!op_stack) error_exit("Missing right paren\n");
398 /* remove rparen from op_stack */ 398 /* remove rparen from op_stack */
399 op_stack = op_stack->next; 399 op_stack = op_stack->next;
400 free(op_node); 400 free(op_node);
401 break; 401 break;
402 default: 402 default:
403 /* push to output */ 403 /* push to output */
404 node->next = filter_root; 404 node->next = filter_root;
405 filter_root = node; 405 filter_root = node;
406 break; 406 break;
407 } 407 }
408 node = next; 408 node = next;
409 } 409 }
410 /* end of input - push opstack to output */ 410 /* end of input - push opstack to output */
411 /* pop opstack to output till empty */ 411 /* pop opstack to output till empty */
412 op_node = op_stack; 412 op_node = op_stack;
413 while (op_node) { 413 while (op_node) {
414 /*if (op_node->op == RPAREN || op_node->op == LPAREN) { 414 /*if (op_node->op == RPAREN || op_node->op == LPAREN) {
415 error_exit("Error: extra paren found\n"); 415 error_exit("Error: extra paren found\n");
416 } 416 }
417 */ 417 */
418 op_stack = op_node->next; 418 op_stack = op_node->next;
419 op_node->next = filter_root; 419 op_node->next = filter_root;
420 filter_root = op_node; 420 filter_root = op_node;
421 op_node = op_stack; 421 op_node = op_stack;
422 } 422 }
423 } 423 }
424 424
425 void find_main(void) 425 void find_main(void)
426 { 426 {
427 /* parse filters, if present */ 427 /* parse filters, if present */
428 /* also, fill toybuf with the directory to start in, if present */ 428 /* also, fill toybuf with the directory to start in, if present */
429 build_filter_list(); 429 build_filter_list();
430 430
431 /* FIXTHIS - parse actions, if present */ 431 /* FIXTHIS - parse actions, if present */
432 432
433 if (!toybuf[0]) { 433 if (!toybuf[0]) {
434 strcpy(toybuf, "."); 434 strcpy(toybuf, ".");
435 } 435 }
436 dirtree_read(toybuf, check_node_callback); 436 dirtree_read(toybuf, check_node_callback);
437 } 437 }