From e78b234a35508c64158f264f3c8da12592e8536e Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 1 Apr 2000 11:54:10 +0000 Subject: [PATCH] r225: Added support for searching multiple cases and shell-style wildcards. --- ROX-Filer/src/action.c | 40 +++++++---- ROX-Filer/src/find.c | 187 +++++++++++++++++++++++++++++++++++++++++++++---- ROX-Filer/src/find.h | 16 ++++- 3 files changed, 218 insertions(+), 25 deletions(-) diff --git a/ROX-Filer/src/action.c b/ROX-Filer/src/action.c index de0ee46c..7ae27626 100644 --- a/ROX-Filer/src/action.c +++ b/ROX-Filer/src/action.c @@ -305,15 +305,34 @@ static void show_condition_help(gpointer data) help = gtk_window_new(GTK_WINDOW_DIALOG); gtk_container_set_border_width(GTK_CONTAINER(help), 10); gtk_window_set_title(GTK_WINDOW(help), - "Find condition reference"); + "Find expression reference"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(help), vbox); text = gtk_label_new( - "The format of a condition is:\n" - "[ more help here ]\n" - "\nRead the ROX-Filer manual for full details."); + "An expression is a series of cases which are checked from left \n" + "to right. As soon as one matches, the expression is TRUE. If none \n" + "match then the expression is FALSE.\n" + "\n" + "Cases are separated by commas:\n" + "'*.htm', '*.html' (finds HTML files)\n" + "\n" + "Each case is a list of conditions which must all be met for the \n" + "case to succeed:\n" + "IsReg 'core' (finds a regular file called 'core')\n" + "Preceeding a condition by ! inverts the result of the condition:\n" + "IsDev ! '/dev/*' (finds device files not in /dev)\n" + "\n" + "Simple tests are: IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev,\n" + "IsPipe, IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsEmpty\n" + "IsExecutable, IsMine\n" + "\n" + "A pattern in single quotes is a shell-style wildcard pattern to \n" + "match. If it contains a slash then the match is agaist the full \n" + "path; otherwise it is agaist the leafname only.\n" + "\n" + "Read the ROX-Filer manual for full details."); gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT); gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0); @@ -710,8 +729,6 @@ static void read_new_entry_text(void) g_free(new_entry_string); new_entry_string = new->str; g_string_free(new, FALSE); - - g_print("[ new_entry_string = %s ]\n", new_entry_string); } /* Read until the user sends a reply. If ignore_quiet is TRUE then @@ -1039,7 +1056,6 @@ static gboolean do_find(char *path, char *dummy) { if (new_entry_string) { - g_print("[ find changed ]\n"); if (find_condition) find_condition_free(find_condition); find_condition = find_compile(new_entry_string); @@ -1115,7 +1131,6 @@ static gboolean do_chmod(char *path, char *dummy) { if (new_entry_string) { - g_print("[ chmod changed ]\n"); if (mode_change) mode_free(mode_change); mode_change = mode_compile(new_entry_string, @@ -1741,7 +1756,7 @@ void action_find(FilerWindow *filer_window) } if (!last_find_string) - last_find_string = g_strdup("core"); + last_find_string = g_strdup("'core'"); new_entry_string = last_find_string; gui_side = start_action(filer_window, find_cb, FALSE); @@ -1751,17 +1766,18 @@ void action_find(FilerWindow *filer_window) gui_side->show_info = TRUE; hbox = gtk_hbox_new(FALSE, 0); - label = gtk_label_new("Conditions:"); + label = gtk_label_new("Expression:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4); gui_side->default_string = &last_find_string; gui_side->entry = gtk_entry_new(); + gtk_widget_set_style(gui_side->entry, fixed_style); gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string); gtk_widget_set_sensitive(gui_side->entry, FALSE); gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4); gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed", entry_changed, gui_side); - gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0); - button = gtk_button_new_with_label("Show condition reference"); + gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4); + button = gtk_button_new_with_label("Show expression reference"); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", show_condition_help, NULL); diff --git a/ROX-Filer/src/find.c b/ROX-Filer/src/find.c index 8e88ec30..9f082e55 100644 --- a/ROX-Filer/src/find.c +++ b/ROX-Filer/src/find.c @@ -19,15 +19,28 @@ * Place, Suite 330, Boston, MA 02111-1307 USA */ -/* find.c - processes the find conditions */ +/* find.c - processes the find conditions + * + * A Condition is a tree structure. Each node has a test() fn which + * can be used to see whether the current file matches, and a free() fn + * which frees it. Both will recurse down the tree as needed. + */ #include "config.h" #include +#include #include "find.h" /* Static prototypes */ +static FindCondition *parse_expression(guchar **expression); +static FindCondition *parse_case(guchar **expression); + + +#define EAT ((*expression)++) +#define NEXT (**expression) +#define SKIP while (NEXT == ' ' || NEXT == '\t') EAT /**************************************************************** @@ -35,35 +48,185 @@ ****************************************************************/ /* Take a string and parse it, returning a condition object which - * can be passed to find_test_condition() later. + * can be passed to find_test_condition() later. NULL if the string + * is not a valid expression. */ FindCondition *find_compile(guchar *string) { - FindCondition *retval; + FindCondition *cond; + guchar **expression = &string; - retval = g_new(FindCondition, 1); - retval->name = g_strdup(string); + g_return_val_if_fail(string != NULL, NULL); - return retval; + cond = parse_expression(expression); + if (!cond) + return NULL; + + SKIP; + if (NEXT != '\0') + { + cond->free(cond); + cond = NULL; + } + + return cond; } gboolean find_test_condition(FindCondition *condition, guchar *path) { - guchar *slash, *leaf; + FindInfo info; + guchar *slash; + + g_return_val_if_fail(condition != NULL, FALSE); + g_return_val_if_fail(path != NULL, FALSE); + + info.fullpath = path; slash = strrchr(path, '/'); - leaf = slash ? slash + 1 : path; - - return g_strcasecmp(condition->name, leaf) == 0; + info.leaf = slash ? slash + 1 : path; + if (lstat(path, &info.stats)) + return FALSE; /* XXX: Log error */ + + return condition->test(condition, &info); } void find_condition_free(FindCondition *condition) { - g_free(condition->name); - g_free(condition); + g_return_if_fail(condition != NULL); + + condition->free(condition); } /**************************************************************** * INTERNAL FUNCTIONS * ****************************************************************/ +/* TESTING CODE */ + +static gboolean test_leaf(FindCondition *condition, FindInfo *info) +{ + return fnmatch(condition->data1, info->leaf, 0) == 0; +} + +static gboolean test_path(FindCondition *condition, FindInfo *info) +{ + return fnmatch(condition->data1, info->fullpath, FNM_PATHNAME) == 0; +} + +static gboolean test_OR(FindCondition *condition, FindInfo *info) +{ + FindCondition *first = (FindCondition *) condition->data1; + FindCondition *second = (FindCondition *) condition->data2; + + return first->test(first, info) || first->test(second, info); +} + +/* FREEING CODE */ + +/* Frees the structure and g_free()s both data items (NULL is OK) */ +static void free_simple(FindCondition *condition) +{ + g_return_if_fail(condition != NULL); + + g_free(condition->data1); + g_free(condition->data2); + g_free(condition); +} + +/* Treats data1 and data2 as conditions and frees recursively */ +static void free_binary_op(FindCondition *condition) +{ + FindCondition *first = (FindCondition *) condition->data1; + FindCondition *second = (FindCondition *) condition->data2; + + first->free(first); + second->free(second); + g_free(condition); +} + +/* PARSING CODE */ + +/* These all work in the same way - you give them an expression and the + * parse as much as they can, returning a condition for everything that + * was parsed and updating the expression pointer to the first unknown + * token. NULL indicates an error. + */ + + +/* An expression is a series of comma-separated cases, any of which + * may match. + */ +static FindCondition *parse_expression(guchar **expression) +{ + FindCondition *first, *second, *cond; + + first = parse_case(expression); + if (!first) + return NULL; + + SKIP; + if (NEXT != ',') + return first; + EAT; + + second = parse_expression(expression); + if (!second) + { + first->free(first); + return NULL; + } + + cond = g_new(FindCondition, 1); + cond->test = &test_OR; + cond->free = &free_binary_op; + cond->data1 = first; + cond->data2 = second; + + return cond; +} + +static FindCondition *parse_case(guchar **expression) +{ + FindCondition *cond = NULL; + GString *str; + FindTest test = &test_leaf; + + SKIP; + if (NEXT != '\'') + return NULL; + EAT; + + str = g_string_new(NULL); + + while (NEXT != '\'') + { + guchar c = NEXT; + + if (c == '\0') + goto out; + EAT; + + if (c == '\\' && NEXT == '\'') + { + c = NEXT; + EAT; + } + + if (c == '/') + test = &test_path; + + g_string_append_c(str, c); + } + EAT; + + cond = g_new(FindCondition, 1); + cond->test = test; + cond->free = &free_simple; + cond->data1 = str->str; + cond->data2 = NULL; + +out: + g_string_free(str, cond ? FALSE : TRUE); + + return cond; +} diff --git a/ROX-Filer/src/find.h b/ROX-Filer/src/find.h index 4a8af511..62eb6444 100644 --- a/ROX-Filer/src/find.h +++ b/ROX-Filer/src/find.h @@ -6,12 +6,26 @@ */ #include +#include typedef struct _FindCondition FindCondition; +typedef struct _FindInfo FindInfo; +typedef gboolean (*FindTest)(FindCondition *condition, FindInfo *info); +typedef void (*FindFree)(FindCondition *condition); struct _FindCondition { - guchar *name; + FindTest test; + FindFree free; + gpointer data1; + gpointer data2; +}; + +struct _FindInfo +{ + guchar *fullpath; + guchar *leaf; + struct stat stats; }; FindCondition *find_compile(guchar *string); -- 2.11.4.GIT