regedit: search values and repeat search from cursor positions
authorChris Davis <cd.rattan@gmail.com>
Fri, 1 Aug 2014 06:24:19 +0000 (23:24 -0700)
committerMichael Adam <obnox@samba.org>
Wed, 1 Oct 2014 12:32:09 +0000 (14:32 +0200)
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 <cd.rattan@gmail.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Michael Adam <obnox@samba.org>
source3/utils/regedit.c
source3/utils/regedit.h
source3/utils/regedit_treeview.c
source3/utils/regedit_treeview.h
source3/utils/regedit_valuelist.c
source3/utils/regedit_valuelist.h

index c1b94ac9d5a8d97d664dda1643537dc6dbee88da..a5fb913e5da65c7b3a59d46f6d10f339cb2f37a8 100644 (file)
@@ -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 = &regedit->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 = &regedit->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 = &regedit->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 = &regedit->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;
index c99aebab97fef348924d6ddd7c32af808d37a34b..0928f9ea9b44224fcd2cee8b14c1b64835910ef8 100644 (file)
@@ -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;
index 885e6d09981fa842b487dacaa50c23db84b3e426..cf071de3d880ef6f22ea8d34c38504574b4de1c6 100644 (file)
@@ -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);
index 919507f9e220c00c560e5f39f10b2413c8ee0d68..ba0cd0a8ae0d4f810fb780ef1da1f7da9fee3b8d 100644 (file)
@@ -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);
index aa387a740aef2f88e051b45f176e253e7c79cf77..f12a81ebd27cccc4bab79903c4d1df0f4a23c45b 100644 (file)
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#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,
index b84b4ff4e4186e95c24009619cd45f4cd1d6b5b8..11783899ff8c118cac083bf78a91c8067cdc5f9a 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef _REGEDIT_VALUELIST_H_
 #define _REGEDIT_VALUELIST_H_
 
-#include "includes.h"
 #include <ncurses.h>
 #include <panel.h>
 
@@ -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