diff options
Diffstat (limited to 'toys/pending/find.c')
-rw-r--r-- | toys/pending/find.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/toys/pending/find.c b/toys/pending/find.c new file mode 100644 index 00000000..dddca5f5 --- /dev/null +++ b/toys/pending/find.c @@ -0,0 +1,742 @@ +/* vi: set sw=4 ts=4: + * + * find.c - find files matching a criteria + * + * Copyright 2012 Tim Bird <tbird20d@gmail.com> + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/find.html + +USE_LINK(NEWTOY(find, "?", TOYFLAG_USR|TOYFLAG_BIN)) + +config FIND + bool "find" + default n + help + usage: find [<dir>] [<options] + + -name <pattern> match pattern + -type [fcdl] match file type + !, -a, -o not, and , or + (, ) group expressions + -mtime [-+]n match file modification time (to within 24 hours) + \t\t +=greater (older) than, -=less (younger) than +*/ + +/* To Do: + * -exec action + */ + +#include "toys.h" +#include <strings.h> +#include <time.h> + +/* to remove debugging statements, uncomment the #define and + * comment out the next line */ +//#define debug 0 +int debug = 0; + +#define DPRINTF(fmt, args...) if (debug) printf("DEBUG: " fmt, ##args) + +#define SECONDS_PER_DAY (24*60*60) + +#define SUCCESS 1 + +int have_action; + +struct filter_node { + struct filter_node *next; + int op; + union { + char *name_regex; + struct { + char time_op; + time_t time; + } t; + mode_t type; + struct { + int arg_path_index; + char **exec_args; + } e; + } data; +}; + +char exec_buf[1024]; + +static struct filter_node *filter_root; + +/* filter operation types */ +#define OP_UNKNOWN 0 +#define OP_NOT 1 +#define OP_OR 2 +#define OP_AND 3 + +#define LPAREN 4 +#define RPAREN 5 + +#define CHECK_NAME 7 +#define CHECK_MTIME 8 +#define CHECK_TYPE 9 + +#define ACTION_PRINT 20 +#define ACTION_PRINT0 21 +#define ACTION_EXEC 22 + +#define TEST_LT 0 +#define TEST_EQ 1 +#define TEST_GT 2 + +void dump_node(struct filter_node *node) +{ + char c, **arg_array; + int i; + + switch(node->op) { + case CHECK_NAME: + printf("-name '%s' ", node->data.name_regex); + break; + case CHECK_MTIME: + printf("-mtime %d '%s' ", node->data.t.time_op, + ctime(&node->data.t.time)); + break; + case CHECK_TYPE: + switch(node->data.type) { + case S_IFREG: c='f'; + break; + case S_IFDIR: c='d'; + break; + case S_IFCHR: c='c'; + break; + case S_IFBLK: c='b'; + break; + case S_IFLNK: c='l'; + break; + case S_IFSOCK: c='s'; + break; + case S_IFIFO: c='p'; + break; + default: c='u'; + break; + } + + printf("-type %c ", c); + break; + case OP_NOT: + printf("! "); + break; + case OP_OR: + printf("-o "); + break; + case OP_AND: + printf("-a "); + break; + case LPAREN: + printf("( "); + break; + case RPAREN: + printf(") "); + break; + case ACTION_PRINT: + printf("-print "); + break; + case ACTION_PRINT0: + printf("-print0 "); + break; + case ACTION_EXEC: + printf("-exec "); + arg_array=node->data.e.exec_args; + for(i=0; arg_array[i]; i++) { + printf("%s ", arg_array[i]); + } + printf("; "); + break; + default: + printf("unknown node type (%d) ", node->op); + break; + } +} + +void dump_filters(struct filter_node *node) +{ + if (!node) { + printf("no filters\n"); + return; + } + do { + dump_node(node); + node = node->next; + } while(node); + printf("\n"); +} + +/* executes the command for a filter node + * return the program return value (0=success) + */ +int do_exec(struct filter_node *filter, struct dirtree *node) +{ + char *path; + int plen; + char **arg_array; + int i; + pid_t pid; + int ccode; + int status; + + arg_array = filter->data.e.exec_args; + if (filter->data.e.arg_path_index) { + path = dirtree_path(node, &plen); + arg_array[filter->data.e.arg_path_index] = path; + } else { + path = NULL; + } + DPRINTF("Try to execute: '"); + for(i=0; arg_array[i]; i++) { + DPRINTF("%s ", arg_array[i]); + } + DPRINTF("' here!\n"); + + pid = fork(); + if (pid==0) { + /* child */ + ccode = execvp(arg_array[0], arg_array); + if (ccode<0) { + printf("Error: problem executing sub-program %s\n", arg_array[0]); + exit(ccode); + } + } else { + /* parent */ + /* wait for child */ + waitpid(pid, &status, 0); + ccode = WEXITSTATUS(status); + } + free(path); + DPRINTF("do_exec() returning %d\n", ccode); + return ccode; +} + +/* prefix evaluator */ +/* returns 0 for failure or SUCCESS for success */ +int evaluate(struct filter_node *filter, struct dirtree *node, + struct filter_node **fnext) +{ + int result, result2; + char *path; + int plen = 0; + struct stat st_buf; + time_t node_time; + char terminator; + + /* if no filters, success */ + if (!filter) { + *fnext = NULL; + return SUCCESS; + } + + if (debug) { + /* show filter node */ + DPRINTF("eval:"); + dump_node(filter); + DPRINTF("\n"); + } + + if (filter->op==OP_NOT) { + result = evaluate(filter->next, node, fnext); + if (result==0) { + return SUCCESS; + } else { + return 0; + } + } + if (filter->op==OP_OR) { + result = evaluate(filter->next, node, fnext); + result2 = evaluate(*fnext, node, fnext); + if (result) { + return result; + } else { + if (result2) { + return result2; + } else { + return 0; + } + } + } + if (filter->op==OP_AND) { + result = evaluate(filter->next, node, fnext); + if (result) { + result2 = evaluate(*fnext, node, fnext); + if (result && result2) { + return SUCCESS; + } + } + return 0; + } + /* we're down to single operations, mark our position */ + *fnext = filter->next; + if (filter->op==CHECK_NAME) { + //if (regex_cmp(fn->name_regex, node->name)==0) + if (strcmp(filter->data.name_regex, node->name)==0) + return SUCCESS; + else + return 0; + } + if (filter->op==CHECK_MTIME) { + /* do mtime check */ + path = dirtree_path(node, &plen); + result = stat(path, &st_buf); + free(path); + if (result<0) { + return 0; + } + node_time = st_buf.st_mtime/SECONDS_PER_DAY; + result = SUCCESS; + switch (filter->data.t.time_op) { + /* do time compare here */ + case TEST_LT: + /* FIXTHIS - should result be < or <= ?*/ + if (node_time > filter->data.t.time) + result = 0; + break; + case TEST_GT: + if (node_time < filter->data.t.time) + result = 0; + break; + case TEST_EQ: + if (node_time != filter->data.t.time) + result = 0; + break; + default: + /* how'd I get here? */ + result = 0; + break; + } + return result; + + } + if (filter->op==CHECK_TYPE) { + path = dirtree_path(node, &plen); + result = lstat(path, &st_buf); + free(path); + if (result<0) { + return 0; + } + if ((st_buf.st_mode & S_IFMT) == filter->data.type) { + return SUCCESS; + } else { + return 0; + } + } + + + if (filter->op==ACTION_PRINT || filter->op==ACTION_PRINT0) { + if (filter->op==ACTION_PRINT) { + terminator = '\n'; + } else { + terminator = 0; + } + path = dirtree_path(node, &plen); + printf("%s%c", path, terminator); + free(path); + return SUCCESS; + } + + if (filter->op==ACTION_EXEC) { + if (do_exec(filter, node)==0) { + return SUCCESS; + } else { + return 0; + } + } + printf("ERROR: ran out of operations in filter list!!\n"); + return SUCCESS; +} + +int check_node_callback(struct dirtree *node) +{ + char *path; + int plen = 0; + int result; + struct filter_node *junk; + + /* only recurse on "." at the root */ + /* so, don't recurse on 1) non-root "." and 2) any ".." */ + //printf("node->name = %s\n", node->name); + DPRINTF("node->name=%s\n", node->name); + if (node->name[0] == '.' && + ((!node->name[1] && node->parent) || + (node->name[1]=='.' && !node->name[2]))) + return 0; + + DPRINTF("passed . and .. check, evaluating filters...\n"); + result = evaluate(filter_root, node, &junk); + DPRINTF("filter evaluation result=%d\n", result); + if (result & SUCCESS & !have_action) { + /* default action is just print the path */ + path = dirtree_path(node, &plen); + printf("%s\n", path); + free(path); + } + return DIRTREE_RECURSE; +} + + +void build_test_filter(void) +{ + struct filter_node *node; + int test=3; + + if (test==1) { /* test -name */ + printf("using test filter: '-name 'tail.c''\n"); + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + node->next = NULL; + node->op = CHECK_NAME; + node->data.name_regex = "tail.c"; + filter_root = node; + } + if (test==2) { /* test 'not' */ + printf("using test filter: '! -name 'tail.c''\n"); + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + node->next = NULL; + node->op = CHECK_NAME; + node->data.name_regex = "tail.c"; + filter_root = node; + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + /* put a not on the stack before the check_name */ + node->op = OP_NOT; + /* push this node */ + node->next = filter_root; + filter_root = node; + } + if (test==3) { /* test 'or' */ + printf("using test filter: '-name 'tail.c' -o -name 'other.c''\n"); + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + node->next = NULL; + node->op = CHECK_NAME; + node->data.name_regex = "other.c"; + filter_root = node; + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + node->next = filter_root; + node->op = CHECK_NAME; + node->data.name_regex = "tail.c"; + filter_root = node; + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + /* put an OR on the stack before the check_names */ + node->op = OP_OR; + /* push this node */ + node->next = filter_root; + filter_root = node; + } +} + +void build_filter_list(void) +{ + struct filter_node *node_list; + struct filter_node *op_stack; + struct filter_node *node, *op_node; + struct filter_node *next; + + char *arg, **arg_array; + int i, j; + + /* part optargs here and build a filter list in prefix format */ + + /* DEBUG - print optargs */ + if (debug) { + for(i=0; toys.optargs[i]; i++) { + printf("optargs[%d]=%s\n", i, toys.optargs[i]); + } + } + + node_list = NULL; + toybuf[0] = 0; + have_action = 0; + for(i=0; toys.optargs[i]; i++) { + node = (struct filter_node *) + xmalloc(sizeof(struct filter_node)); + node->op = OP_UNKNOWN; + arg = toys.optargs[i]; + if (strcmp(arg, "-o") == 0) { + node->op = OP_OR; + } + if (strcmp(arg, "-a") == 0) { + node->op = OP_AND; + } + if (strcmp(arg, "!")==0) { + node->op = OP_NOT; + } + if (strcmp(arg, "(") == 0) { + node->op = LPAREN; + } + if (strcmp(arg, ")") == 0) { + node->op = RPAREN; + } + if (strcmp(arg, "--debug") == 0) { + debug = 1; + continue; + } + + if (strcmp(arg, "-name") == 0) { + node->op = CHECK_NAME; + arg = toys.optargs[i+1]; + if (!arg) { + printf("Error: missing argument to -name\n"); + exit(1); + } + node->data.name_regex = arg; + i++; + } + + if (strcmp(arg, "-mtime") == 0) { + node->op = CHECK_MTIME; + arg = toys.optargs[i+1]; + if (!arg) { + printf("Error: missing argument to -mtime\n"); + exit(1); + } + switch(arg[0]) { + case '+': + node->data.t.time_op=TEST_GT; + arg++; + break; + case '-': + node->data.t.time_op=TEST_LT; + arg++; + break; + default: + node->data.t.time_op=TEST_EQ; + break; + } + /* convert to days (very crudely) */ + node->data.t.time = atoi(arg)/SECONDS_PER_DAY; + i++; + } + + if (strcmp(arg, "-type") == 0) { + node->op = CHECK_TYPE; + arg = toys.optargs[i+1]; + if (!arg) { + printf("Error: missing argument to -type\n"); + exit(1); + } + switch(arg[0]) { + case 'f': + node->data.type = S_IFREG; + break; + case 'd': + node->data.type = S_IFDIR; + break; + case 'c': + node->data.type = S_IFCHR; + break; + case 'b': + node->data.type = S_IFBLK; + break; + case 'l': + node->data.type = S_IFLNK; + break; + case 's': + node->data.type = S_IFSOCK; + break; + case 'p': + /* named pipe */ + node->data.type = S_IFIFO; + break; + default: + printf("Error: unknown file type '%s'\n", arg); + exit(1); + } + i++; + } + if (strcmp(arg, "-print") == 0) { + node->op = ACTION_PRINT; + have_action = 1; + } + if (strcmp(arg, "-print0") == 0) { + node->op = ACTION_PRINT0; + have_action = 1; + } + if (strcmp(arg, "-exec") == 0) { + node->op = ACTION_EXEC; + have_action = 1; + exec_buf[0] = 0; + j = 0; + arg_array = xmalloc(sizeof(char *)*(j+1)); + i++; + arg = toys.optargs[i]; + while (arg && (strcmp(arg, ";") != 0)) { + /* new method */ + arg_array = xrealloc(arg_array, sizeof(char *) * (j+2)); + arg_array[j] = arg; + if (strcmp(arg, "{}") == 0) { + node->data.e.arg_path_index = j; + } + j++; + + i++; + arg = toys.optargs[i]; + } + if (!arg) { + printf("Error: missing ';' in exec command\n"); + exit(1); + } + arg_array[j] = 0; + node->data.e.exec_args = arg_array; + } + if (node->op == OP_UNKNOWN) { + if( arg[0]=='-') { + printf("Error: unknown option '%s'\n", arg); + exit(1); + } else { + /* use toybuf as start directory */ + strcpy(toybuf, arg); + } + } else { + /* push node */ + node->next = node_list;; + node_list = node; + } + + } + if (debug) { + printf("here is the infix node list (reversed):\n"); + dump_filters(node_list); + } + + /* now convert from infix to prefix */ + filter_root = NULL; + op_stack = NULL; + node = node_list; + while( node ) { + next = node->next; + switch( node->op ) { + case OP_AND: + case OP_OR: + case OP_NOT: + case RPAREN: + /* push to opstack */ + node->next = op_stack; + op_stack = node; + break; + case LPAREN: + free(node); + /* pop opstack to output (up to rparen) */ + op_node = op_stack; + while (op_node && op_node->op != RPAREN) { + /* remove from op_stack */ + op_stack = op_node->next; + /* push to output */ + op_node->next = filter_root; + filter_root = op_node; + /* get next node */ + op_node = op_stack; + } + /* rparen should be on op_stack */ + if (!op_stack) { + printf("Error: missing right paren\n"); + exit(1); + } + /* remove rparen from op_stack */ + op_stack = op_stack->next; + free(op_node); + break; + default: + /* push to output */ + node->next = filter_root; + filter_root = node; + break; + } + node = next; + } + /* end of input - push opstack to output */ + /* pop opstack to output till empty */ + op_node = op_stack; + while (op_node) { + /*if (op_node->op == RPAREN || op_node->op == LPAREN) { + printf("Error: extra paren found\n"); + exit(1); + } + */ + op_stack = op_node->next; + op_node->next = filter_root; + filter_root = op_node; + op_node = op_stack; + } +} + +int test_callback(struct dirtree *node) +{ + char *path; + int plen; + + //printf("%s: ", node->name); + + /* skip non-root '.' and all '..' */ + if (node->name[0] == '.' && + ((!node->name[1] && node->parent) || + (node->name[1]=='.' && !node->name[2]))) { + //printf("skipping\n"); + return 0; + } + + //printf("\n"); + path = dirtree_path(node, &plen); + printf("%s\n", path); + free(path); + return DIRTREE_RECURSE; +} + +void dt_test(void) +{ + struct dirtree *root; + + printf("testing dirtree routines...\n"); + + root = dirtree_read(".", test_callback); + printf("root=%p\n", root); + exit(0); +} + +void find_main(void) +{ + int i; + + /* do a special check for --debug */ + for(i=0; toys.optargs[i]; i++) { + if (strcmp(toys.optargs[i], "--debug")==0) { + debug = 1; + printf("[debug mode on]\n"); + /* shift args down, deleting '--debug' */ + for (;toys.optargs[i]; i++) { + toys.optargs[i] = toys.optargs[i+1]; + } + } + } + + /* change if to enalbe dt_test routine */ + if (debug && 0) { + dt_test(); + } + + /* parse filters, if present */ + /* also, fill toybuf with the directory to start in, if present */ + build_filter_list(); + if (debug) { + printf("using prefix filter list:\n"); + dump_filters(filter_root); + } + + /* DEBUG - override parsed filter list with test filter */ + //build_test_filter(); + //dump_filters(filter_root); + + /* FIXTHIS - parse actions, if present */ + + if (toybuf[0]==0) { + strcpy(toybuf, "."); + } + dirtree_read(toybuf, check_node_callback); +} |