From bcab659516382d5dc51b1558a895a9a26d81c6b8 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Thu, 31 Jul 2014 23:24:19 -0700 Subject: regedit: search values and repeat search from cursor positions Recovering the search position from the cursors is safer than retaining a pointer to the last node, as the pointer will become invalid if the user deletes the item or refreshes the cache. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam --- source3/utils/regedit.c | 186 ++++++++++++++++++++++++-------------- source3/utils/regedit.h | 1 - source3/utils/regedit_treeview.c | 47 ++++++++++ source3/utils/regedit_treeview.h | 1 + source3/utils/regedit_valuelist.c | 100 +++++++++++++++++--- source3/utils/regedit_valuelist.h | 13 ++- 6 files changed, 265 insertions(+), 83 deletions(-) (limited to 'source3/utils') diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index c1b94ac9d5..a5fb913e5d 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -208,57 +208,19 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, } } -static WERROR next_depth_first(struct tree_node **node) -{ - WERROR rv = WERR_OK; - - SMB_ASSERT(node != NULL && *node != NULL); - - if (tree_node_has_children(*node)) { - /* 1. If the node has children, go to the first one. */ - rv = tree_node_load_children(*node); - if (W_ERROR_IS_OK(rv)) { - SMB_ASSERT((*node)->child_head != NULL); - *node = (*node)->child_head; - } - } else if ((*node)->next) { - /* 2. If there's a node directly after this one, go there */ - *node = (*node)->next; - } else { - /* 3. Otherwise, go up the hierarchy to find the next one */ - do { - *node = (*node)->parent; - if (*node && (*node)->next) { - *node = (*node)->next; - break; - } - } while (*node); - } - - return rv; -} - -static WERROR regedit_search_next(struct regedit *regedit) -{ - WERROR rv; - struct regedit_search_opts *opts = ®edit->active_search; - - if (opts->search_recursive) { - rv = next_depth_first(&opts->node); - if (!W_ERROR_IS_OK(rv)) { - return rv; - } - } else { - opts->node = opts->node->next; - } - - return WERR_OK; -} - -static WERROR regedit_search(struct regedit *regedit) +enum search_flags { + SEARCH_NEXT = (1<<0), + SEARCH_PREV = (1<<1), + SEARCH_REPEAT = (1<<2) +}; +static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, + struct value_item *vitem, unsigned flags) { struct regedit_search_opts *opts; struct tree_node *found; + struct value_item *found_value; + bool search_key, need_sync; + char *save_value_name; WERROR rv; opts = ®edit->active_search; @@ -269,25 +231,73 @@ static WERROR regedit_search(struct regedit *regedit) SMB_ASSERT(opts->search_key || opts->search_value); - for (found = NULL; opts->node && !found; ) { - if (opts->search_key && - opts->match(opts->node->name, opts->query)) { - found = opts->node; + rv = WERR_OK; + found = NULL; + found_value = NULL; + save_value_name = NULL; + search_key = opts->search_key; + need_sync = false; + + if (opts->search_value) { + struct value_item *it; + + it = value_list_get_current_item(regedit->vl); + if (it) { + save_value_name = talloc_strdup(regedit, + it->value_name); + if (save_value_name == NULL) { + return WERR_NOMEM; + } } + + if (vitem) { + search_key = false; + } + } + + if (!vitem && (flags & SEARCH_REPEAT)) { if (opts->search_value) { - /* TODO - rv = regedit_search_value(regedit); - if (W_ERROR_IS_OK(rv)) { - found = opts->node; - } else if (!W_ERROR_EQUAL(rv, WERR_NO_MORE_ITEMS)) { - return rv; + search_key = false; + } else if (!tree_node_next(&node, opts->search_recursive, &rv)) { + beep(); + return rv; + } + } + + do { + if (search_key) { + SMB_ASSERT(opts->search_key == true); + if (opts->match(node->name, opts->query)) { + found = node; + } else if (opts->search_value) { + search_key = false; } - */ } - rv = regedit_search_next(regedit); - if (!W_ERROR_IS_OK(rv)) { - return rv; + if (!search_key) { + SMB_ASSERT(opts->search_value == true); + if (!vitem) { + rv = value_list_load_quick(regedit->vl, + node->key); + if (!W_ERROR_IS_OK(rv)) { + goto out; + } + need_sync = true; + } + found_value = value_list_find_next_item(regedit->vl, + vitem, + opts->query, + opts->match); + if (found_value) { + found = node; + } else { + vitem = NULL; + search_key = opts->search_key; + } } + } while (!found && tree_node_next(&node, opts->search_recursive, &rv)); + + if (!W_ERROR_IS_OK(rv)) { + goto out; } if (found) { @@ -298,14 +308,51 @@ static WERROR regedit_search(struct regedit *regedit) print_path(regedit, found); } tree_view_set_current_node(regedit->keys, found); - load_values(regedit); + if (found_value) { + if (need_sync) { + value_list_sync(regedit->vl); + } + value_list_set_current_item(regedit->vl, found_value); + regedit->tree_input = false; + } else { + load_values(regedit); + regedit->tree_input = true; + } tree_view_show(regedit->keys); value_list_show(regedit->vl); + print_heading(regedit); } else { + if (need_sync) { + load_values(regedit); + value_list_set_current_item_by_name(regedit->vl, + save_value_name); + } beep(); } - return WERR_OK; +out: + talloc_free(save_value_name); + + return rv; +} + +static void regedit_search_repeat(struct regedit *regedit, unsigned flags) +{ + struct tree_node *node; + struct value_item *vitem; + struct regedit_search_opts *opts; + + opts = ®edit->active_search; + if (opts->query == NULL) { + return; + } + + node = tree_view_get_current_node(regedit->keys); + vitem = NULL; + if (opts->search_value && !regedit->tree_input) { + vitem = value_list_get_current_item(regedit->vl); + } + regedit_search(regedit, node, vitem, flags | SEARCH_REPEAT); } static void handle_tree_input(struct regedit *regedit, int c) @@ -535,27 +582,28 @@ static void handle_main_input(struct regedit *regedit, int c) case '/': { int rv; struct regedit_search_opts *opts; + struct tree_node *node; opts = ®edit->active_search; rv = dialog_search_input(regedit, opts); if (rv == DIALOG_OK) { SMB_ASSERT(opts->query != NULL); opts->match = find_substring_nocase; - opts->node = regedit->keys->root; + node = regedit->keys->root; if (opts->search_case) { opts->match = find_substring; } if (!opts->search_recursive) { - opts->node = - tree_view_get_current_node(regedit->keys); + node = tree_view_get_current_node(regedit->keys); + node = tree_node_first(node); } - regedit_search(regedit); + regedit_search(regedit, node, NULL, SEARCH_NEXT); } break; } case 'x': case 'X': - regedit_search(regedit); + regedit_search_repeat(regedit, SEARCH_NEXT); break; case '\t': regedit->tree_input = !regedit->tree_input; diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h index c99aebab97..0928f9ea9b 100644 --- a/source3/utils/regedit.h +++ b/source3/utils/regedit.h @@ -64,7 +64,6 @@ typedef bool (*regedit_search_match_fn_t)(const char *, const char *); struct regedit_search_opts { const char *query; regedit_search_match_fn_t match; - struct tree_node *node; bool search_key; bool search_value; bool search_recursive; diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 885e6d0998..cf071de3d8 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -312,6 +312,53 @@ finish: return rv; } +static WERROR next_depth_first(struct tree_node **node) +{ + WERROR rv = WERR_OK; + + SMB_ASSERT(node != NULL && *node != NULL); + + if (tree_node_has_children(*node)) { + /* 1. If the node has children, go to the first one. */ + rv = tree_node_load_children(*node); + if (W_ERROR_IS_OK(rv)) { + SMB_ASSERT((*node)->child_head != NULL); + *node = (*node)->child_head; + } + } else if ((*node)->next) { + /* 2. If there's a node directly after this one, go there */ + *node = (*node)->next; + } else { + /* 3. Otherwise, go up the hierarchy to find the next one */ + do { + *node = (*node)->parent; + if (*node && (*node)->next) { + *node = (*node)->next; + break; + } + } while (*node); + } + + return rv; +} + +bool tree_node_next(struct tree_node **node, bool depth, WERROR *err) +{ + *err = WERR_OK; + + if (*node == NULL) { + return false; + } + + if (depth) { + *err = next_depth_first(node); + } else { + *node = (*node)->next; + } + + return *node != NULL && W_ERROR_IS_OK(*err); +} + void tree_view_clear(struct tree_view *view) { multilist_set_data(view->list, NULL); diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 919507f9e2..ba0cd0a8ae 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -60,6 +60,7 @@ void tree_node_append(struct tree_node *left, struct tree_node *right); struct tree_node *tree_node_pop(struct tree_node **plist); struct tree_node *tree_node_first(struct tree_node *list); struct tree_node *tree_node_last(struct tree_node *list); +bool tree_node_next(struct tree_node **node, bool depth, WERROR *err); void tree_node_append_last(struct tree_node *list, struct tree_node *node); size_t tree_node_print_path(WINDOW *label, struct tree_node *node); const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node); diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index aa387a740a..f12a81ebd2 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -17,6 +17,8 @@ * along with this program. If not, see . */ +#include "includes.h" +#include "regedit.h" #include "regedit_valuelist.h" #include "regedit_list.h" #include "lib/registry/registry.h" @@ -335,7 +337,8 @@ static int vitem_cmp(struct value_item *a, struct value_item *b) return strcmp(a->value_name, b->value_name); } -WERROR value_list_load(struct value_list *vl, struct registry_key *key) +/* load only the value names into memory to enable searching */ +WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key) { uint32_t nvalues; uint32_t idx; @@ -366,18 +369,28 @@ WERROR value_list_load(struct value_list *vl, struct registry_key *key) talloc_free(new_items); return rv; } + } + + TYPESAFE_QSORT(new_items, nvalues, vitem_cmp); + vl->nvalues = nvalues; + vl->values = new_items; - rv = append_data_summary(new_items, vitem); + return rv; +} + +/* sync up the UI with the list */ +WERROR value_list_sync(struct value_list *vl) +{ + uint32_t idx; + WERROR rv; + + for (idx = 0; idx < vl->nvalues; ++idx) { + rv = append_data_summary(vl->values, &vl->values[idx]); if (!W_ERROR_IS_OK(rv)) { - talloc_free(new_items); return rv; } } - TYPESAFE_QSORT(new_items, nvalues, vitem_cmp); - - vl->nvalues = nvalues; - vl->values = new_items; rv = multilist_set_data(vl->list, vl); if (W_ERROR_IS_OK(rv)) { multilist_refresh(vl->list); @@ -386,6 +399,72 @@ WERROR value_list_load(struct value_list *vl, struct registry_key *key) return rv; } +WERROR value_list_load(struct value_list *vl, struct registry_key *key) +{ + WERROR rv; + + rv = value_list_load_quick(vl, key); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + rv = value_list_sync(vl); + + return rv; +} + +struct value_item *value_list_find_next_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match) +{ + struct value_item *end; + + if (!vl->values) { + return NULL; + } + + if (vitem) { + ++vitem; + } else { + vitem = &vl->values[0]; + } + + for (end = &vl->values[vl->nvalues]; vitem < end; ++vitem) { + if (match(vitem->value_name, s)) { + return vitem; + } + } + + return NULL; +} + +struct value_item *value_list_find_prev_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match) +{ + struct value_item *end; + + if (!vl->values) { + return NULL; + } + + if (vitem) { + --vitem; + } else { + vitem = &vl->values[vl->nvalues - 1]; + } + + for (end = &vl->values[-1]; vitem > end; --vitem) { + if (match(vitem->value_name, s)) { + return vitem; + } + } + + return NULL; +} + struct value_item *value_list_get_current_item(struct value_list *vl) { return discard_const_p(struct value_item, @@ -396,16 +475,13 @@ void value_list_set_current_item_by_name(struct value_list *vl, const char *name) { size_t i; - struct value_item *item = NULL; for (i = 0; i < vl->nvalues; ++i) { if (strequal(vl->values[i].value_name, name)) { - item = &vl->values[i]; - break; + multilist_set_current_row(vl->list, &vl->values[i]); + return; } } - - multilist_set_current_row(vl->list, item); } void value_list_set_current_item(struct value_list *vl, diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h index b84b4ff4e4..11783899ff 100644 --- a/source3/utils/regedit_valuelist.h +++ b/source3/utils/regedit_valuelist.h @@ -20,7 +20,6 @@ #ifndef _REGEDIT_VALUELIST_H_ #define _REGEDIT_VALUELIST_H_ -#include "includes.h" #include #include @@ -48,6 +47,7 @@ struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, int begin_y, int begin_x); void value_list_show(struct value_list *vl); void value_list_set_selected(struct value_list *vl, bool select); +const char **value_list_load_names(TALLOC_CTX *ctx, struct registry_key *key); WERROR value_list_load(struct value_list *vl, struct registry_key *key); void value_list_resize(struct value_list *vl, int nlines, int ncols, int begin_y, int begin_x); @@ -58,4 +58,15 @@ void value_list_set_current_item_by_name(struct value_list *vl, const char *name); void value_list_driver(struct value_list *vl, int c); +WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key); +WERROR value_list_sync(struct value_list *vl); +struct value_item *value_list_find_next_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match); +struct value_item *value_list_find_prev_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match); + #endif -- cgit