regedit: search values and repeat search from cursor positions
[samba.git] / source3 / utils / regedit_valuelist.c
index e5c3822b55b24d7f64fc732c3a2663a313e10392..f12a81ebd27cccc4bab79903c4d1df0f4a23c45b 100644 (file)
  * 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"
 
-static void value_list_free_items(ITEM **items)
-{
-       size_t i;
-       ITEM *item;
-       struct value_item *vitem;
+#define HEADING_X 3
 
-       if (items == NULL) {
-               return;
+static int value_list_free(struct value_list *vl)
+{
+       if (vl->panel) {
+               del_panel(vl->panel);
+       }
+       if (vl->sub) {
+               delwin(vl->sub);
+       }
+       if (vl->window) {
+               delwin(vl->window);
        }
 
-       for (i = 0; items[i] != NULL; ++i) {
-               item = items[i];
-               vitem = item_userptr(item);
-               SMB_ASSERT(vitem != NULL);
-               free_item(item);
+       return 0;
+}
+
+static const char *vl_get_column_header(const void *data, unsigned col)
+{
+       switch (col) {
+       case 0:
+               return "Name";
+       case 1:
+               return "Type";
+       case 2:
+               return "Data";
        }
 
-       talloc_free(items);
+       return "???";
 }
 
-static int value_list_free(struct value_list *vl)
+static const void *vl_get_first_row(const void *data)
 {
-       if (vl->menu) {
-               unpost_menu(vl->menu);
-               free_menu(vl->menu);
+       const struct value_list *vl;
+
+       if (data) {
+               vl = talloc_get_type_abort(data, struct value_list);
+               if (vl->nvalues) {
+                       return &vl->values[0];
+               }
        }
-       if (vl->empty && vl->empty[0]) {
-               free_item(vl->empty[0]);
+
+       return NULL;
+}
+
+static const void *vl_get_next_row(const void *data, const void *row)
+{
+       const struct value_list *vl;
+       const struct value_item *value = row;
+
+       SMB_ASSERT(data != NULL);
+       SMB_ASSERT(value != NULL);
+       vl = talloc_get_type_abort(data, struct value_list);
+       if (value == &vl->values[vl->nvalues - 1]) {
+               return NULL;
        }
-       if (vl->panel) {
-               del_panel(vl->panel);
+
+       return value + 1;
+}
+
+static const void *vl_get_prev_row(const void *data, const void *row)
+{
+       const struct value_list *vl;
+       const struct value_item *value = row;
+
+       SMB_ASSERT(data != NULL);
+       SMB_ASSERT(value != NULL);
+       vl = talloc_get_type_abort(data, struct value_list);
+       if (value == &vl->values[0]) {
+               return NULL;
        }
-       if (vl->window) {
-               delwin(vl->window);
+
+       return value - 1;
+}
+
+static const char *vl_get_item_label(const void *row, unsigned col)
+{
+       const struct value_item *value = row;
+
+       SMB_ASSERT(value != NULL);
+       SMB_ASSERT(value->value_name != NULL);
+       switch (col) {
+       case 0:
+               return value->value_name;
+       case 1:
+               return str_regtype(value->type);
+       case 2:
+               if (value->value) {
+                       return value->value;
+               }
+               return "";
        }
-       value_list_free_items(vl->items);
 
-       return 0;
+       return "???";
 }
 
+static struct multilist_accessors vl_accessors = {
+       .get_column_header = vl_get_column_header,
+       .get_first_row = vl_get_first_row,
+       .get_next_row = vl_get_next_row,
+       .get_prev_row = vl_get_prev_row,
+       .get_item_label = vl_get_item_label
+};
+
 struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
                                  int begin_y, int begin_x)
 {
-       static const char *empty = "(no values)";
-       static const char *empty_desc = "";
        struct value_list *vl;
 
        vl = talloc_zero(ctx, struct value_list);
@@ -74,35 +139,28 @@ struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
 
        talloc_set_destructor(vl, value_list_free);
 
-       vl->empty = talloc_zero_array(vl, ITEM *, 2);
-       if (vl->empty == NULL) {
+       vl->window = newwin(nlines, ncols, begin_y, begin_x);
+       if (vl->window == NULL) {
                goto fail;
        }
-       vl->empty[0] = new_item(empty, empty_desc);
-       if (vl->empty[0] == NULL) {
+       vl->sub = subwin(vl->window, nlines - 2, ncols - 2,
+                        begin_y + 1, begin_x + 1);
+       if (vl->sub == NULL) {
                goto fail;
        }
+       box(vl->window, 0, 0);
+       mvwprintw(vl->window, 0, HEADING_X, "Value");
 
-       vl->window = newwin(nlines, ncols, begin_y, begin_x);
-       if (vl->window == NULL) {
-               goto fail;
-       }
        vl->panel = new_panel(vl->window);
        if (vl->panel == NULL) {
                goto fail;
        }
 
-       vl->menu = new_menu(vl->empty);
-       if (vl->menu == NULL) {
+       vl->list = multilist_new(vl, vl->sub, &vl_accessors, 3);
+       if (vl->list == NULL) {
                goto fail;
        }
 
-       set_menu_format(vl->menu, nlines, 1);
-       set_menu_win(vl->menu, vl->window);
-
-       menu_opts_on(vl->menu, O_SHOWDESC);
-       set_menu_mark(vl->menu, "* ");
-
        return vl;
 
 fail:
@@ -111,19 +169,39 @@ fail:
        return NULL;
 }
 
+void value_list_set_selected(struct value_list *vl, bool select)
+{
+       attr_t attr = A_NORMAL;
+
+       if (select) {
+               attr = A_REVERSE;
+       }
+       mvwchgat(vl->window, 0, HEADING_X, 5, attr, 0, NULL);
+}
+
 void value_list_resize(struct value_list *vl, int nlines, int ncols,
                       int begin_y, int begin_x)
 {
-       WINDOW *nwin;
+       WINDOW *nwin, *nsub;
 
-       unpost_menu(vl->menu);
        nwin = newwin(nlines, ncols, begin_y, begin_x);
+       if (nwin == NULL) {
+               return;
+       }
+       nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1);
+       if (nsub == NULL) {
+               delwin(nwin);
+               return;
+       }
        replace_panel(vl->panel, nwin);
+       delwin(vl->sub);
        delwin(vl->window);
        vl->window = nwin;
-       set_menu_format(vl->menu, nlines, 1);
-       set_menu_win(vl->menu, vl->window);
-       post_menu(vl->menu);
+       vl->sub = nsub;
+       box(vl->window, 0, 0);
+       mvwprintw(vl->window, 0, HEADING_X, "Value");
+       multilist_set_window(vl->list, vl->sub);
+       value_list_show(vl);
 }
 
 static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key)
@@ -151,12 +229,28 @@ static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key)
 
 void value_list_show(struct value_list *vl)
 {
-       post_menu(vl->menu);
+       multilist_refresh(vl->list);
+       touchwin(vl->window);
+       wnoutrefresh(vl->window);
+       wnoutrefresh(vl->sub);
 }
 
-static WERROR append_data_summary(struct value_item *vitem)
+static bool string_is_printable(const char *s)
 {
-       char *tmp;
+       const char *p;
+
+       for (p = s; *p; ++p) {
+               if (!isprint(*p)) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem)
+{
+       char *tmp = NULL;
 
 /* This is adapted from print_registry_value() in net_registry_util.c */
 
@@ -166,29 +260,54 @@ static WERROR append_data_summary(struct value_item *vitem)
                if (vitem->data.length >= 4) {
                        v = IVAL(vitem->data.data, 0);
                }
-               tmp = talloc_asprintf_append(vitem->value_desc, "(0x%x)", v);
+               tmp = talloc_asprintf(ctx, "0x%08x (%u)", v, v);
                break;
        }
        case REG_SZ:
        case REG_EXPAND_SZ: {
                const char *s;
 
-               if (!pull_reg_sz(vitem, &vitem->data, &s)) {
+               if (!pull_reg_sz(ctx, &vitem->data, &s)) {
                        break;
                }
-               tmp = talloc_asprintf_append(vitem->value_desc, "(\"%s\")", s);
+               vitem->unprintable = !string_is_printable(s);
+               if (vitem->unprintable) {
+                       tmp = talloc_asprintf(ctx, "(unprintable)");
+               } else {
+                       tmp = talloc_asprintf(ctx, "%s", s);
+               }
                break;
        }
        case REG_MULTI_SZ: {
-               size_t i;
+               size_t i, len;
                const char **a;
+               const char *val;
 
-               if (!pull_reg_multi_sz(vitem, &vitem->data, &a)) {
+               if (!pull_reg_multi_sz(ctx, &vitem->data, &a)) {
                        break;
                }
-               tmp = vitem->value_desc;
-               for (i = 0; a[i] != NULL; ++i) {
-                       tmp = talloc_asprintf_append(tmp, "\"%s\" ", a[i]);
+               for (len = 0; a[len] != NULL; ++len) {
+               }
+               tmp = talloc_asprintf(ctx, "(%u) ", (unsigned)len);
+               if (tmp == NULL) {
+                       return WERR_NOMEM;
+               }
+               for (i = 0; i < len; ++i) {
+                       if (!string_is_printable(a[i])) {
+                               val = "(unprintable)";
+                               vitem->unprintable = true;
+                       } else {
+                               val = a[i];
+                       }
+                       if (i == len - 1) {
+                               tmp = talloc_asprintf_append(tmp,
+                                                            "[%u]=\"%s\"",
+                                                            (unsigned)i, val);
+                       } else {
+                               tmp = talloc_asprintf_append(tmp,
+                                                            "[%u]=\"%s\", ",
+                                                            (unsigned)i, val);
+                       }
                        if (tmp == NULL) {
                                return WERR_NOMEM;
                        }
@@ -196,12 +315,11 @@ static WERROR append_data_summary(struct value_item *vitem)
                break;
        }
        case REG_BINARY:
-               tmp = talloc_asprintf_append(vitem->value_desc, "(%d bytes)",
-                                            (int)vitem->data.length);
+               tmp = talloc_asprintf(ctx, "(%d bytes)",
+                                     (int)vitem->data.length);
                break;
        default:
-               tmp = talloc_asprintf_append(vitem->value_desc,
-                                            "(<unprintable>)");
+               tmp = talloc_asprintf(ctx, "(unknown)");
                break;
        }
 
@@ -209,80 +327,170 @@ static WERROR append_data_summary(struct value_item *vitem)
                return WERR_NOMEM;
        }
 
-       vitem->value_desc = tmp;
+       vitem->value = tmp;
 
        return WERR_OK;
 }
 
-WERROR value_list_load(struct value_list *vl, struct registry_key *key)
+static int vitem_cmp(struct value_item *a, struct value_item *b)
 {
-       uint32_t n_values;
+       return strcmp(a->value_name, b->value_name);
+}
+
+/* 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;
-       struct value_item *vitem;
-       ITEM **new_items;
+       struct value_item *vitem, *new_items;
        WERROR rv;
-       static const char *empty_name = "(empty)";
-       const char *name;
 
-       unpost_menu(vl->menu);
+       multilist_set_data(vl->list, NULL);
+       vl->nvalues = 0;
+       TALLOC_FREE(vl->values);
 
-       n_values = get_num_values(vl, key);
-       if (n_values == 0) {
-               set_menu_items(vl->menu, vl->empty);
+       nvalues = get_num_values(vl, key);
+       if (nvalues == 0) {
                return WERR_OK;
        }
 
-       new_items = talloc_zero_array(vl, ITEM *, n_values + 1);
+       new_items = talloc_zero_array(vl, struct value_item, nvalues);
        if (new_items == NULL) {
                return WERR_NOMEM;
        }
 
-       for (idx = 0; idx < n_values; ++idx) {
-               vitem = talloc_zero(new_items, struct value_item);
-               if (vitem == NULL) {
-                       return WERR_NOMEM;
-               }
-
-               rv = reg_key_get_value_by_index(vitem, key, idx,
+       for (idx = 0; idx < nvalues; ++idx) {
+               vitem = &new_items[idx];
+               rv = reg_key_get_value_by_index(new_items, key, idx,
                                                &vitem->value_name,
                                                &vitem->type,
                                                &vitem->data);
-
                if (!W_ERROR_IS_OK(rv)) {
-                       talloc_free(vitem);
+                       talloc_free(new_items);
                        return rv;
                }
+       }
 
-               vitem->value_desc = talloc_asprintf(vitem, "%-14s",
-                       str_regtype(vitem->type));
-               if (vitem->value_desc == NULL) {
-                       talloc_free(vitem);
-                       return rv;
-               }
+       TYPESAFE_QSORT(new_items, nvalues, vitem_cmp);
+       vl->nvalues = nvalues;
+       vl->values = new_items;
 
-               rv = append_data_summary(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(vitem);
                        return rv;
                }
+       }
+
+       rv = multilist_set_data(vl->list, vl);
+       if (W_ERROR_IS_OK(rv)) {
+               multilist_refresh(vl->list);
+       }
+
+       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;
 
-               /* ncurses won't accept empty strings in menu items */
-               name = vitem->value_name;
-               if (name[0] == '\0') {
-                       name = empty_name;
+       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;
                }
-               new_items[idx] = new_item(name, vitem->value_desc);
-               if (new_items[idx] == NULL) {
-                       talloc_free(vitem);
-                       return WERR_NOMEM;
+       }
+
+       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;
+}
 
-               set_item_userptr(new_items[idx], vitem);
+struct value_item *value_list_get_current_item(struct value_list *vl)
+{
+       return discard_const_p(struct value_item,
+                              multilist_get_current_row(vl->list));
+}
+
+void value_list_set_current_item_by_name(struct value_list *vl,
+                                        const char *name)
+{
+       size_t i;
+
+       for (i = 0; i < vl->nvalues; ++i) {
+               if (strequal(vl->values[i].value_name, name)) {
+                       multilist_set_current_row(vl->list, &vl->values[i]);
+                       return;
+               }
        }
+}
 
-       set_menu_items(vl->menu, new_items);
-       value_list_free_items(vl->items);
-       vl->items = new_items;
+void value_list_set_current_item(struct value_list *vl,
+                                const struct value_item *item)
+{
+       multilist_set_current_row(vl->list, item);
+}
 
-       return WERR_OK;
+void value_list_driver(struct value_list *vl, int c)
+{
+       multilist_driver(vl->list, c);
 }