From 7ce73452215e68ec78d36b99abc47232f58a5710 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 4 Aug 2014 21:48:04 -0700 Subject: [PATCH 01/45] regedit: silence some warnings Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 7e7c649376bd17c7ac9908bbd0ffc235e7cd18f7) --- source3/utils/regedit.c | 4 ++-- source3/utils/regedit.h | 3 ++- source3/utils/regedit_samba3.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 21fd257..a1118d5 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -197,7 +197,7 @@ static void load_values(struct regedit *regedit) static void add_reg_key(struct regedit *regedit, struct tree_node *node, bool subkey) { - char *name; + const char *name; const char *msg; if (!subkey && !node->parent) { @@ -244,7 +244,7 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, dialog_notice(regedit, DIA_ALERT, "New Key", "Failed to create key."); } - talloc_free(name); + talloc_free(discard_const(name)); } } diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h index fb9ad61..2ab40f9 100644 --- a/source3/utils/regedit.h +++ b/source3/utils/regedit.h @@ -33,7 +33,8 @@ WERROR reg_openhive_wrap(TALLOC_CTX *ctx, const char *hive, WERROR reg_openkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent, const char *name, struct samba3_registry_key *key); WERROR reg_enumvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, - uint32 idx, char **name, uint32_t *type, DATA_BLOB *data); + uint32 idx, char **name, uint32_t *type, + DATA_BLOB *data); WERROR reg_queryvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, const char *name, uint32_t *type, DATA_BLOB *data); WERROR reg_enumkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, diff --git a/source3/utils/regedit_samba3.c b/source3/utils/regedit_samba3.c index 39b06bc..4e22be8 100644 --- a/source3/utils/regedit_samba3.c +++ b/source3/utils/regedit_samba3.c @@ -107,7 +107,7 @@ static WERROR samba3_get_value_by_index(TALLOC_CTX *mem_ctx, mykeydata = talloc_get_type(parent, struct samba3_key); return reg_enumvalue_wrap(mem_ctx, &mykeydata->s3key, n, - value_name, type, data); + discard_const(value_name), type, data); } static WERROR samba3_get_value_by_name(TALLOC_CTX *mem_ctx, @@ -138,7 +138,7 @@ static WERROR samba3_get_subkey_by_index(TALLOC_CTX *mem_ctx, *keyclass = NULL; return reg_enumkey_wrap(mem_ctx, &mykeydata->s3key, n, - name, last_changed_time); + discard_const(name), last_changed_time); } static WERROR samba3_add_key(TALLOC_CTX *mem_ctx, -- 2.1.1 From 96500ec9db8b9f3501e96f7006734e7092362f69 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 19 May 2014 11:08:09 -0700 Subject: [PATCH 02/45] regedit: add white on blue color scheme Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 994f1ed301e2023f0e4c8afa7429388ea90362e1) --- source3/utils/regedit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index a1118d5..5e6db7e 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -450,6 +450,7 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) if (colors) { start_color(); use_default_colors(); + assume_default_colors(COLOR_WHITE, COLOR_BLUE); } regedit = talloc_zero(mem_ctx, struct regedit); -- 2.1.1 From a6d58234c3dfb5e7d7bf559b770f0b8f9a900790 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 19 May 2014 17:34:01 -0700 Subject: [PATCH 03/45] regedit: add padding for key labels when there's not a prefix. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit b48f081dc681a0a769165da43668b356c71fdb35) --- source3/utils/regedit_treeview.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index c74f052..1f2354a 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -253,20 +253,21 @@ WERROR tree_view_update(struct tree_view *view, struct tree_node *list) } for (i = 0, node = list; node != NULL; ++i, node = node->next) { - const char *label = node->name; + char prefix = ' '; /* Add a '+' marker to indicate that the item has descendants. */ if (tree_node_has_children(node)) { - SMB_ASSERT(node->label == NULL); - node->label = talloc_asprintf(node, "+%s", node->name); - if (node->label == NULL) { - goto fail; - } - label = node->label; + prefix = '+'; + } + + SMB_ASSERT(node->label == NULL); + node->label = talloc_asprintf(node, "%c%s", prefix, node->name); + if (node->label == NULL) { + goto fail; } - items[i] = new_item(label, node->name); + items[i] = new_item(node->label, node->name); set_item_userptr(items[i], node); } -- 2.1.1 From cccb802cb3d8a5da5ddae5ae026326d07b6fe0d4 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Tue, 20 May 2014 16:17:42 -0700 Subject: [PATCH 04/45] regedit: add borders around key and value lists, and change headings Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit c79837c215de59fa07b665bb79149058f36828d7) --- source3/utils/regedit.c | 25 +++++++------------------ source3/utils/regedit_treeview.c | 32 +++++++++++++++++++++++++++++++- source3/utils/regedit_treeview.h | 2 ++ source3/utils/regedit_valuelist.c | 29 ++++++++++++++++++++++++++++- source3/utils/regedit_valuelist.h | 2 ++ 5 files changed, 70 insertions(+), 20 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 5e6db7e..86983c2 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -30,14 +30,14 @@ #include #define KEY_START_X 0 -#define KEY_START_Y 3 +#define KEY_START_Y 1 #define KEY_WIDTH (COLS / 4) #define KEY_HEIGHT (LINES - KEY_START_Y - 2) #define VAL_START_X KEY_WIDTH -#define VAL_START_Y 3 +#define VAL_START_Y 1 #define VAL_WIDTH (COLS - KEY_WIDTH) #define VAL_HEIGHT (LINES - VAL_START_Y - 2) -#define HEADING_START_Y (KEY_START_Y - 1) + #define HELP1_START_Y (LINES - 2) #define HELP1_START_X 0 #define HELP1_WIDTH (LINES) @@ -164,24 +164,13 @@ static void print_help(struct regedit *regedit) static void print_heading(struct regedit *regedit) { - move(HEADING_START_Y, 0); - clrtoeol(); - if (regedit->tree_input) { - attron(A_REVERSE); - } else { - attroff(A_REVERSE); - } - mvprintw(HEADING_START_Y, KEY_START_X, "Key"); - attroff(A_REVERSE); - - if (!regedit->tree_input) { - attron(A_REVERSE); + tree_view_set_selected(regedit->keys, true); + value_list_set_selected(regedit->vl, false); } else { - attroff(A_REVERSE); + tree_view_set_selected(regedit->keys, false); + value_list_set_selected(regedit->vl, true); } - mvprintw(HEADING_START_Y, VAL_START_X, "Value"); - attroff(A_REVERSE); print_help(regedit); } diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 1f2354a..f2241e6 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -20,6 +20,8 @@ #include "regedit_treeview.h" #include "lib/registry/registry.h" +#define HEADING_X 3 + struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, const char *name, struct registry_key *key) { @@ -284,6 +286,16 @@ fail: return WERR_NOMEM; } +void tree_view_set_selected(struct tree_view *view, bool select) +{ + attr_t attr = A_NORMAL; + + if (select) { + attr = A_REVERSE; + } + mvwchgat(view->window, 0, HEADING_X, 3, attr, 0, NULL); +} + void tree_view_show(struct tree_view *view) { post_menu(view->menu); @@ -301,6 +313,9 @@ static int tree_view_free(struct tree_view *view) if (view->panel) { del_panel(view->panel); } + if (view->sub) { + delwin(view->sub); + } if (view->window) { delwin(view->window); } @@ -332,6 +347,14 @@ struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, if (view->window == NULL) { goto fail; } + view->sub = subwin(view->window, nlines - 2, ncols - 2, + begin_y + 1, begin_x + 1); + if (view->sub == NULL) { + goto fail; + } + box(view->window, 0, 0); + mvwprintw(view->window, 0, HEADING_X, "Key"); + view->panel = new_panel(view->window); if (view->panel == NULL) { goto fail; @@ -344,6 +367,7 @@ struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, } set_menu_format(view->menu, nlines, 1); set_menu_win(view->menu, view->window); + set_menu_sub(view->menu, view->sub); menu_opts_off(view->menu, O_SHOWDESC); set_menu_mark(view->menu, "* "); @@ -360,15 +384,21 @@ fail: void tree_view_resize(struct tree_view *view, int nlines, int ncols, int begin_y, int begin_x) { - WINDOW *nwin; + WINDOW *nwin, *nsub; unpost_menu(view->menu); nwin = newwin(nlines, ncols, begin_y, begin_x); + nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); replace_panel(view->panel, nwin); + delwin(view->sub); delwin(view->window); view->window = nwin; + view->sub = nsub; + box(view->window, 0, 0); + mvwprintw(view->window, 0, HEADING_X, "Key"); set_menu_format(view->menu, nlines, 1); set_menu_win(view->menu, view->window); + set_menu_sub(view->menu, view->sub); post_menu(view->menu); } diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 3931441..66e692e 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -43,6 +43,7 @@ struct tree_view { struct tree_node *root; WINDOW *window; + WINDOW *sub; PANEL *panel; MENU *menu; ITEM **current_items; @@ -61,6 +62,7 @@ size_t tree_node_print_path(WINDOW *label, struct tree_node *node); struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, int nlines, int ncols, int begin_y, int begin_x); +void tree_view_set_selected(struct tree_view *view, bool select); void tree_view_resize(struct tree_view *view, int nlines, int ncols, int begin_y, int begin_x); void tree_view_show(struct tree_view *view); diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index b135159..4d56fa6 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -20,6 +20,8 @@ #include "regedit_valuelist.h" #include "lib/registry/registry.h" +#define HEADING_X 3 + static void value_list_free_items(ITEM **items) { size_t i; @@ -87,6 +89,14 @@ struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, if (vl->window == NULL) { goto fail; } + 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->panel = new_panel(vl->window); if (vl->panel == NULL) { goto fail; @@ -99,6 +109,7 @@ struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, set_menu_format(vl->menu, nlines, 1); set_menu_win(vl->menu, vl->window); + set_menu_sub(vl->menu, vl->sub); menu_opts_on(vl->menu, O_SHOWDESC); set_menu_mark(vl->menu, "* "); @@ -111,18 +122,34 @@ 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); + nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); replace_panel(vl->panel, nwin); + delwin(vl->sub); delwin(vl->window); vl->window = nwin; + vl->sub = nsub; + box(vl->window, 0, 0); + mvwprintw(vl->window, 0, HEADING_X, "Value"); set_menu_format(vl->menu, nlines, 1); set_menu_win(vl->menu, vl->window); + set_menu_sub(vl->menu, vl->sub); post_menu(vl->menu); } diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h index d01db51..16b0d50 100644 --- a/source3/utils/regedit_valuelist.h +++ b/source3/utils/regedit_valuelist.h @@ -37,6 +37,7 @@ struct value_item { struct value_list { WINDOW *window; + WINDOW *sub; PANEL *panel; MENU *menu; ITEM **items; @@ -46,6 +47,7 @@ struct value_list { 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); 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); -- 2.1.1 From abe1f7f1f373d3b661022ef994253e3cbcb9f30e Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 21 May 2014 15:03:50 -0700 Subject: [PATCH 05/45] regedit: free value list subwindow Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit b5d0eb8ee06908ad8171f71b648c43de6303047b) --- source3/utils/regedit_valuelist.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index 4d56fa6..791a8ca 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -54,6 +54,9 @@ 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); } -- 2.1.1 From 5a0a51d2123eedd7d32fa8ee986b0fce4deabb24 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 21 May 2014 17:08:06 -0700 Subject: [PATCH 06/45] regedit: sort keys Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit bd8abef327e68cf1c1fc9c2cf6e0b11738971521) --- source3/utils/regedit_treeview.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index f2241e6..cfcf5f2 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -236,6 +236,16 @@ void tree_view_clear(struct tree_view *view) view->current_items = NULL; } +static int item_comp(ITEM **a, ITEM **b) +{ + struct tree_node *nodea, *nodeb; + + nodea = item_userptr(*a); + nodeb = item_userptr(*b); + + return strcmp(nodea->name, nodeb->name); +} + WERROR tree_view_update(struct tree_view *view, struct tree_node *list) { ITEM **items; @@ -273,6 +283,8 @@ WERROR tree_view_update(struct tree_view *view, struct tree_node *list) set_item_userptr(items[i], node); } + TYPESAFE_QSORT(items, n_items, item_comp); + unpost_menu(view->menu); set_menu_items(view->menu, items); tree_view_free_current_items(view->current_items); -- 2.1.1 From 4050476fa01c69b77618d78ad39076ffbf673413 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Thu, 22 May 2014 15:23:52 -0700 Subject: [PATCH 07/45] regedit: add a color scheme for path and context help sections Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit b8b83509ca080d48530fbde9b012b9c3eb1c42fe) --- source3/utils/regedit.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 86983c2..a69fa6b 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -50,6 +50,9 @@ #define PATH_WIDTH (COLS - 6) #define PATH_WIDTH_MAX 1024 +#define PAIR_YELLOW_CYAN 1 +#define PAIR_BLACK_CYAN 2 + struct regedit { WINDOW *main_window; WINDOW *path_label; @@ -73,6 +76,8 @@ static void show_path(struct regedit *regedit) } copywin(regedit->path_label, regedit->main_window, 0, start_pad, PATH_START_Y, start_win, PATH_START_Y, PATH_MAX_Y, false); + + mvchgat(0, 0, COLS, A_BOLD, PAIR_YELLOW_CYAN, NULL); } static void print_path(struct regedit *regedit, struct tree_node *node) @@ -147,15 +152,16 @@ static void print_help(struct regedit *regedit) move(HELP1_START_Y, HELP1_START_X); clrtoeol(); - attron(A_REVERSE); + attron(COLOR_PAIR(PAIR_BLACK_CYAN)); mvaddstr(HELP1_START_Y, HELP1_START_X, help); pad = COLS - strlen(msg) - strlen(help); for (i = 0; i < pad; ++i) { addch(' '); } - attron(A_BOLD); + attroff(COLOR_PAIR(PAIR_BLACK_CYAN)); + attron(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD); addstr(msg); - attroff(A_REVERSE | A_BOLD); + attroff(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD); move(HELP2_START_Y, HELP2_START_X); clrtoeol(); @@ -440,6 +446,8 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) start_color(); use_default_colors(); assume_default_colors(COLOR_WHITE, COLOR_BLUE); + init_pair(PAIR_YELLOW_CYAN, COLOR_YELLOW, COLOR_CYAN); + init_pair(PAIR_BLACK_CYAN, COLOR_BLACK, COLOR_CYAN); } regedit = talloc_zero(mem_ctx, struct regedit); -- 2.1.1 From 94708b2d05cefb919e0153d36b9d6b12ec9dbdb0 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 2 Jun 2014 21:50:01 -0700 Subject: [PATCH 08/45] regedit: add search feature. Open up a search input with '/'. 'x' key gets the next result. This patch also ensures that keys are always sorted, so that the search order matches the order the keys appear on screen. TODO: + flesh out search interface + find previous + search values Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 0ab07cb8069ae59e45fb0f6016096d30de9b4142) --- source3/utils/regedit.c | 150 +++++++++++++++++++++++++++++++++++++-- source3/utils/regedit.h | 13 ++++ source3/utils/regedit_dialog.c | 14 ++++ source3/utils/regedit_dialog.h | 4 ++ source3/utils/regedit_treeview.c | 134 +++++++++++++++++++++++++--------- source3/utils/regedit_treeview.h | 4 ++ 6 files changed, 280 insertions(+), 39 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index a69fa6b..77c254b 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -60,6 +60,7 @@ struct regedit { struct value_list *vl; struct tree_view *keys; bool tree_input; + struct regedit_search_opts active_search; }; static struct regedit *regedit_main = NULL; @@ -141,8 +142,9 @@ static void print_help(struct regedit *regedit) "[b] Edit binary"; const char *msg = "KEYS"; const char *help = khelp; - const char *genhelp = "[TAB] Switch sections [q] Quit regedit " - "[UP] List up [DOWN] List down"; + const char *genhelp = "[TAB] Switch sections [q] Quit " + "[UP] List up [DOWN] List down " + "[/] Search [x] Next"; int i, pad; if (!regedit->tree_input) { @@ -185,7 +187,7 @@ static void load_values(struct regedit *regedit) { struct tree_node *node; - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); value_list_load(regedit->vl, node->key); } @@ -229,7 +231,7 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, new_node = tree_node_new(parent, parent, name, new_key); SMB_ASSERT(new_node); - tree_node_append_last(list, new_node); + tree_node_insert_sorted(list, new_node); } list = tree_node_first(node); @@ -243,6 +245,106 @@ 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) +{ + struct regedit_search_opts *opts; + struct tree_node *found; + WERROR rv; + + opts = ®edit->active_search; + + if (!opts->query || !opts->match) { + return WERR_OK; + } + + 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; + } + 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; + } + */ + } + rv = regedit_search_next(regedit); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + } + + if (found) { + /* Put the cursor on the node that was found */ + if (!tree_view_is_node_visible(regedit->keys, found)) { + tree_view_update(regedit->keys, + tree_node_first(found)); + print_path(regedit, found); + } + tree_view_set_current_node(regedit->keys, found); + load_values(regedit); + tree_view_show(regedit->keys); + value_list_show(regedit->vl); + } else { + beep(); + } + + return WERR_OK; +} + static void handle_tree_input(struct regedit *regedit, int c) { struct tree_node *node; @@ -394,9 +496,49 @@ static void handle_value_input(struct regedit *regedit, int c) value_list_show(regedit->vl); } +static bool find_substring(const char *haystack, const char *needle) +{ + return strstr(haystack, needle) != NULL; +} + +static bool find_substring_nocase(const char *haystack, const char *needle) +{ + return strcasestr(haystack, needle) != NULL; +} + static void handle_main_input(struct regedit *regedit, int c) { switch (c) { + case 'f': + case 'F': + case '/': { + int rv; + struct regedit_search_opts *opts; + + opts = ®edit->active_search; + if (opts->query) { + talloc_free(discard_const(opts->query)); + } + rv = dialog_search_input(regedit, opts); + if (rv == DIALOG_OK) { + SMB_ASSERT(opts->query != NULL); + opts->match = find_substring; + opts->node = regedit->keys->root; + if (opts->search_nocase) { + opts->match = find_substring_nocase; + } + if (opts->search_relative) { + opts->node = + tree_view_get_current_node(regedit->keys); + } + regedit_search(regedit); + } + break; + } + case 'x': + case 'X': + regedit_search(regedit); + break; case '\t': regedit->tree_input = !regedit->tree_input; print_heading(regedit); diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h index 2ab40f9..113f226 100644 --- a/source3/utils/regedit.h +++ b/source3/utils/regedit.h @@ -59,4 +59,17 @@ WERROR reg_open_samba3(TALLOC_CTX *mem_ctx, struct registry_context **ctx); int regedit_getch(void); +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; + unsigned int search_key:1; + unsigned int search_value:1; + unsigned int search_recursive:1; + unsigned int search_relative:1; + unsigned int search_nocase:1; +}; + #endif diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index c3dd198..944fcc8 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1207,3 +1207,17 @@ finish: return sel; } + +int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) +{ + int rv; + // TODO + + opts->search_key = 1; + opts->search_recursive = 1; + opts->search_nocase = 1; + + rv = dialog_input(ctx, &opts->query, "Search", "Query"); + + return rv; +} diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index 0a0aec8..f57bcd1 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -73,4 +73,8 @@ WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, int dialog_select_type(TALLOC_CTX *ctx, int *type); +struct regedit_search_opts; + +int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts); + #endif diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index cfcf5f2..ef85754 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -117,7 +117,7 @@ struct tree_node *tree_node_last(struct tree_node *list) return list; } -bool tree_node_has_children(struct tree_node *node) +static uint32_t get_num_subkeys(struct tree_node *node) { const char *classname; uint32_t num_subkeys; @@ -128,20 +128,49 @@ bool tree_node_has_children(struct tree_node *node) uint32_t max_valbufsize; WERROR rv; - if (node->child_head) { - return true; - } - rv = reg_key_get_info(node, node->key, &classname, &num_subkeys, &num_values, &last_change_time, &max_subkeynamelen, &max_valnamelen, &max_valbufsize); if (W_ERROR_IS_OK(rv)) { - return num_subkeys != 0; + return num_subkeys; + } + + return 0; +} + +bool tree_node_has_children(struct tree_node *node) +{ + if (node->child_head) { + return true; } - return false; + return get_num_subkeys(node) > 0; +} + +static int node_cmp(struct tree_node **a, struct tree_node **b) +{ + return strcmp((*a)->name, (*b)->name); +} + +void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node) +{ + list = tree_node_first(list); + + if (node_cmp(&list, &node) >= 0) { + tree_node_append(node, list); + if (list->parent) { + list->parent->child_head = node; + } + return; + } + + while (list->next && node_cmp(&list->next, &node) < 0) { + list = list->next; + } + + tree_node_append(list, node); } WERROR tree_node_load_children(struct tree_node *node) @@ -149,43 +178,63 @@ WERROR tree_node_load_children(struct tree_node *node) struct registry_key *key; const char *key_name, *klass; NTTIME modified; - uint32_t i; + uint32_t i, nsubkeys; WERROR rv; - struct tree_node *new_node, *prev; + struct tree_node *prev, **array; /* does this node already have it's children loaded? */ if (node->child_head) return WERR_OK; - for (prev = NULL, i = 0; ; ++i) { + nsubkeys = get_num_subkeys(node); + if (nsubkeys == 0) + return WERR_OK; + + array = talloc_zero_array(node, struct tree_node *, nsubkeys); + if (array == NULL) { + return WERR_NOMEM; + } + + for (i = 0; i < nsubkeys; ++i) { rv = reg_key_get_subkey_by_index(node, node->key, i, &key_name, &klass, &modified); if (!W_ERROR_IS_OK(rv)) { - if (W_ERROR_EQUAL(rv, WERR_NO_MORE_ITEMS)) { - break; - } - - return rv; + goto finish; } rv = reg_open_key(node, node->key, key_name, &key); if (!W_ERROR_IS_OK(rv)) { - return rv; + goto finish; } - new_node = tree_node_new(node, node, key_name, key); - if (new_node == NULL) { - return WERR_NOMEM; + array[i] = tree_node_new(node, node, key_name, key); + if (array[i] == NULL) { + rv = WERR_NOMEM; + goto finish; } + } + + TYPESAFE_QSORT(array, nsubkeys, node_cmp); + + for (i = 1, prev = array[0]; i < nsubkeys; ++i) { + tree_node_append(prev, array[i]); + prev = array[i]; + } + node->child_head = array[0]; + + rv = WERR_OK; - if (prev) { - tree_node_append(prev, new_node); +finish: + if (!W_ERROR_IS_OK(rv)) { + for (i = 0; i < nsubkeys; ++i) { + talloc_free(array[i]); } - prev = new_node; + node->child_head = NULL; } + talloc_free(array); - return WERR_OK; + return rv; } void tree_node_free_recursive(struct tree_node *list) @@ -236,16 +285,6 @@ void tree_view_clear(struct tree_view *view) view->current_items = NULL; } -static int item_comp(ITEM **a, ITEM **b) -{ - struct tree_node *nodea, *nodeb; - - nodea = item_userptr(*a); - nodeb = item_userptr(*b); - - return strcmp(nodea->name, nodeb->name); -} - WERROR tree_view_update(struct tree_view *view, struct tree_node *list) { ITEM **items; @@ -283,8 +322,6 @@ WERROR tree_view_update(struct tree_view *view, struct tree_node *list) set_item_userptr(items[i], node); } - TYPESAFE_QSORT(items, n_items, item_comp); - unpost_menu(view->menu); set_menu_items(view->menu, items); tree_view_free_current_items(view->current_items); @@ -298,6 +335,33 @@ fail: return WERR_NOMEM; } +/* is this node in the current level? */ +bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node) +{ + struct tree_node *first; + + first = item_userptr(view->current_items[0]); + + return first->parent == node->parent; +} + +void tree_view_set_current_node(struct tree_view *view, struct tree_node *node) +{ + ITEM **it; + + for (it = view->current_items; *it; ++it) { + if (item_userptr(*it) == node) { + set_current_item(view->menu, *it); + return; + } + } +} + +struct tree_node *tree_view_get_current_node(struct tree_view *view) +{ + return item_userptr(current_item(view->menu)); +} + void tree_view_set_selected(struct tree_view *view, bool select) { attr_t attr = A_NORMAL; diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 66e692e..152ba3c 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -70,5 +70,9 @@ void tree_view_clear(struct tree_view *view); WERROR tree_view_update(struct tree_view *view, struct tree_node *list); bool tree_node_has_children(struct tree_node *node); WERROR tree_node_load_children(struct tree_node *node); +void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node); +bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node); +void tree_view_set_current_node(struct tree_view *view, struct tree_node *node); +struct tree_node *tree_view_get_current_node(struct tree_view *view); #endif -- 2.1.1 From ac78eeafbfa98f56cb9fa6688e50073e5629b6fd Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 9 Jun 2014 18:29:56 -0700 Subject: [PATCH 09/45] regedit: add multicolumn list widget Widget works for lists with one or more column(s). Column headers are optional. As a test, the patch also modifies regedit to use the new widget for viewing registry keys. Valuelist still needs to be upgraded to use this. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 7ad75367b2a42225a9f3539172e0b0e320d3a517) --- source3/utils/regedit.c | 28 +-- source3/utils/regedit.h | 4 + source3/utils/regedit_list.c | 524 +++++++++++++++++++++++++++++++++++++++ source3/utils/regedit_list.h | 78 ++++++ source3/utils/regedit_treeview.c | 189 ++++++-------- source3/utils/regedit_treeview.h | 9 +- source3/wscript_build | 2 +- 7 files changed, 703 insertions(+), 131 deletions(-) create mode 100644 source3/utils/regedit_list.c create mode 100644 source3/utils/regedit_list.h diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 77c254b..ea9aec8 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -25,6 +25,7 @@ #include "regedit_treeview.h" #include "regedit_valuelist.h" #include "regedit_dialog.h" +#include "regedit_list.h" #include #include #include @@ -50,9 +51,6 @@ #define PATH_WIDTH (COLS - 6) #define PATH_WIDTH_MAX 1024 -#define PAIR_YELLOW_CYAN 1 -#define PAIR_BLACK_CYAN 2 - struct regedit { WINDOW *main_window; WINDOW *path_label; @@ -351,17 +349,17 @@ static void handle_tree_input(struct regedit *regedit, int c) switch (c) { case KEY_DOWN: - menu_driver(regedit->keys->menu, REQ_DOWN_ITEM); + tree_view_driver(regedit->keys, ML_CURSOR_DOWN); load_values(regedit); break; case KEY_UP: - menu_driver(regedit->keys->menu, REQ_UP_ITEM); + tree_view_driver(regedit->keys, ML_CURSOR_UP); load_values(regedit); break; case '\n': case KEY_ENTER: case KEY_RIGHT: - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); if (node && tree_node_has_children(node)) { tree_node_load_children(node); print_path(regedit, node->child_head); @@ -370,7 +368,7 @@ static void handle_tree_input(struct regedit *regedit, int c) } break; case KEY_LEFT: - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); if (node && node->parent) { print_path(regedit, node->parent); node = tree_node_first(node->parent); @@ -380,19 +378,19 @@ static void handle_tree_input(struct regedit *regedit, int c) break; case 'n': case 'N': - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); add_reg_key(regedit, node, false); break; case 's': case 'S': - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); add_reg_key(regedit, node, true); break; case 'd': case 'D': { int sel; - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); if (!node->parent) { break; } @@ -451,7 +449,7 @@ static void handle_value_input(struct regedit *regedit, int c) vitem = item_userptr(current_item(regedit->vl->menu)); if (vitem) { struct tree_node *node; - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); dialog_edit_value(regedit, node->key, vitem->type, vitem, binmode); value_list_load(regedit->vl, node->key); @@ -465,7 +463,7 @@ static void handle_value_input(struct regedit *regedit, int c) sel = dialog_select_type(regedit, &new_type); if (sel == DIALOG_OK) { struct tree_node *node; - node = item_userptr(current_item(regedit->keys->menu)); + node = tree_view_get_current_node(regedit->keys); dialog_edit_value(regedit, node->key, new_type, NULL, false); value_list_load(regedit->vl, node->key); @@ -483,8 +481,8 @@ static void handle_value_input(struct regedit *regedit, int c) "Really delete value \"%s\"?", vitem->value_name); if (sel == DIALOG_OK) { - ITEM *it = current_item(regedit->keys->menu); - struct tree_node *node = item_userptr(it); + struct tree_node *node; + node = tree_view_get_current_node(regedit->keys); reg_del_value(regedit, node->key, vitem->value_name); value_list_load(regedit->vl, node->key); @@ -590,6 +588,7 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) assume_default_colors(COLOR_WHITE, COLOR_BLUE); init_pair(PAIR_YELLOW_CYAN, COLOR_YELLOW, COLOR_CYAN); init_pair(PAIR_BLACK_CYAN, COLOR_BLACK, COLOR_CYAN); + init_pair(PAIR_YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE); } regedit = talloc_zero(mem_ctx, struct regedit); @@ -620,7 +619,6 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) print_heading(regedit); tree_view_show(regedit->keys); - menu_driver(regedit->keys->menu, REQ_FIRST_ITEM); load_values(regedit); value_list_show(regedit->vl); diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h index 113f226..688bc72 100644 --- a/source3/utils/regedit.h +++ b/source3/utils/regedit.h @@ -72,4 +72,8 @@ struct regedit_search_opts { unsigned int search_nocase:1; }; +#define PAIR_YELLOW_CYAN 1 +#define PAIR_BLACK_CYAN 2 +#define PAIR_YELLOW_BLUE 3 + #endif diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c new file mode 100644 index 0000000..2da70bf --- /dev/null +++ b/source3/utils/regedit_list.c @@ -0,0 +1,524 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "regedit_list.h" +#include "regedit.h" + +struct multilist { + WINDOW *window; + WINDOW *pad; + + unsigned window_height; + unsigned window_width; + unsigned start_row; + unsigned cursor_row; + + unsigned ncols; + struct multilist_column *columns; + + const void *data; + unsigned nrows; + const void *current_row; + const struct multilist_accessors *cb; +}; + +/* data getters */ +static const void *data_get_first_row(struct multilist *list) +{ + SMB_ASSERT(list->cb->get_first_row); + return list->cb->get_first_row(list->data); +} + +static const void *data_get_next_row(struct multilist *list, const void *row) +{ + SMB_ASSERT(list->cb->get_next_row); + return list->cb->get_next_row(list->data, row); +} + +static const void *data_get_prev_row(struct multilist *list, const void *row) +{ + const void *tmp, *next; + + if (list->cb->get_prev_row) { + return list->cb->get_prev_row(list->data, row); + } + + tmp = data_get_first_row(list); + if (tmp == row) { + return NULL; + } + + for (; tmp && (next = data_get_next_row(list, tmp)) != row; + tmp = next) { + } + + SMB_ASSERT(tmp != NULL); + + return tmp; +} + +static unsigned data_get_row_count(struct multilist *list) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_count) + return list->cb->get_row_count(list->data); + + for (i = 0, row = data_get_first_row(list); + row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return i; +} + +static const void *data_get_row_n(struct multilist *list, size_t n) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_n) + return list->cb->get_row_n(list->data, n); + + for (i = 0, row = data_get_first_row(list); + i < n && row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return row; +} + +static const char *data_get_column_header(struct multilist *list, unsigned col) +{ + SMB_ASSERT(list->cb->get_column_header); + return list->cb->get_column_header(list->data, col); +} + +static const char *data_get_item_label(struct multilist *list, const void *row, + unsigned col) +{ + SMB_ASSERT(list->cb->get_item_label); + return list->cb->get_item_label(row, col); +} + +static const char *data_get_item_prefix(struct multilist *list, const void *row, + unsigned col) +{ + if (list->cb->get_item_prefix) + return list->cb->get_item_prefix(row, col); + return ""; +} + +static int multilist_free(struct multilist *list) +{ + if (list->pad) { + delwin(list->pad); + } + + return 0; +} + +struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window, + const struct multilist_accessors *cb, + unsigned ncol) +{ + struct multilist *list; + + SMB_ASSERT(ncol > 0); + + list = talloc_zero(ctx, struct multilist); + if (list == NULL) { + return NULL; + } + talloc_set_destructor(list, multilist_free); + + list->cb = cb; + list->ncols = ncol; + list->columns = talloc_zero_array(list, struct multilist_column, ncol); + if (list->columns == NULL) { + talloc_free(list); + return NULL; + } + multilist_set_window(list, window); + + return list; +} + +struct multilist_column *multilist_column_config(struct multilist *list, + unsigned col) +{ + SMB_ASSERT(col < list->ncols); + return &list->columns[col]; +} + +static void put_padding(WINDOW *win, size_t col_width, size_t item_len) +{ + size_t amt; + + SMB_ASSERT(item_len <= col_width); + + amt = col_width - item_len; + while (amt--) { + waddch(win, ' '); + } +} + +static void put_item(struct multilist *list, WINDOW *win, unsigned col, + const char *item, int attr) +{ + bool append_sep = true; + unsigned i; + size_t len; + struct multilist_column *col_info; + bool trim = false; + + SMB_ASSERT(col < list->ncols); + SMB_ASSERT(item != NULL); + + if (col == list->ncols - 1) { + append_sep = false; + } + col_info = &list->columns[col]; + + len = strlen(item); + if (len > col_info->width) { + len = col_info->width; + trim = true; + } + + if (col_info->align_right) { + put_padding(win, col_info->width, len); + } + for (i = 0; i < len; ++i) { + if (i == len - 1 && trim) { + waddch(win, '~' | attr); + } else { + waddch(win, item[i] | attr); + } + } + if (!col_info->align_right) { + put_padding(win, col_info->width, len); + } + + if (append_sep) { + waddch(win, ' '); + waddch(win, '|'); + waddch(win, ' '); + } +} + +static void put_header(struct multilist *list) +{ + unsigned col; + const char *header; + + if (!list->cb->get_column_header) { + return; + } + + wmove(list->window, 0, 0); + for (col = 0; col < list->ncols; ++col) { + header = data_get_column_header(list, col); + SMB_ASSERT(header != NULL); + put_item(list, list->window, col, header, + A_BOLD | COLOR_PAIR(PAIR_YELLOW_BLUE)); + } +} + +static WERROR put_data(struct multilist *list) +{ + const void *row; + int ypos; + unsigned col; + const char *prefix, *item; + char *tmp; + + for (ypos = 0, row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row), ++ypos) { + wmove(list->pad, ypos, 0); + for (col = 0; col < list->ncols; ++col) { + prefix = data_get_item_prefix(list, row, col); + SMB_ASSERT(prefix != NULL); + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + tmp = talloc_asprintf(list, "%s%s", prefix, item); + if (tmp == NULL) { + return WERR_NOMEM; + } + put_item(list, list->pad, col, tmp, 0); + talloc_free(tmp); + } + } + + return WERR_OK; +} + +static struct multilist_column *find_widest_column(struct multilist *list) +{ + unsigned col; + struct multilist_column *colp; + + SMB_ASSERT(list->ncols > 0); + colp = &list->columns[0]; + + for (col = 1; col < list->ncols; ++col) { + if (list->columns[col].width > colp->width) { + colp = &list->columns[col]; + } + } + + return colp; +} + +static WERROR calc_column_widths(struct multilist *list) +{ + const void *row; + unsigned col; + size_t len; + const char *item; + size_t width, total_width, overflow; + + /* calculate the maximum widths for each column */ + for (col = 0; col < list->ncols; ++col) { + len = 0; + if (list->cb->get_column_header) { + item = data_get_column_header(list, col); + len = strlen(item); + } + list->columns[col].width = len; + } + + for (row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row)) { + for (col = 0; col < list->ncols; ++col) { + item = data_get_item_prefix(list, row, col); + SMB_ASSERT(item != NULL); + len = strlen(item); + + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + len += strlen(item); + if (len > list->columns[col].width) { + list->columns[col].width = len; + } + } + } + + /* calculate row width */ + for (width = 0, col = 0; col < list->ncols; ++col) { + width += list->columns[col].width; + } + /* width including column spacing and separations */ + total_width = width + (list->ncols - 1) * 3; + /* if everything fits, we're done */ + if (total_width <= list->window_width) { + return WERR_OK; + } + + overflow = total_width - list->window_width; + /* the window is so narrow that no amount of trimming will + help fit the data. just give up. */ + if (overflow > width) { + return WERR_OK; + } + + /* keep trimming from the widest column until the row fits */ + while (overflow) { + struct multilist_column *colp = find_widest_column(list); + size_t max_trim = colp->width / 3; + size_t trim = MIN(overflow, max_trim); + colp->width -= trim; + overflow -= trim; + } + + return WERR_OK; +} + +static void highlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_REVERSE, 0, NULL); +} + +static void unhighlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_NORMAL, 0, NULL); +} + +const void *multilist_get_data(struct multilist *list) +{ + return list->data; +} + +WERROR multilist_set_data(struct multilist *list, const void *data) +{ + WERROR rv; + + SMB_ASSERT(list->window != NULL); + list->data = data; + + calc_column_widths(list); + + if (list->pad) { + delwin(list->pad); + } + /* construct a pad that is exactly the width of the window, and + as tall as required to fit all data rows. */ + list->nrows = data_get_row_count(list); + list->pad = newpad(MAX(list->nrows, 1), list->window_width); + if (list->pad == NULL) { + return WERR_NOMEM; + } + + /* add the column headers to the window and render all rows to + the pad. */ + werase(list->window); + put_header(list); + rv = put_data(list); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + /* initialize the cursor */ + list->start_row = 0; + list->cursor_row = 0; + list->current_row = data_get_first_row(list); + highlight_current_row(list); + + return WERR_OK; +} + +WERROR multilist_set_window(struct multilist *list, WINDOW *window) +{ + int maxy, maxx; + bool rerender = false; + + getmaxyx(window, maxy, maxx); + + /* rerender pad if window width is different. */ + if (list->data && maxx != list->window_width) { + rerender = true; + } + + list->window = window; + list->window_width = maxx; + list->window_height = maxy; + if (rerender) { + return multilist_set_data(list, list->data); + } else { + put_header(list); + } + + return WERR_OK; +} + +void multilist_refresh(struct multilist *list) +{ + int window_start_row, height; + + /* copy from pad, starting at start_row, to the window, accounting + for the column header (if present). */ + height = MIN(list->window_height, list->nrows); + window_start_row = 0; + if (list->cb->get_column_header) { + window_start_row = 1; + if (height < list->window_height) { + height++; + } + } + copywin(list->pad, list->window, list->start_row, 0, + window_start_row, 0, height - 1, list->window_width - 1, + false); +} + +static void fix_start_row(struct multilist *list) +{ + int height; + + /* adjust start_row so that the cursor appears on the screen */ + + height = list->window_height; + if (list->cb->get_column_header) { + height--; + } + if (list->cursor_row < list->start_row) { + list->start_row = list->cursor_row; + } else if (list->cursor_row >= list->start_row + height) { + list->start_row = list->cursor_row - height + 1; + } +} + +void multilist_driver(struct multilist *list, int c) +{ + const void *tmp; + + if (list->nrows == 0) { + return; + } + + switch (c) { + case ML_CURSOR_UP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row--; + tmp = data_get_prev_row(list, list->current_row); + break; + case ML_CURSOR_DOWN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row++; + tmp = data_get_next_row(list, list->current_row); + break; + } + + SMB_ASSERT(tmp); + list->current_row = tmp; + highlight_current_row(list); + fix_start_row(list); +} + +const void *multilist_get_current_row(struct multilist *list) +{ + return list->current_row; +} + +void multilist_set_current_row(struct multilist *list, const void *row) +{ + unsigned i; + const void *tmp; + + for (i = 0, tmp = data_get_first_row(list); + tmp != NULL; + ++i, tmp = data_get_next_row(list, tmp)) { + if (tmp == row) { + unhighlight_current_row(list); + list->cursor_row = i; + list->current_row = row; + highlight_current_row(list); + fix_start_row(list); + return; + } + } +} diff --git a/source3/utils/regedit_list.h b/source3/utils/regedit_list.h new file mode 100644 index 0000000..4b1840a --- /dev/null +++ b/source3/utils/regedit_list.h @@ -0,0 +1,78 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _REGEDIT_LIST_H_ +#define _REGEDIT_LIST_H_ + +#include "includes.h" +#include + +struct multilist_accessors { + /* (optional) return the column header for col */ + const char *(*get_column_header)(const void *data, unsigned col); + + /* return a pointer to the first row of data */ + const void *(*get_first_row)(const void *data); + + /* (optional) return a count of all data rows */ + size_t (*get_row_count)(const void *data); + + /* return the next row or NULL if there aren't any more */ + const void *(*get_next_row)(const void *data, const void *row); + + /* (optional) return the previous row or NULL if row is on top. */ + const void *(*get_prev_row)(const void *data, const void *row); + + /* (optional) return row n of data */ + const void *(*get_row_n)(const void *data, size_t n); + + /* return the label for row and col */ + const char *(*get_item_label)(const void *row, unsigned col); + + /* (optional) return a prefix string to be prepended to an item's + label. */ + const char *(*get_item_prefix)(const void *row, unsigned col); +}; + +struct multilist_column { + size_t width; + unsigned int align_right:1; +}; + +struct multilist; + +struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window, + const struct multilist_accessors *cb, + unsigned ncol); +struct multilist_column *multilist_column_config(struct multilist *list, + unsigned col); +WERROR multilist_set_window(struct multilist *list, WINDOW *window); +const void *multilist_get_data(struct multilist *list); +WERROR multilist_set_data(struct multilist *list, const void *data); +void multilist_refresh(struct multilist *list); + +enum { + ML_CURSOR_UP, + ML_CURSOR_DOWN +}; +void multilist_driver(struct multilist *list, int c); +const void *multilist_get_current_row(struct multilist *list); +void multilist_set_current_row(struct multilist *list, const void *row); + +#endif diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index ef85754..7e65c39 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -18,6 +18,7 @@ */ #include "regedit_treeview.h" +#include "regedit_list.h" #include "lib/registry/registry.h" #define HEADING_X 3 @@ -254,112 +255,47 @@ void tree_node_free_recursive(struct tree_node *list) } } -static void tree_view_free_current_items(ITEM **items) -{ - size_t i; - struct tree_node *node; - ITEM *item; - - if (items == NULL) { - return; - } - - for (i = 0; items[i] != NULL; ++i) { - item = items[i]; - node = item_userptr(item); - if (node && node->label) { - talloc_free(node->label); - node->label = NULL; - } - free_item(item); - } - - talloc_free(items); -} - void tree_view_clear(struct tree_view *view) { - unpost_menu(view->menu); - set_menu_items(view->menu, view->empty); - tree_view_free_current_items(view->current_items); - view->current_items = NULL; + multilist_set_data(view->list, NULL); } WERROR tree_view_update(struct tree_view *view, struct tree_node *list) { - ITEM **items; - struct tree_node *node; - size_t i, n_items; - - if (list == NULL) { - list = view->root; - } - for (n_items = 0, node = list; node != NULL; node = node->next) { - n_items++; - } - - items = talloc_zero_array(view, ITEM *, n_items + 1); - if (items == NULL) { - return WERR_NOMEM; - } - - for (i = 0, node = list; node != NULL; ++i, node = node->next) { - char prefix = ' '; - - /* Add a '+' marker to indicate that the item has - descendants. */ - if (tree_node_has_children(node)) { - prefix = '+'; - } - - SMB_ASSERT(node->label == NULL); - node->label = talloc_asprintf(node, "%c%s", prefix, node->name); - if (node->label == NULL) { - goto fail; - } + WERROR rv; - items[i] = new_item(node->label, node->name); - set_item_userptr(items[i], node); + rv = multilist_set_data(view->list, list); + if (W_ERROR_IS_OK(rv)) { + multilist_refresh(view->list); } - unpost_menu(view->menu); - set_menu_items(view->menu, items); - tree_view_free_current_items(view->current_items); - view->current_items = items; - - return WERR_OK; - -fail: - tree_view_free_current_items(items); - - return WERR_NOMEM; + return rv; } /* is this node in the current level? */ bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node) { - struct tree_node *first; + const struct tree_node *first; - first = item_userptr(view->current_items[0]); + first = multilist_get_data(view->list); - return first->parent == node->parent; + return first && first->parent == node->parent; } void tree_view_set_current_node(struct tree_view *view, struct tree_node *node) { - ITEM **it; - - for (it = view->current_items; *it; ++it) { - if (item_userptr(*it) == node) { - set_current_item(view->menu, *it); - return; - } - } + multilist_set_current_row(view->list, node); } struct tree_node *tree_view_get_current_node(struct tree_view *view) { - return item_userptr(current_item(view->menu)); + return discard_const_p(struct tree_node, + multilist_get_current_row(view->list)); +} + +void tree_view_driver(struct tree_view *view, int c) +{ + multilist_driver(view->list, c); } void tree_view_set_selected(struct tree_view *view, bool select) @@ -374,18 +310,14 @@ void tree_view_set_selected(struct tree_view *view, bool select) void tree_view_show(struct tree_view *view) { - post_menu(view->menu); + multilist_refresh(view->list); + touchwin(view->window); + wnoutrefresh(view->window); + wnoutrefresh(view->sub); } static int tree_view_free(struct tree_view *view) { - if (view->menu) { - unpost_menu(view->menu); - free_menu(view->menu); - } - if (view->empty[0]) { - free_item(view->empty[0]); - } if (view->panel) { del_panel(view->panel); } @@ -395,18 +327,69 @@ static int tree_view_free(struct tree_view *view) if (view->window) { delwin(view->window); } - tree_view_free_current_items(view->current_items); tree_node_free_recursive(view->root); return 0; } +static const char *tv_get_column_header(const void *data, unsigned col) +{ + SMB_ASSERT(col == 0); + return "Name"; +} + +static const void *tv_get_first_row(const void *data) +{ + return data; +} + +static const void *tv_get_next_row(const void *data, const void *row) +{ + const struct tree_node *node = row; + SMB_ASSERT(node != NULL); + return node->next; +} + +static const void *tv_get_prev_row(const void *data, const void *row) +{ + const struct tree_node *node = row; + SMB_ASSERT(node != NULL); + return node->previous; +} + +static const char *tv_get_item_prefix(const void *row, unsigned col) +{ + struct tree_node *node = discard_const_p(struct tree_node, row); + SMB_ASSERT(col == 0); + SMB_ASSERT(node != NULL); + if (tree_node_has_children(node)) { + return "+"; + } + return " "; +} + +static const char *tv_get_item_label(const void *row, unsigned col) +{ + const struct tree_node *node = row; + SMB_ASSERT(col == 0); + SMB_ASSERT(node != NULL); + return node->name; +} + +static struct multilist_accessors tv_accessors = { + .get_column_header = tv_get_column_header, + .get_first_row = tv_get_first_row, + .get_next_row = tv_get_next_row, + .get_prev_row = tv_get_prev_row, + .get_item_prefix = tv_get_item_prefix, + .get_item_label = tv_get_item_label +}; + struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, int nlines, int ncols, int begin_y, int begin_x) { struct tree_view *view; - static const char *dummy = "(empty)"; view = talloc_zero(ctx, struct tree_view); if (view == NULL) { @@ -415,10 +398,6 @@ struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, talloc_set_destructor(view, tree_view_free); - view->empty[0] = new_item(dummy, dummy); - if (view->empty[0] == NULL) { - goto fail; - } view->window = newwin(nlines, ncols, begin_y, begin_x); if (view->window == NULL) { goto fail; @@ -437,16 +416,10 @@ struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, } view->root = root; - view->menu = new_menu(view->empty); - if (view->menu == NULL) { + view->list = multilist_new(view, view->sub, &tv_accessors, 1); + if (view->list == NULL) { goto fail; } - set_menu_format(view->menu, nlines, 1); - set_menu_win(view->menu, view->window); - set_menu_sub(view->menu, view->sub); - menu_opts_off(view->menu, O_SHOWDESC); - set_menu_mark(view->menu, "* "); - tree_view_update(view, root); return view; @@ -458,11 +431,10 @@ fail: } void tree_view_resize(struct tree_view *view, int nlines, int ncols, - int begin_y, int begin_x) + int begin_y, int begin_x) { WINDOW *nwin, *nsub; - unpost_menu(view->menu); nwin = newwin(nlines, ncols, begin_y, begin_x); nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); replace_panel(view->panel, nwin); @@ -472,10 +444,7 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, view->sub = nsub; box(view->window, 0, 0); mvwprintw(view->window, 0, HEADING_X, "Key"); - set_menu_format(view->menu, nlines, 1); - set_menu_win(view->menu, view->window); - set_menu_sub(view->menu, view->sub); - post_menu(view->menu); + multilist_set_window(view->list, view->sub); } static void print_path_recursive(WINDOW *label, struct tree_node *node, diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 152ba3c..cd17a3c 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -22,7 +22,6 @@ #include "includes.h" #include -#include #include struct registry_key; @@ -30,7 +29,6 @@ struct registry_key; struct tree_node { char *name; - char *label; struct registry_key *key; struct tree_node *parent; @@ -39,15 +37,15 @@ struct tree_node { struct tree_node *next; }; +struct multilist; + struct tree_view { struct tree_node *root; WINDOW *window; WINDOW *sub; PANEL *panel; - MENU *menu; - ITEM **current_items; - ITEM *empty[2]; + struct multilist *list; }; struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, @@ -74,5 +72,6 @@ void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node); bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node); void tree_view_set_current_node(struct tree_view *view, struct tree_node *node); struct tree_node *tree_view_get_current_node(struct tree_view *view); +void tree_view_driver(struct tree_view *view, int c); #endif diff --git a/source3/wscript_build b/source3/wscript_build index 9103b14..7394a57 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1459,7 +1459,7 @@ bld.SAMBA3_BINARY('samba-regedit', source="""utils/regedit.c utils/regedit_samba3.c utils/regedit_wrap.c utils/regedit_treeview.c utils/regedit_valuelist.c utils/regedit_dialog.c - utils/regedit_hexedit.c""", + utils/regedit_hexedit.c utils/regedit_list.c""", deps='ncurses menu panel form registry param popt_samba3 smbregistry', enabled=bld.env.build_regedit) -- 2.1.1 From b3ad33e1d6aa0fb432bf531fa7a11a3f6b898845 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Tue, 10 Jun 2014 10:35:19 -0700 Subject: [PATCH 10/45] regedit: make value list display data in multiple columns Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit ec6a7a7335f5b9e3a54b9967563ba198fc67b120) --- source3/utils/regedit.c | 8 +- source3/utils/regedit_treeview.c | 1 + source3/utils/regedit_valuelist.c | 274 ++++++++++++++++++++++---------------- source3/utils/regedit_valuelist.h | 16 ++- 4 files changed, 171 insertions(+), 128 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index ea9aec8..b8f442c 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -435,10 +435,10 @@ static void handle_value_input(struct regedit *regedit, int c) switch (c) { case KEY_DOWN: - menu_driver(regedit->vl->menu, REQ_DOWN_ITEM); + value_list_driver(regedit->vl, ML_CURSOR_DOWN); break; case KEY_UP: - menu_driver(regedit->vl->menu, REQ_UP_ITEM); + value_list_driver(regedit->vl, ML_CURSOR_UP); break; case 'b': case 'B': @@ -446,7 +446,7 @@ static void handle_value_input(struct regedit *regedit, int c) /* Falthrough... */ case '\n': case KEY_ENTER: - vitem = item_userptr(current_item(regedit->vl->menu)); + vitem = value_list_get_current_item(regedit->vl); if (vitem) { struct tree_node *node; node = tree_view_get_current_node(regedit->keys); @@ -472,7 +472,7 @@ static void handle_value_input(struct regedit *regedit, int c) } case 'd': case 'D': - vitem = item_userptr(current_item(regedit->vl->menu)); + vitem = value_list_get_current_item(regedit->vl); if (vitem) { int sel; diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 7e65c39..ee6b631 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -445,6 +445,7 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, box(view->window, 0, 0); mvwprintw(view->window, 0, HEADING_X, "Key"); multilist_set_window(view->list, view->sub); + tree_view_show(view); } static void print_path_recursive(WINDOW *label, struct tree_node *node, diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index 791a8ca..4922cdb 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -18,39 +18,13 @@ */ #include "regedit_valuelist.h" +#include "regedit_list.h" #include "lib/registry/registry.h" #define HEADING_X 3 -static void value_list_free_items(ITEM **items) -{ - size_t i; - ITEM *item; - struct value_item *vitem; - - if (items == NULL) { - return; - } - - for (i = 0; items[i] != NULL; ++i) { - item = items[i]; - vitem = item_userptr(item); - SMB_ASSERT(vitem != NULL); - free_item(item); - } - - talloc_free(items); -} - static int value_list_free(struct value_list *vl) { - if (vl->menu) { - unpost_menu(vl->menu); - free_menu(vl->menu); - } - if (vl->empty && vl->empty[0]) { - free_item(vl->empty[0]); - } if (vl->panel) { del_panel(vl->panel); } @@ -60,16 +34,94 @@ static int value_list_free(struct value_list *vl) if (vl->window) { delwin(vl->window); } - value_list_free_items(vl->items); 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"; + } + + return "???"; +} + +static const void *vl_get_first_row(const void *data) +{ + const struct value_list *vl = data; + + if (vl && vl->nvalues) { + return &vl->values[0]; + } + return NULL; +} + +static const void *vl_get_next_row(const void *data, const void *row) +{ + const struct value_list *vl = data; + const struct value_item *value = row; + + SMB_ASSERT(vl != NULL); + SMB_ASSERT(value != NULL); + if (value == &vl->values[vl->nvalues - 1]) { + return NULL; + } + + return value + 1; +} + +static const void *vl_get_prev_row(const void *data, const void *row) +{ + const struct value_list *vl = data; + const struct value_item *value = row; + + SMB_ASSERT(vl != NULL); + SMB_ASSERT(value != NULL); + if (value == &vl->values[0]) { + return NULL; + } + + 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 ""; + } + + 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); @@ -79,15 +131,6 @@ 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) { - goto fail; - } - vl->empty[0] = new_item(empty, empty_desc); - if (vl->empty[0] == NULL) { - goto fail; - } - vl->window = newwin(nlines, ncols, begin_y, begin_x); if (vl->window == NULL) { goto fail; @@ -105,18 +148,11 @@ struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, 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); - set_menu_sub(vl->menu, vl->sub); - - menu_opts_on(vl->menu, O_SHOWDESC); - set_menu_mark(vl->menu, "* "); - return vl; fail: @@ -140,7 +176,6 @@ void value_list_resize(struct value_list *vl, int nlines, int ncols, { WINDOW *nwin, *nsub; - unpost_menu(vl->menu); nwin = newwin(nlines, ncols, begin_y, begin_x); nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); replace_panel(vl->panel, nwin); @@ -150,10 +185,8 @@ void value_list_resize(struct value_list *vl, int nlines, int ncols, vl->sub = nsub; box(vl->window, 0, 0); mvwprintw(vl->window, 0, HEADING_X, "Value"); - set_menu_format(vl->menu, nlines, 1); - set_menu_win(vl->menu, vl->window); - set_menu_sub(vl->menu, vl->sub); - post_menu(vl->menu); + 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) @@ -181,7 +214,10 @@ 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 bool string_is_printable(const char *s) @@ -197,7 +233,7 @@ static bool string_is_printable(const char *s) return true; } -static WERROR append_data_summary(struct value_item *vitem) +static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem) { char *tmp = NULL; @@ -209,42 +245,53 @@ 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; } vitem->unprintable = !string_is_printable(s); if (vitem->unprintable) { - tmp = talloc_asprintf_append(vitem->value_desc, - "(unprintable)"); + tmp = talloc_asprintf(ctx, "(unprintable)"); } else { - tmp = talloc_asprintf_append(vitem->value_desc, - "(\"%s\")", s); + 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) { + 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])) { - tmp = talloc_asprintf_append(tmp, - "(unprintable)"); + val = "(unprintable)"; vitem->unprintable = true; } else { - tmp = talloc_asprintf_append(tmp, "\"%s\" ", - a[i]); + 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; @@ -253,12 +300,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, - "()"); + tmp = talloc_asprintf(ctx, "(unknown)"); break; } @@ -266,80 +312,74 @@ static WERROR append_data_summary(struct value_item *vitem) return WERR_NOMEM; } - vitem->value_desc = tmp; + vitem->value = tmp; return WERR_OK; } +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) { - uint32_t n_values; + 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; - } - - rv = append_data_summary(vitem); + rv = append_data_summary(new_items, vitem); if (!W_ERROR_IS_OK(rv)) { - talloc_free(vitem); + talloc_free(new_items); return rv; } + } - /* ncurses won't accept empty strings in menu items */ - name = vitem->value_name; - if (name[0] == '\0') { - name = empty_name; - } - new_items[idx] = new_item(name, vitem->value_desc); - if (new_items[idx] == NULL) { - talloc_free(vitem); - return WERR_NOMEM; - } + TYPESAFE_QSORT(new_items, nvalues, vitem_cmp); - set_item_userptr(new_items[idx], vitem); + vl->nvalues = nvalues; + vl->values = new_items; + rv = multilist_set_data(vl->list, vl); + if (W_ERROR_IS_OK(rv)) { + multilist_refresh(vl->list); } - set_menu_items(vl->menu, new_items); - value_list_free_items(vl->items); - vl->items = new_items; + return rv; +} - return WERR_OK; +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_driver(struct value_list *vl, int c) +{ + multilist_driver(vl->list, c); } diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h index 16b0d50..ea67075 100644 --- a/source3/utils/regedit_valuelist.h +++ b/source3/utils/regedit_valuelist.h @@ -22,7 +22,6 @@ #include "includes.h" #include -#include #include struct registry_key; @@ -31,19 +30,20 @@ struct value_item { uint32_t type; DATA_BLOB data; const char *value_name; - char *value_desc; + char *value; bool unprintable; }; +struct multilist; + struct value_list { WINDOW *window; WINDOW *sub; PANEL *panel; - MENU *menu; - ITEM **items; - ITEM **empty; -} -; + size_t nvalues; + struct value_item *values; + struct multilist *list; +}; 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); @@ -51,5 +51,7 @@ void value_list_set_selected(struct value_list *vl, bool select); 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); +struct value_item *value_list_get_current_item(struct value_list *vl); +void value_list_driver(struct value_list *vl, int c); #endif -- 2.1.1 From e5849a3e56abb60eda2ed39e3ced3b1e357b27d4 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 11 Jun 2014 10:40:38 -0700 Subject: [PATCH 11/45] regedit: restore list cursor when window is resized Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit d38eba5244d0615994249070adee1f3df4c0d4aa) --- source3/utils/regedit_list.c | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c index 2da70bf..417dea8 100644 --- a/source3/utils/regedit_list.c +++ b/source3/utils/regedit_list.c @@ -406,6 +406,23 @@ WERROR multilist_set_data(struct multilist *list, const void *data) return WERR_OK; } +static void fix_start_row(struct multilist *list) +{ + int height; + + /* adjust start_row so that the cursor appears on the screen */ + + height = list->window_height; + if (list->cb->get_column_header) { + height--; + } + if (list->cursor_row < list->start_row) { + list->start_row = list->cursor_row; + } else if (list->cursor_row >= list->start_row + height) { + list->start_row = list->cursor_row - height + 1; + } +} + WERROR multilist_set_window(struct multilist *list, WINDOW *window) { int maxy, maxx; @@ -421,10 +438,17 @@ WERROR multilist_set_window(struct multilist *list, WINDOW *window) list->window = window; list->window_width = maxx; list->window_height = maxy; + list->start_row = 0; if (rerender) { - return multilist_set_data(list, list->data); + const void *row = multilist_get_current_row(list); + WERROR rv = multilist_set_data(list, list->data); + if (W_ERROR_IS_OK(rv) && row) { + multilist_set_current_row(list, row); + } + return rv; } else { put_header(list); + fix_start_row(list); } return WERR_OK; @@ -449,23 +473,6 @@ void multilist_refresh(struct multilist *list) false); } -static void fix_start_row(struct multilist *list) -{ - int height; - - /* adjust start_row so that the cursor appears on the screen */ - - height = list->window_height; - if (list->cb->get_column_header) { - height--; - } - if (list->cursor_row < list->start_row) { - list->start_row = list->cursor_row; - } else if (list->cursor_row >= list->start_row + height) { - list->start_row = list->cursor_row - height + 1; - } -} - void multilist_driver(struct multilist *list, int c) { const void *tmp; -- 2.1.1 From 7339bb6b33447cfc5fc337bd313c7238e7e73786 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sat, 14 Jun 2014 14:13:10 -0700 Subject: [PATCH 12/45] regedit: use talloc typesafety features Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit c85cc6b8360284c64d60ff4d09ca8d5e03188f7b) --- source3/utils/regedit_treeview.c | 31 ++++++++++++++++++++----------- source3/utils/regedit_valuelist.c | 20 +++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index ee6b631..47765c1 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -289,8 +289,8 @@ void tree_view_set_current_node(struct tree_view *view, struct tree_node *node) struct tree_node *tree_view_get_current_node(struct tree_view *view) { - return discard_const_p(struct tree_node, - multilist_get_current_row(view->list)); + const void *row = multilist_get_current_row(view->list); + return talloc_get_type_abort(row, struct tree_node); } void tree_view_driver(struct tree_view *view, int c) @@ -340,28 +340,36 @@ static const char *tv_get_column_header(const void *data, unsigned col) static const void *tv_get_first_row(const void *data) { - return data; + if (data == NULL) { + return NULL; + } + + return talloc_get_type_abort(data, struct tree_node); } static const void *tv_get_next_row(const void *data, const void *row) { - const struct tree_node *node = row; - SMB_ASSERT(node != NULL); + const struct tree_node *node; + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); return node->next; } static const void *tv_get_prev_row(const void *data, const void *row) { - const struct tree_node *node = row; - SMB_ASSERT(node != NULL); + const struct tree_node *node; + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); return node->previous; } static const char *tv_get_item_prefix(const void *row, unsigned col) { - struct tree_node *node = discard_const_p(struct tree_node, row); + struct tree_node *node; + SMB_ASSERT(col == 0); - SMB_ASSERT(node != NULL); + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); if (tree_node_has_children(node)) { return "+"; } @@ -370,9 +378,10 @@ static const char *tv_get_item_prefix(const void *row, unsigned col) static const char *tv_get_item_label(const void *row, unsigned col) { - const struct tree_node *node = row; + const struct tree_node *node; SMB_ASSERT(col == 0); - SMB_ASSERT(node != NULL); + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); return node->name; } diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index 4922cdb..1c80c09 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -54,21 +54,26 @@ static const char *vl_get_column_header(const void *data, unsigned col) static const void *vl_get_first_row(const void *data) { - const struct value_list *vl = data; + const struct value_list *vl; - if (vl && vl->nvalues) { - return &vl->values[0]; + if (data) { + vl = talloc_get_type_abort(data, struct value_list); + if (vl->nvalues) { + return &vl->values[0]; + } } + return NULL; } static const void *vl_get_next_row(const void *data, const void *row) { - const struct value_list *vl = data; + const struct value_list *vl; const struct value_item *value = row; - SMB_ASSERT(vl != NULL); + 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; } @@ -78,11 +83,12 @@ static const void *vl_get_next_row(const void *data, const void *row) static const void *vl_get_prev_row(const void *data, const void *row) { - const struct value_list *vl = data; + const struct value_list *vl; const struct value_item *value = row; - SMB_ASSERT(vl != NULL); + SMB_ASSERT(data != NULL); SMB_ASSERT(value != NULL); + vl = talloc_get_type_abort(data, struct value_list); if (value == &vl->values[0]) { return NULL; } -- 2.1.1 From 4a098be77c9178293d58bf0af87c3072a50d9966 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 16 Jun 2014 17:36:16 -0700 Subject: [PATCH 13/45] regedit: handle awkward window sizes better This fixes some assertion failures and an infinte loop that occurs when the terminal window is shrunk down far enough to the point regedit can't fit everything on screen. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 55662513e14c026eb4cfa044ed772b31be5d6ab9) --- source3/utils/regedit_list.c | 23 +++++++++++------------ source3/utils/regedit_treeview.c | 7 +++++++ source3/utils/regedit_valuelist.c | 7 +++++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c index 417dea8..ac4e240 100644 --- a/source3/utils/regedit_list.c +++ b/source3/utils/regedit_list.c @@ -271,6 +271,7 @@ static WERROR put_data(struct multilist *list) return WERR_OK; } +#define MIN_WIDTH 3 static struct multilist_column *find_widest_column(struct multilist *list) { unsigned col; @@ -285,6 +286,10 @@ static struct multilist_column *find_widest_column(struct multilist *list) } } + if (colp->width < MIN_WIDTH) { + return NULL; + } + return colp; } @@ -295,6 +300,7 @@ static WERROR calc_column_widths(struct multilist *list) size_t len; const char *item; size_t width, total_width, overflow; + struct multilist_column *colp; /* calculate the maximum widths for each column */ for (col = 0; col < list->ncols; ++col) { @@ -335,19 +341,12 @@ static WERROR calc_column_widths(struct multilist *list) } overflow = total_width - list->window_width; - /* the window is so narrow that no amount of trimming will - help fit the data. just give up. */ - if (overflow > width) { - return WERR_OK; - } - /* keep trimming from the widest column until the row fits */ - while (overflow) { - struct multilist_column *colp = find_widest_column(list); - size_t max_trim = colp->width / 3; - size_t trim = MIN(overflow, max_trim); - colp->width -= trim; - overflow -= trim; + /* attempt to trim as much as possible to fit all the columns to + the window */ + while (overflow && (colp = find_widest_column(list))) { + colp->width--; + overflow--; } return WERR_OK; diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 47765c1..9962ad9 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -445,7 +445,14 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, WINDOW *nwin, *nsub; 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(view->panel, nwin); delwin(view->sub); delwin(view->window); diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index 1c80c09..9557c8c 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -183,7 +183,14 @@ void value_list_resize(struct value_list *vl, int nlines, int ncols, WINDOW *nwin, *nsub; 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); -- 2.1.1 From 720ae399ef2580a41116ecb40c1ac5a1867cdd7a Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 25 Jun 2014 21:48:52 -0700 Subject: [PATCH 14/45] regedit: notify user if there's a failure loading subkeys Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 0b334dd1911206bac633421e1667052200574d29) --- source3/utils/regedit.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index b8f442c..7a47db2 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -361,10 +361,17 @@ static void handle_tree_input(struct regedit *regedit, int c) case KEY_RIGHT: node = tree_view_get_current_node(regedit->keys); if (node && tree_node_has_children(node)) { - tree_node_load_children(node); - print_path(regedit, node->child_head); - tree_view_update(regedit->keys, node->child_head); - value_list_load(regedit->vl, node->child_head->key); + WERROR rv; + + rv = tree_node_load_children(node); + if (W_ERROR_IS_OK(rv)) { + print_path(regedit, node->child_head); + tree_view_update(regedit->keys, node->child_head); + value_list_load(regedit->vl, node->child_head->key); + } else { + dialog_notice(regedit, DIA_ALERT, "Loading Subkeys", + "Failed to load subkeys."); + } } break; case KEY_LEFT: -- 2.1.1 From 8e75d8add141ecae4a71235fe5df78aeffa0f5b6 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 25 Jun 2014 21:55:27 -0700 Subject: [PATCH 15/45] regedit: include error description in popups Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit df36fe7998fee3fc22976aeb42c25e2a2f0d73c5) --- source3/utils/regedit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 7a47db2..2b32e94 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -236,8 +236,9 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, tree_view_clear(regedit->keys); tree_view_update(regedit->keys, list); } else { + msg = get_friendly_werror_msg(rv); dialog_notice(regedit, DIA_ALERT, "New Key", - "Failed to create key."); + "Failed to create key: %s", msg); } talloc_free(discard_const(name)); } @@ -369,8 +370,9 @@ static void handle_tree_input(struct regedit *regedit, int c) tree_view_update(regedit->keys, node->child_head); value_list_load(regedit->vl, node->child_head->key); } else { + const char *msg = get_friendly_werror_msg(rv); dialog_notice(regedit, DIA_ALERT, "Loading Subkeys", - "Failed to load subkeys."); + "Failed to load subkeys: %s", msg); } } break; @@ -423,8 +425,9 @@ static void handle_tree_input(struct regedit *regedit, int c) tree_view_update(regedit->keys, node); value_list_load(regedit->vl, node->key); } else { + const char *msg = get_friendly_werror_msg(rv); dialog_notice(regedit, DIA_ALERT, "Delete Key", - "Failed to delete key."); + "Failed to delete key: %s", msg); } } break; -- 2.1.1 From 7b4932b60c2cb5c50b571b55de2207d861e5643f Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 25 Jun 2014 22:29:06 -0700 Subject: [PATCH 16/45] regedit: don't fail loading keys if just a few are unavailable Sometimes a key might fail to open due to access restrictions. Only report failure if all keys failed to be opened. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit aba2b04b6c5584db63d03adab57fe1fd12ee71b4) --- source3/utils/regedit_treeview.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 9962ad9..c8a6596 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -179,7 +179,7 @@ WERROR tree_node_load_children(struct tree_node *node) struct registry_key *key; const char *key_name, *klass; NTTIME modified; - uint32_t i, nsubkeys; + uint32_t i, nsubkeys, count; WERROR rv; struct tree_node *prev, **array; @@ -196,7 +196,7 @@ WERROR tree_node_load_children(struct tree_node *node) return WERR_NOMEM; } - for (i = 0; i < nsubkeys; ++i) { + for (count = 0, i = 0; i < nsubkeys; ++i) { rv = reg_key_get_subkey_by_index(node, node->key, i, &key_name, &klass, &modified); @@ -206,25 +206,28 @@ WERROR tree_node_load_children(struct tree_node *node) rv = reg_open_key(node, node->key, key_name, &key); if (!W_ERROR_IS_OK(rv)) { - goto finish; + continue; } - array[i] = tree_node_new(node, node, key_name, key); - if (array[i] == NULL) { + array[count] = tree_node_new(node, node, key_name, key); + if (array[count] == NULL) { rv = WERR_NOMEM; goto finish; } + ++count; } - TYPESAFE_QSORT(array, nsubkeys, node_cmp); + if (count) { + TYPESAFE_QSORT(array, count, node_cmp); - for (i = 1, prev = array[0]; i < nsubkeys; ++i) { - tree_node_append(prev, array[i]); - prev = array[i]; - } - node->child_head = array[0]; + for (i = 1, prev = array[0]; i < count; ++i) { + tree_node_append(prev, array[i]); + prev = array[i]; + } + node->child_head = array[0]; - rv = WERR_OK; + rv = WERR_OK; + } finish: if (!W_ERROR_IS_OK(rv)) { -- 2.1.1 From 34292323dce0b0d740da9230f69aacf79f2ce5db Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 25 Jun 2014 22:51:27 -0700 Subject: [PATCH 17/45] regedit: set cursor to the parent node when ascending Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit baf491ff0bdb40520d8a093e5c08c8608ed3075a) --- source3/utils/regedit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 2b32e94..8b2a35d 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -380,8 +380,9 @@ static void handle_tree_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); if (node && node->parent) { print_path(regedit, node->parent); - node = tree_node_first(node->parent); - tree_view_update(regedit->keys, node); + node = node->parent; + tree_view_update(regedit->keys, tree_node_first(node)); + tree_view_set_current_node(regedit->keys, node); value_list_load(regedit->vl, node->key); } break; -- 2.1.1 From bacd2285185aaa3e58e3c62686f7e150577063b7 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Thu, 26 Jun 2014 07:51:22 -0700 Subject: [PATCH 18/45] regedit: set cursor after creating a new key Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 2a8beb99a49829adfbad1c887448e3a2caa32255) --- source3/utils/regedit.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 8b2a35d..471f875 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -235,6 +235,10 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, list = tree_node_first(node); tree_view_clear(regedit->keys); tree_view_update(regedit->keys, list); + if (!subkey) { + node = new_node; + } + tree_view_set_current_node(regedit->keys, node); } else { msg = get_friendly_werror_msg(rv); dialog_notice(regedit, DIA_ALERT, "New Key", -- 2.1.1 From b0ada6d626f68029a2488f3f334d7bff9ee6039f Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 27 Jun 2014 18:01:36 -0700 Subject: [PATCH 19/45] regedit: reopen parent keys when adding or removing subkeys This clears any cache associated with the parent key, and ensures the changes will be noticed by the UI. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 75045f052a5bc00fc8ffe35514c60e2f1611c9e9) --- source3/utils/regedit.c | 5 +++++ source3/utils/regedit_treeview.c | 9 +++++++++ source3/utils/regedit_treeview.h | 1 + 3 files changed, 15 insertions(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 471f875..b006fac 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -230,6 +230,10 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, name, new_key); SMB_ASSERT(new_node); tree_node_insert_sorted(list, new_node); + } else { + /* Reopen the parent key to make sure the + new subkey will be noticed. */ + tree_node_reopen_key(parent); } list = tree_node_first(node); @@ -419,6 +423,7 @@ static void handle_tree_input(struct regedit *regedit, int c) rv = reg_key_del(node, parent->key, node->name); if (W_ERROR_IS_OK(rv)) { + tree_node_reopen_key(parent); tree_view_clear(regedit->keys); pop = tree_node_pop(&node); tree_node_free_recursive(pop); diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index c8a6596..3af3c8b 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -141,6 +141,15 @@ static uint32_t get_num_subkeys(struct tree_node *node) return 0; } +WERROR tree_node_reopen_key(struct tree_node *node) +{ + SMB_ASSERT(node->parent != NULL); + SMB_ASSERT(node->name != NULL); + TALLOC_FREE(node->key); + return reg_open_key(node->parent, node->parent->key, node->name, + &node->key); +} + bool tree_node_has_children(struct tree_node *node) { if (node->child_head) { diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index cd17a3c..954bae5 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -66,6 +66,7 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, void tree_view_show(struct tree_view *view); void tree_view_clear(struct tree_view *view); WERROR tree_view_update(struct tree_view *view, struct tree_node *list); +WERROR tree_node_reopen_key(struct tree_node *node); bool tree_node_has_children(struct tree_node *node); WERROR tree_node_load_children(struct tree_node *node); void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node); -- 2.1.1 From 25cf42caf2d5e0157f7adacc6c5ab40ba8531003 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 27 Jun 2014 19:33:03 -0700 Subject: [PATCH 20/45] regedit: reopen key after editing or removing values Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 5732d3f01284a0080740f1cb69c91cf4893fb401) --- source3/utils/regedit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index b006fac..9b8a7dd 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -472,6 +472,7 @@ static void handle_value_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); dialog_edit_value(regedit, node->key, vitem->type, vitem, binmode); + tree_node_reopen_key(node); value_list_load(regedit->vl, node->key); } break; @@ -486,6 +487,7 @@ static void handle_value_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); dialog_edit_value(regedit, node->key, new_type, NULL, false); + tree_node_reopen_key(node); value_list_load(regedit->vl, node->key); } break; @@ -505,6 +507,7 @@ static void handle_value_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); reg_del_value(regedit, node->key, vitem->value_name); + tree_node_reopen_key(node); value_list_load(regedit->vl, node->key); } } -- 2.1.1 From 62a7e2b92432afb08a74c2dd169d782cb2f161bb Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 30 Jun 2014 23:14:20 -0700 Subject: [PATCH 21/45] regedit: add a refresh command to clear cache and reload current path This is needed to view changes made externally Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider (cherry picked from commit 41d5112407d7426d75671ac6967e62b080f63857) --- source3/utils/regedit.c | 32 +++++++++++++--- source3/utils/regedit_treeview.c | 83 ++++++++++++++++++++++++++++++++++++---- source3/utils/regedit_treeview.h | 4 +- 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 9b8a7dd..c0dbf90 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -52,6 +52,7 @@ #define PATH_WIDTH_MAX 1024 struct regedit { + struct registry_context *registry_context; WINDOW *main_window; WINDOW *path_label; size_t path_len; @@ -86,8 +87,7 @@ static void print_path(struct regedit *regedit, struct tree_node *node) } /* load all available hives */ -static struct tree_node *load_hives(TALLOC_CTX *mem_ctx, - struct registry_context *ctx) +static struct tree_node *load_hives(struct regedit *regedit) { const char *hives[] = { "HKEY_CLASSES_ROOT", @@ -110,12 +110,13 @@ static struct tree_node *load_hives(TALLOC_CTX *mem_ctx, prev = NULL; for (i = 0; hives[i] != NULL; ++i) { - rv = reg_get_predefined_key_by_name(ctx, hives[i], &key); + rv = reg_get_predefined_key_by_name(regedit->registry_context, + hives[i], &key); if (!W_ERROR_IS_OK(rv)) { continue; } - node = tree_node_new(mem_ctx, NULL, hives[i], key); + node = tree_node_new(regedit, NULL, hives[i], key); if (node == NULL) { return NULL; } @@ -426,7 +427,7 @@ static void handle_tree_input(struct regedit *regedit, int c) tree_node_reopen_key(parent); tree_view_clear(regedit->keys); pop = tree_node_pop(&node); - tree_node_free_recursive(pop); + talloc_free(pop); node = parent->child_head; if (node == NULL) { node = tree_node_first(parent); @@ -530,6 +531,24 @@ static bool find_substring_nocase(const char *haystack, const char *needle) static void handle_main_input(struct regedit *regedit, int c) { switch (c) { + case 18: { /* CTRL-R */ + struct tree_node *root, *node; + const char **path; + + node = tree_view_get_current_node(regedit->keys); + path = tree_node_get_path(regedit, node); + + root = load_hives(regedit); + tree_view_set_root(regedit->keys, root); + tree_view_set_path(regedit->keys, path); + node = tree_view_get_current_node(regedit->keys); + value_list_load(regedit->vl, node->key); + tree_view_show(regedit->keys); + value_list_show(regedit->vl); + print_path(regedit, node); + talloc_free(discard_const(path)); + break; + } case 'f': case 'F': case '/': { @@ -618,6 +637,7 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) SMB_ASSERT(regedit != NULL); regedit_main = regedit; + regedit->registry_context = ctx; regedit->main_window = stdscr; keypad(regedit->main_window, TRUE); @@ -627,7 +647,7 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) wprintw(regedit->path_label, "/"); show_path(regedit_main); - root = load_hives(regedit, ctx); + root = load_hives(regedit); SMB_ASSERT(root != NULL); regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH, diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 3af3c8b..2f44613 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -272,6 +272,44 @@ void tree_view_clear(struct tree_view *view) multilist_set_data(view->list, NULL); } +WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root) +{ + multilist_set_data(view->list, NULL); + talloc_free(view->root); + view->root = root; + return tree_view_update(view, root); +} + +WERROR tree_view_set_path(struct tree_view *view, const char **path) +{ + struct tree_node *top, *node; + WERROR rv; + + top = view->root; + while (*path) { + for (node = top; node != NULL; node = node->next) { + if (strcmp(*path, node->name) == 0) { + if (path[1] && tree_node_has_children(node)) { + rv = tree_node_load_children(node); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + SMB_ASSERT(node->child_head); + top = node->child_head; + break; + } else { + tree_view_update(view, top); + tree_view_set_current_node(view, node); + return WERR_OK; + } + } + } + ++path; + } + + return WERR_OK; +} + WERROR tree_view_update(struct tree_view *view, struct tree_node *list) { WERROR rv; @@ -476,20 +514,38 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, tree_view_show(view); } -static void print_path_recursive(WINDOW *label, struct tree_node *node, - size_t *len) +const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node) { - if (node->parent) - print_path_recursive(label, node->parent, len); + const char **array; + size_t nitems, index; + struct tree_node *p; - wprintw(label, "%s/", node->name); - *len += 1 + strlen(node->name); + for (nitems = 0, p = node; p != NULL; p = p->parent) { + ++nitems; + } + + array = talloc_zero_array(ctx, const char *, nitems + 1); + if (array == NULL) { + return NULL; + } + + for (index = nitems - 1, p = node; p != NULL; p = p->parent, --index) { + array[index] = talloc_strdup(array, p->name); + if (array[index] == NULL) { + talloc_free(discard_const(array)); + return NULL; + } + } + + return array; } /* print the path of node to label */ size_t tree_node_print_path(WINDOW *label, struct tree_node *node) { size_t len = 1; + const char **path; + TALLOC_CTX *frame; if (node == NULL) return 0; @@ -497,8 +553,19 @@ size_t tree_node_print_path(WINDOW *label, struct tree_node *node) werase(label); wprintw(label, "/"); - if (node->parent) - print_path_recursive(label, node->parent, &len); + if (node->parent == NULL) + return 0; + + frame = talloc_stackframe(); + path = tree_node_get_path(frame, node->parent); + + while (*path) { + len += strlen(*path) + 1; + wprintw(label, "%s/", *path); + ++path; + } + + talloc_free(frame); return len; } diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 954bae5..29c5fe2 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -55,8 +55,8 @@ 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); void tree_node_append_last(struct tree_node *list, struct tree_node *node); -void tree_node_free_recursive(struct tree_node *list); 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); struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, int nlines, int ncols, int begin_y, int begin_x); @@ -65,6 +65,8 @@ void tree_view_resize(struct tree_view *view, int nlines, int ncols, int begin_y, int begin_x); void tree_view_show(struct tree_view *view); void tree_view_clear(struct tree_view *view); +WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root); +WERROR tree_view_set_path(struct tree_view *view, const char **path); WERROR tree_view_update(struct tree_view *view, struct tree_node *list); WERROR tree_node_reopen_key(struct tree_node *node); bool tree_node_has_children(struct tree_node *node); -- 2.1.1 From f5493178cae0d68195299cb257b9ec79588fdd1a Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 30 Jun 2014 23:19:53 -0700 Subject: [PATCH 22/45] regedit: make all hives descend from a root node This helps simplify cleanup, since each node's talloc context is the parent node, and freeing the root node will destroy the entire tree without any extra utility functions. It also wouldn't be hard to extend this later on to support browsing multiple registries at the same time. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit f9a2f2eb7cc3c69f20ab615dd98b750414672fe3) --- source3/utils/regedit.c | 60 ++++----------------------- source3/utils/regedit_treeview.c | 90 +++++++++++++++++++++++++++++----------- source3/utils/regedit_treeview.h | 6 +++ 3 files changed, 79 insertions(+), 77 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index c0dbf90..8fca96b 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -86,53 +86,6 @@ static void print_path(struct regedit *regedit, struct tree_node *node) show_path(regedit); } -/* load all available hives */ -static struct tree_node *load_hives(struct regedit *regedit) -{ - const char *hives[] = { - "HKEY_CLASSES_ROOT", - "HKEY_CURRENT_USER", - "HKEY_LOCAL_MACHINE", - "HKEY_PERFORMANCE_DATA", - "HKEY_USERS", - "HKEY_CURRENT_CONFIG", - "HKEY_DYN_DATA", - "HKEY_PERFORMANCE_TEXT", - "HKEY_PERFORMANCE_NLSTEXT", - NULL - }; - struct tree_node *root, *prev, *node; - struct registry_key *key; - WERROR rv; - size_t i; - - root = NULL; - prev = NULL; - - for (i = 0; hives[i] != NULL; ++i) { - rv = reg_get_predefined_key_by_name(regedit->registry_context, - hives[i], &key); - if (!W_ERROR_IS_OK(rv)) { - continue; - } - - node = tree_node_new(regedit, NULL, hives[i], key); - if (node == NULL) { - return NULL; - } - - if (root == NULL) { - root = node; - } - if (prev) { - tree_node_append(prev, node); - } - prev = node; - } - - return root; -} - static void print_help(struct regedit *regedit) { const char *khelp = "[n] New Key [s] New Subkey [d] Del Key " @@ -196,7 +149,7 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, const char *name; const char *msg; - if (!subkey && !node->parent) { + if (!subkey && tree_node_is_top_level(node)) { return; } @@ -387,7 +340,7 @@ static void handle_tree_input(struct regedit *regedit, int c) break; case KEY_LEFT: node = tree_view_get_current_node(regedit->keys); - if (node && node->parent) { + if (node && !tree_node_is_top_level(node)) { print_path(regedit, node->parent); node = node->parent; tree_view_update(regedit->keys, tree_node_first(node)); @@ -410,7 +363,7 @@ static void handle_tree_input(struct regedit *regedit, int c) int sel; node = tree_view_get_current_node(regedit->keys); - if (!node->parent) { + if (tree_node_is_top_level(node)) { break; } sel = dialog_notice(regedit, DIA_CONFIRM, @@ -537,8 +490,11 @@ static void handle_main_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); path = tree_node_get_path(regedit, node); + SMB_ASSERT(path != NULL); + + root = tree_node_new_root(regedit, regedit->registry_context); + SMB_ASSERT(root != NULL); - root = load_hives(regedit); tree_view_set_root(regedit->keys, root); tree_view_set_path(regedit->keys, path); node = tree_view_get_current_node(regedit->keys); @@ -647,7 +603,7 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) wprintw(regedit->path_label, "/"); show_path(regedit_main); - root = load_hives(regedit); + root = tree_node_new_root(regedit, ctx); SMB_ASSERT(root != NULL); regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH, diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 2f44613..dd4b5cb 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -23,6 +23,12 @@ #define HEADING_X 3 +static int tree_node_free(struct tree_node *node) +{ + DEBUG(9, ("tree_node_free('%s', %p)\n", node->name, node)); + return 0; +} + struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, const char *name, struct registry_key *key) { @@ -32,6 +38,8 @@ struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, if (!node) { return NULL; } + talloc_set_destructor(node, tree_node_free); + DEBUG(9, ("tree_node_new('%s', %p)\n", name, node)); node->name = talloc_strdup(node, name); if (!node->name) { @@ -39,7 +47,9 @@ struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, return NULL; } - node->key = talloc_steal(node, key); + if (key) { + node->key = talloc_steal(node, key); + } if (parent) { /* Check if this node is the first descendant of parent. */ @@ -52,6 +62,52 @@ struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, return node; } +/* prepare a root node with all available hives as children */ +struct tree_node *tree_node_new_root(TALLOC_CTX *ctx, + struct registry_context *regctx) +{ + const char *hives[] = { + "HKEY_CLASSES_ROOT", + "HKEY_CURRENT_USER", + "HKEY_LOCAL_MACHINE", + "HKEY_PERFORMANCE_DATA", + "HKEY_USERS", + "HKEY_CURRENT_CONFIG", + "HKEY_DYN_DATA", + "HKEY_PERFORMANCE_TEXT", + "HKEY_PERFORMANCE_NLSTEXT", + NULL + }; + struct tree_node *root, *prev, *node; + struct registry_key *key; + WERROR rv; + size_t i; + + root = tree_node_new(ctx, NULL, "ROOT", NULL); + if (root == NULL) { + return NULL; + } + prev = NULL; + + for (i = 0; hives[i] != NULL; ++i) { + rv = reg_get_predefined_key_by_name(regctx, hives[i], &key); + if (!W_ERROR_IS_OK(rv)) { + continue; + } + + node = tree_node_new(root, root, hives[i], key); + if (node == NULL) { + return NULL; + } + if (prev) { + tree_node_append(prev, node); + } + prev = node; + } + + return root; +} + void tree_node_append(struct tree_node *left, struct tree_node *right) { if (left->next) { @@ -250,23 +306,6 @@ finish: return rv; } -void tree_node_free_recursive(struct tree_node *list) -{ - struct tree_node *node; - - if (list == NULL) { - return; - } - - while ((node = tree_node_pop(&list)) != NULL) { - if (node->child_head) { - tree_node_free_recursive(node->child_head); - } - node->child_head = NULL; - talloc_free(node); - } -} - void tree_view_clear(struct tree_view *view) { multilist_set_data(view->list, NULL); @@ -277,7 +316,7 @@ WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root) multilist_set_data(view->list, NULL); talloc_free(view->root); view->root = root; - return tree_view_update(view, root); + return tree_view_update(view, root->child_head); } WERROR tree_view_set_path(struct tree_view *view, const char **path) @@ -285,7 +324,7 @@ WERROR tree_view_set_path(struct tree_view *view, const char **path) struct tree_node *top, *node; WERROR rv; - top = view->root; + top = view->root->child_head; while (*path) { for (node = top; node != NULL; node = node->next) { if (strcmp(*path, node->name) == 0) { @@ -377,7 +416,6 @@ static int tree_view_free(struct tree_view *view) if (view->window) { delwin(view->window); } - tree_node_free_recursive(view->root); return 0; } @@ -479,7 +517,7 @@ struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, if (view->list == NULL) { goto fail; } - tree_view_update(view, root); + tree_view_update(view, root->child_head); return view; @@ -520,7 +558,7 @@ const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node) size_t nitems, index; struct tree_node *p; - for (nitems = 0, p = node; p != NULL; p = p->parent) { + for (nitems = 0, p = node; !tree_node_is_root(p); p = p->parent) { ++nitems; } @@ -529,7 +567,9 @@ const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node) return NULL; } - for (index = nitems - 1, p = node; p != NULL; p = p->parent, --index) { + for (index = nitems - 1, p = node; + !tree_node_is_root(p); + p = p->parent, --index) { array[index] = talloc_strdup(array, p->name); if (array[index] == NULL) { talloc_free(discard_const(array)); @@ -553,7 +593,7 @@ size_t tree_node_print_path(WINDOW *label, struct tree_node *node) werase(label); wprintw(label, "/"); - if (node->parent == NULL) + if (tree_node_is_top_level(node)) return 0; frame = talloc_stackframe(); diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 29c5fe2..4d1851a 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -48,8 +48,14 @@ struct tree_view { struct multilist *list; }; +struct registry_context; + struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, const char *name, struct registry_key *key); +struct tree_node *tree_node_new_root(TALLOC_CTX *ctx, + struct registry_context *regctx); +#define tree_node_is_root(node) ((node)->key == NULL) +#define tree_node_is_top_level(node) tree_node_is_root((node)->parent) 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); -- 2.1.1 From 870d1ed5dc064ef23df50ad36588f15cb0088905 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Tue, 1 Jul 2014 16:00:16 -0700 Subject: [PATCH 23/45] regedit: add a panic handler to restore terminal Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit fec275a4a8e9ecd77fb30c4a242a2fe832d9f41c) --- source3/utils/regedit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 8fca96b..5b7885c 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -567,6 +567,12 @@ int regedit_getch(void) return c; } +static void regedit_panic_handler(const char *msg) +{ + endwin(); + smb_panic_s3(msg); +} + static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) { struct regedit *regedit; @@ -579,6 +585,8 @@ static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) cbreak(); noecho(); + fault_configure(regedit_panic_handler); + colors = has_colors(); if (colors) { start_color(); -- 2.1.1 From b456047d21919bfcbd19796107efdc7eae6c32eb Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 7 Jul 2014 21:30:49 -0700 Subject: [PATCH 24/45] regedit: simplify cleanup after loading children Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit c94c765f01dc145ee7f1abfe1379ed9c923f3d43) --- source3/utils/regedit_treeview.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index dd4b5cb..47d9282 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -274,7 +274,7 @@ WERROR tree_node_load_children(struct tree_node *node) continue; } - array[count] = tree_node_new(node, node, key_name, key); + array[count] = tree_node_new(array, node, key_name, key); if (array[count] == NULL) { rv = WERR_NOMEM; goto finish; @@ -286,21 +286,16 @@ WERROR tree_node_load_children(struct tree_node *node) TYPESAFE_QSORT(array, count, node_cmp); for (i = 1, prev = array[0]; i < count; ++i) { + talloc_steal(node, array[i]); tree_node_append(prev, array[i]); prev = array[i]; } - node->child_head = array[0]; + node->child_head = talloc_steal(node, array[0]); rv = WERR_OK; } finish: - if (!W_ERROR_IS_OK(rv)) { - for (i = 0; i < nsubkeys; ++i) { - talloc_free(array[i]); - } - node->child_head = NULL; - } talloc_free(array); return rv; -- 2.1.1 From a8f3c0407e4cc3f074a52a673e94cdefb889a886 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 18 Jul 2014 13:35:14 -0700 Subject: [PATCH 25/45] regedit: add padding to fit REG_MULTI_SZ to the text field This fixes a bug loading REG_MULTI_SZ values into the editor dialog, since ncurses fields don't handle newline characters. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 42df2e643f68f5ec19e6a41eade29199a3266035) --- source3/utils/regedit_dialog.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 944fcc8..ce67065 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -642,22 +642,35 @@ static WERROR fill_value_buffer(struct edit_dialog *edit, break; } case REG_MULTI_SZ: { - const char **p, **a; + int rows, cols, max; + size_t padding, length, index; + const char **array, **arrayp; char *buf = NULL; - if (!pull_reg_multi_sz(edit, &vitem->data, &a)) { + dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max); + if (!pull_reg_multi_sz(edit, &vitem->data, &array)) { return WERR_NOMEM; } - for (p = a; *p != NULL; ++p) { - if (buf == NULL) { - buf = talloc_asprintf(edit, "%s\n", *p); - } else { - buf = talloc_asprintf_append(buf, "%s\n", *p); - } + + /* try to fit each string on it's own line. each line + needs to be padded with whitespace manually, since + ncurses fields do not have newlines. */ + for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) { + length = MIN(strlen(*arrayp), cols); + padding = cols - length; + buf = talloc_realloc(edit, buf, char, + talloc_array_length(buf) + + length + padding + 1); if (buf == NULL) { return WERR_NOMEM; } + memcpy(&buf[index], *arrayp, length); + index += length; + memset(&buf[index], ' ', padding); + index += padding; + buf[index] = '\0'; } + set_field_buffer(edit->field[FLD_DATA], 0, buf); talloc_free(buf); } -- 2.1.1 From 6bdd7c00076f68cc972836ef169a31be24cf9661 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 25 Jul 2014 23:16:52 -0700 Subject: [PATCH 26/45] regedit: improvements for hexedit This mainly enables setting the hexedit buffer after the editor is allocated. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 30ae8f1014122258ff3b9415370fc679675929de) --- source3/utils/regedit_hexedit.c | 82 +++++++++++++++++++++++++++++++++++------ source3/utils/regedit_hexedit.h | 30 ++------------- 2 files changed, 74 insertions(+), 38 deletions(-) diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c index 8f1c2c7..847c5d8 100644 --- a/source3/utils/regedit_hexedit.c +++ b/source3/utils/regedit_hexedit.c @@ -20,6 +20,33 @@ #include "includes.h" #include "regedit_hexedit.h" +/* + offset hex1 hex2 ascii + 00000000 FF FF FF FF FF FF FF FF ........ +*/ + +#define HEX_COL1 10 +#define HEX_COL1_END 21 +#define HEX_COL2 23 +#define HEX_COL2_END 34 +#define ASCII_COL 36 +#define ASCII_COL_END LINE_WIDTH +#define BYTES_PER_LINE 8 + +struct hexedit { + size_t offset; + size_t len; + size_t alloc_size; + int cursor_y; + int cursor_x; + size_t cursor_offset; + size_t cursor_line_offset; + int nibble; + uint8_t *data; + WINDOW *win; + WINDOW *status_line; +}; + static int max_rows(WINDOW *win) { int maxy, maxx; @@ -44,6 +71,7 @@ static int hexedit_free(struct hexedit *buf) struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, int y, int x, const void *data, size_t sz) { + WERROR rv; struct hexedit *buf; buf = talloc_zero(ctx, struct hexedit); @@ -53,22 +81,10 @@ struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, talloc_set_destructor(buf, hexedit_free); - buf->data = talloc_zero_array(buf, uint8_t, sz); - if (buf->data == NULL) { - goto fail; - } - - if (data) { - memcpy(buf->data, data, sz); - } - - buf->len = sz; - buf->alloc_size = sz; buf->win = derwin(parent, nlines, LINE_WIDTH, y, x); if (buf->win == NULL) { goto fail; } - buf->cursor_x = HEX_COL1; buf->status_line = derwin(buf->win, 1, LINE_WIDTH, max_rows(buf->win), 0); @@ -77,6 +93,11 @@ struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, } wattron(buf->status_line, A_REVERSE | A_STANDOUT); + rv = hexedit_set_buf(buf, data, sz); + if (!W_ERROR_IS_OK(rv)) { + goto fail; + } + return buf; fail: @@ -85,6 +106,40 @@ fail: return NULL; } +WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz) +{ + TALLOC_FREE(buf->data); + + buf->data = talloc_zero_array(buf, uint8_t, sz); + if (buf->data == NULL) { + return WERR_NOMEM; + } + + if (data != NULL) { + memcpy(buf->data, data, sz); + } + + buf->len = sz; + buf->alloc_size = sz; + buf->cursor_x = HEX_COL1; + buf->cursor_y = 0; + buf->cursor_offset = 0; + buf->cursor_line_offset = 0; + buf->nibble = 0; + + return WERR_OK; +} + +const void *hexedit_get_buf(struct hexedit *buf) +{ + return buf->data; +} + +size_t hexedit_get_buf_len(struct hexedit *buf) +{ + return buf->len; +} + static size_t bytes_per_screen(WINDOW *win) { return max_rows(win) * BYTES_PER_LINE; @@ -103,6 +158,7 @@ void hexedit_set_cursor(struct hexedit *buf) wcursyncup(buf->win); wsyncup(buf->win); untouchwin(buf->win); + wnoutrefresh(buf->status_line); } void hexedit_refresh(struct hexedit *buf) @@ -417,6 +473,8 @@ void hexedit_driver(struct hexedit *buf, int c) do_edit(buf, c & 0xff); break; } + + hexedit_set_cursor(buf); } WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz) diff --git a/source3/utils/regedit_hexedit.h b/source3/utils/regedit_hexedit.h index f0424cd..af2432a 100644 --- a/source3/utils/regedit_hexedit.h +++ b/source3/utils/regedit_hexedit.h @@ -31,36 +31,14 @@ enum { HE_CURSOR_PGDN = 0x1500 }; -/* - offset hex1 hex2 ascii - 00000000 FF FF FF FF FF FF FF FF ........ -*/ - #define LINE_WIDTH 44 -#define HEX_COL1 10 -#define HEX_COL1_END 21 -#define HEX_COL2 23 -#define HEX_COL2_END 34 -#define ASCII_COL 36 -#define ASCII_COL_END LINE_WIDTH -#define BYTES_PER_LINE 8 - -struct hexedit { - size_t offset; - size_t len; - size_t alloc_size; - int cursor_y; - int cursor_x; - size_t cursor_offset; - size_t cursor_line_offset; - int nibble; - uint8_t *data; - WINDOW *win; - WINDOW *status_line; -}; +struct hexedit; struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, int y, int x, const void *data, size_t sz); +WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz); +const void *hexedit_get_buf(struct hexedit *buf); +size_t hexedit_get_buf_len(struct hexedit *buf); void hexedit_set_cursor(struct hexedit *buf); void hexedit_refresh(struct hexedit *buf); void hexedit_driver(struct hexedit *buf, int c); -- 2.1.1 From 6237284cc06fb65fb274d047b29907d5ece4a9e3 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 25 Jul 2014 23:26:27 -0700 Subject: [PATCH 27/45] regedit: Introduce a new API to build the dialogs. This helps make new dialogs easier to create, because it provides some common building blocks and a consistent way to deal with user input. Dialogs are divided into sections that stack vertically, and common sections for typical UI things like text boxes, option lists, and buttons are provided. The old dialogs are rewritten to use this API. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit a728c391d7d4f6474e37f7118ba6df14dc9da6fd) --- source3/utils/regedit_dialog.c | 2544 ++++++++++++++++++++++++++-------------- source3/utils/regedit_dialog.h | 186 ++- 2 files changed, 1838 insertions(+), 892 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index ce67065..119219d 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -55,31 +55,19 @@ static char *string_trim(TALLOC_CTX *ctx, const char *buf) static int dialog_free(struct dialog *dia) { - if (dia->window) { - delwin(dia->window); - } - if (dia->sub_window) { - delwin(dia->sub_window); - } - if (dia->panel) { - del_panel(dia->panel); - } - if (dia->choices) { - unpost_menu(dia->choices); - free_menu(dia->choices); - } - if (dia->choice_items) { - ITEM **it; - for (it = dia->choice_items; *it != NULL; ++it) { - free_item(*it); - } - } + dialog_destroy(dia); return 0; } -struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, - int ncols, int y, int x) +static bool default_validator(struct dialog *dia, struct dialog_section *sect, + void *arg) +{ + return true; +} + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title, + int y, int x) { struct dialog *dia; @@ -90,24 +78,14 @@ struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, talloc_set_destructor(dia, dialog_free); - dia->window = newwin(nlines, ncols, y, x); - if (dia->window == NULL) { - goto fail; - } - - box(dia->window, 0, 0); - mvwaddstr(dia->window, 0, 1, title); - - /* body of the dialog within the box outline */ - dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1); - if (dia->sub_window == NULL) { - goto fail; - } - - dia->panel = new_panel(dia->window); - if (dia->panel == NULL) { + dia->title = talloc_strdup(dia, title); + if (dia->title == NULL) { goto fail; } + dia->x = x; + dia->y = y; + dia->color = color; + dia->submit = default_validator; return dia; @@ -118,8 +96,13 @@ fail: } -static void center_dialog_above_window(int *nlines, int *ncols, - int *y, int *x) +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg) +{ + dia->submit = cb; + dia->submit_arg = arg; +} + +static void center_above_window(int *nlines, int *ncols, int *y, int *x) { int centery, centerx; @@ -143,491 +126,1609 @@ static void center_dialog_above_window(int *nlines, int *ncols, } } -static int dialog_getch(struct dialog *dia) +void dialog_section_destroy(struct dialog_section *section) { - int c; + if (section->ops->destroy) { + section->ops->destroy(section); + } + if (section->window) { + delwin(section->window); + section->window = NULL; + } +} - c = regedit_getch(); +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols) +{ + section->ops = ops; + section->nlines = nlines; + section->ncols = ncols; +} - if (c == KEY_RESIZE) { - int nlines, ncols, y, x; +const char *dialog_section_get_name(struct dialog_section *section) +{ + return section->name; +} - getmaxyx(dia->window, nlines, ncols); - getbegyx(dia->window, y, x); - if (dia->centered) { - center_dialog_above_window(&nlines, &ncols, &y, &x); - } else { - if (nlines + y > LINES) { - if (nlines > LINES) { - y = 0; - } else { - y = LINES - nlines; - } - } - if (ncols + x > COLS) { - if (ncols > COLS) { - x = 0; - } else { - x = COLS - ncols; - } - } +void dialog_section_set_name(struct dialog_section *section, const char *name) +{ + TALLOC_FREE(section->name); + section->name = talloc_strdup(section, name); +} + +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify) +{ + section->justify = justify; +} + +/* append a section to the dialog's circular list */ +void dialog_append_section(struct dialog *dia, + struct dialog_section *section) +{ + SMB_ASSERT(section != NULL); + + if (!dia->head_section) { + dia->head_section = section; + } + if (dia->tail_section) { + dia->tail_section->next = section; + } + section->prev = dia->tail_section; + section->next = dia->head_section; + dia->head_section->prev = section; + dia->tail_section = section; +} + +struct dialog_section *dialog_find_section(struct dialog *dia, const char *name) +{ + struct dialog_section *section = dia->head_section; + + do { + if (section->name && strequal(section->name, name)) { + return section; } - move_panel(dia->panel, y, x); - doupdate(); + section = section->next; + } while (section != dia->head_section); + + return NULL; +} + +static void section_on_input(struct dialog *dia, int c) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_input) { + return; } + section->ops->on_input(dia, section, c); +} - return c; +static bool section_on_tab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_tab) { + return false; + } + return section->ops->on_tab(dia, section); } -struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, - int nlines, int ncols) +static bool section_on_btab(struct dialog *dia) { - struct dialog *dia; - int y, x; + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_btab) { + return false; + } + return section->ops->on_btab(dia, section); +} - center_dialog_above_window(&nlines, &ncols, &y, &x); +static bool section_on_up(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; - dia = dialog_new(ctx, title, nlines, ncols, y, x); - if (dia) { - dia->centered = true; + if (!section || !section->ops->on_up) { + return false; } + return section->ops->on_up(dia, section); +} - return dia; +static bool section_on_down(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_down) { + return false; + } + return section->ops->on_down(dia, section); } -struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols, int y, int x) +static bool section_on_left(struct dialog *dia) { - size_t nchoices, i; - struct dialog *dia; + struct dialog_section *section = dia->current_section; - dia = dialog_new(ctx, title, nlines, ncols, y, x); - if (dia == NULL) { - return NULL; + if (!section || !section->ops->on_left) { + return false; } + return section->ops->on_left(dia, section); +} - dia->menu_window = derwin(dia->sub_window, 1, ncols - 3, - nlines - 3, 0); - if (dia->menu_window == NULL) { - goto fail; +static bool section_on_right(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_right) { + return false; } + return section->ops->on_right(dia, section); +} - for (nchoices = 0; choices[nchoices] != NULL; ++nchoices) - ; - dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1); - if (dia->choice_items == NULL) { - goto fail; +static enum dialog_action section_on_enter(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_enter) { + return DIALOG_OK; } - for (i = 0; i < nchoices; ++i) { - char *desc = talloc_strdup(dia, choices[i]); - if (desc == NULL) { - goto fail; - } - dia->choice_items[i] = new_item(desc, desc); - if (dia->choice_items[i] == NULL) { - goto fail; - } - /* store choice index */ - set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i); + return section->ops->on_enter(dia, section); +} + +static bool section_on_focus(struct dialog *dia, bool forward) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_focus) { + return false; } + return section->ops->on_focus(dia, section, forward); +} - dia->choices = new_menu(dia->choice_items); - if (dia->choices == NULL) { - goto fail; +static void section_on_leave_focus(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (section->ops->on_leave_focus) { + section->ops->on_leave_focus(dia, section); } +} - set_menu_format(dia->choices, 1, ncols); - set_menu_win(dia->choices, dia->sub_window); - set_menu_sub(dia->choices, dia->menu_window); - menu_opts_off(dia->choices, O_SHOWDESC); - set_menu_mark(dia->choices, "* "); - post_menu(dia->choices); - wmove(dia->sub_window, 0, 0); +static void section_set_next_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); - return dia; + do { + dia->current_section = dia->current_section->next; + } while (!section_on_focus(dia, true)); +} -fail: - talloc_free(dia); +static void section_set_previous_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); - return NULL; + do { + dia->current_section = dia->current_section->prev; + } while (!section_on_focus(dia, false)); } -struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols) +WERROR dialog_create(struct dialog *dia) { - int y, x; - struct dialog *dia; - center_dialog_above_window(&nlines, &ncols, &y, &x); + WERROR rv = WERR_OK; + int row, col; + int nlines, ncols; + struct dialog_section *section; - dia = dialog_choice_new(ctx, title, choices, nlines, ncols, y, x); - if (dia) { + nlines = 0; + ncols = 0; + SMB_ASSERT(dia->head_section != NULL); + + /* calculate total size based on sections */ + section = dia->head_section; + do { + nlines += section->nlines; + ncols = MAX(ncols, section->ncols); + section = section->next; + } while (section != dia->head_section); + + /* fill in widths for sections that expand */ + section = dia->head_section; + do { + if (section->ncols < 0) { + section->ncols = ncols; + } + section = section->next; + } while (section != dia->head_section); + + /* create window for dialog */ + nlines += 4; + ncols += 6; + dia->centered = false; + if (dia->y < 0 || dia->x < 0) { dia->centered = true; + center_above_window(&nlines, &ncols, &dia->y, &dia->x); + } + dia->window = newwin(nlines, ncols, dia->y, dia->x); + if (dia->window == NULL) { + rv = WERR_NOMEM; + goto fail; + } + dia->panel = new_panel(dia->window); + if (dia->panel == NULL) { + rv = WERR_NOMEM; + goto fail; } - return dia; + /* setup color and border */ + wbkgdset(dia->window, ' ' | COLOR_PAIR(dia->color)); + wclear(dia->window); + mvwhline(dia->window, 1, 2, 0, ncols - 4); + mvwhline(dia->window, nlines - 2, 2, 0, ncols - 4); + mvwvline(dia->window, 2, 1, 0, nlines - 4); + mvwvline(dia->window, 2, ncols - 2, 0, nlines - 4); + mvwaddch(dia->window, 1, 1, ACS_ULCORNER); + mvwaddch(dia->window, 1, ncols - 2, ACS_URCORNER); + mvwaddch(dia->window, nlines - 2, 1, ACS_LLCORNER); + mvwaddch(dia->window, nlines - 2, ncols - 2, ACS_LRCORNER); + col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2; + mvwprintw(dia->window, 1, col, " %s ", dia->title); + + /* create subwindows for each section */ + row = 2; + section = dia->head_section; + do { + col = 3; + + switch (section->justify) { + case SECTION_JUSTIFY_LEFT: + break; + case SECTION_JUSTIFY_CENTER: + col += (ncols - 6)/ 2 - section->ncols / 2; + break; + case SECTION_JUSTIFY_RIGHT: + break; + } + + section->window = derwin(dia->window, section->nlines, + section->ncols, row, col); + if (section->window == NULL) { + rv = WERR_NOMEM; + goto fail; + } + SMB_ASSERT(section->ops->create != NULL); + rv = section->ops->create(dia, section); + row += section->nlines; + section = section->next; + } while (section != dia->head_section && W_ERROR_IS_OK(rv)); + + dia->current_section = dia->head_section; + section_set_next_focus(dia); + +fail: + return rv; } -static bool current_item_is_first(MENU *menu) +void dialog_show(struct dialog *dia) { - const ITEM *it = current_item(menu); + struct dialog_section *section; - return item_index(it) == 0; + touchwin(dia->window); + wnoutrefresh(dia->window); + section = dia->head_section; + do { + wnoutrefresh(section->window); + section = section->next; + } while (section != dia->head_section); } -static bool current_item_is_last(MENU *menu) +void dialog_destroy(struct dialog *dia) { - const ITEM *it = current_item(menu); + struct dialog_section *section; - return item_index(it) == item_count(menu) - 1; + section = dia->head_section; + do { + dialog_section_destroy(section); + section = section->next; + } while (section != dia->head_section); + + if (dia->panel) { + del_panel(dia->panel); + dia->panel = NULL; + } + if (dia->window) { + delwin(dia->window); + dia->window = NULL; + } } -static int handle_menu_input(MENU *menu, int c) +static int dialog_getch(struct dialog *dia) { - ITEM *item; + int c; - switch (c) { - case KEY_BTAB: - if (current_item_is_first(menu)) { - menu_driver(menu, REQ_LAST_ITEM); - } else { - menu_driver(menu, REQ_LEFT_ITEM); - } - break; - case KEY_LEFT: - menu_driver(menu, REQ_LEFT_ITEM); - break; - case '\t': - if (current_item_is_last(menu)) { - menu_driver(menu, REQ_FIRST_ITEM); - break; + c = regedit_getch(); + if (c == KEY_RESIZE) { + int nlines, ncols, y, x; + + getmaxyx(dia->window, nlines, ncols); + getbegyx(dia->window, y, x); + if (dia->centered) { + center_above_window(&nlines, &ncols, &y, &x); } else { - menu_driver(menu, REQ_RIGHT_ITEM); + if (nlines + y > LINES) { + if (nlines > LINES) { + y = 0; + } else { + y = LINES - nlines; + } + } + if (ncols + x > COLS) { + if (ncols > COLS) { + x = 0; + } else { + x = COLS - ncols; + } + } } - case KEY_RIGHT: - menu_driver(menu, REQ_RIGHT_ITEM); - break; - case KEY_ENTER: - case '\n': - item = current_item(menu); - return (int)(uintptr_t)item_userptr(item); + move_panel(dia->panel, y, x); } - return -1; + return c; } -static void handle_form_input(FORM *frm, int c) +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action) { + int c; + + *err = WERR_OK; + + c = dialog_getch(dia); + switch (c) { - case '\n': - form_driver(frm, REQ_NEW_LINE); + case '\t': + if (!section_on_tab(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_BTAB: + if (!section_on_btab(dia)) { + section_set_previous_focus(dia); + } break; case KEY_UP: - form_driver(frm, REQ_UP_CHAR); + if (!section_on_up(dia)) { + section_set_previous_focus(dia); + } break; case KEY_DOWN: - form_driver(frm, REQ_DOWN_CHAR); - break; - case '\b': - case KEY_BACKSPACE: - form_driver(frm, REQ_DEL_PREV); + if (!section_on_down(dia)) { + section_set_next_focus(dia); + } break; case KEY_LEFT: - form_driver(frm, REQ_LEFT_CHAR); + if (!section_on_left(dia)) { + section_set_previous_focus(dia); + } break; case KEY_RIGHT: - form_driver(frm, REQ_RIGHT_CHAR); + if (!section_on_right(dia)) { + section_set_next_focus(dia); + } + break; + case '\n': + case KEY_ENTER: + *action = section_on_enter(dia); + switch (*action) { + case DIALOG_IGNORE: + break; + case DIALOG_CANCEL: + return false; + case DIALOG_OK: + return !dia->submit(dia, dia->current_section, + dia->submit_arg); + } break; + case 27: /* ESC */ + return false; default: - form_driver(frm, c); + section_on_input(dia, c); break; } + + return true; } -static int modal_loop(struct dialog *dia) +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action) { - int c; - int selection = -1; - - update_panels(); - doupdate(); - - while (selection == -1) { - c = dialog_getch(dia); - selection = handle_menu_input(dia->choices, c); + do { update_panels(); doupdate(); + } while (dialog_handle_input(dia, err, action)); +} + +/* text label */ +struct dialog_section_label { + struct dialog_section section; + char **text; +}; + +static WERROR label_create(struct dialog *dia, struct dialog_section *section) +{ + int row; + struct dialog_section_label *label = + talloc_get_type_abort(section, struct dialog_section_label); + + for (row = 0; row < section->nlines; ++row) { + mvwaddstr(section->window, row, 0, label->text[row]); } - talloc_free(dia); + return WERR_OK; +} + +struct dialog_section_ops label_ops = { + .create = label_create, +}; + +static int label_free(struct dialog_section_label *label) +{ + dialog_section_destroy(&label->section); + return 0; +} + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap) +{ + struct dialog_section_label *label; + char *tmp, *ptmp, *line, *saveptr; + int nlines, ncols; + + label = talloc_zero(ctx, struct dialog_section_label); + if (label == NULL) { + return NULL; + } + talloc_set_destructor(label, label_free); + tmp = talloc_vasprintf(label, msg, ap); + if (tmp == NULL) { + goto fail; + } + + for (nlines = 0, ncols = 0, ptmp = tmp; + (line = strtok_r(ptmp, "\n", &saveptr)) != NULL; + ++nlines) { + ptmp = NULL; + label->text = talloc_realloc(label, label->text, + char *, nlines + 1); + if (label->text == NULL) { + goto fail; + } + ncols = MAX(ncols, strlen(line)); + label->text[nlines] = talloc_strdup(label->text, line); + if (label->text[nlines] == NULL) { + goto fail; + } + } + talloc_free(tmp); + dialog_section_init(&label->section, &label_ops, nlines, ncols); + + return &label->section; + +fail: + talloc_free(label); + return NULL; +} + +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...) +{ + va_list ap; + struct dialog_section *rv; + + va_start(ap, msg); + rv = dialog_section_label_new_va(ctx, msg, ap); + va_end(ap); + + return rv; +} + +/* horizontal separator */ +struct dialog_section_hsep { + struct dialog_section section; + int sep; +}; + +static WERROR hsep_create(struct dialog *dia, struct dialog_section *section) +{ + int y, x; + struct dialog_section_hsep *hsep = + talloc_get_type_abort(section, struct dialog_section_hsep); + + whline(section->window, hsep->sep, section->ncols); + + if (hsep->sep == 0 || hsep->sep == ACS_HLINE) { + /* change the border characters around this section to + tee chars */ + getparyx(section->window, y, x); + mvwaddch(dia->window, y, x - 1, ACS_HLINE); + mvwaddch(dia->window, y, x - 2, ACS_LTEE); + mvwaddch(dia->window, y, x + section->ncols, ACS_HLINE); + mvwaddch(dia->window, y, x + section->ncols + 1, ACS_RTEE); + } + + return WERR_OK; +} + +struct dialog_section_ops hsep_ops = { + .create = hsep_create +}; + +static int hsep_free(struct dialog_section_hsep *hsep) +{ + dialog_section_destroy(&hsep->section); + return 0; +} + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep) +{ + struct dialog_section_hsep *hsep; + + hsep = talloc_zero(ctx, struct dialog_section_hsep); + if (hsep) { + talloc_set_destructor(hsep, hsep_free); + dialog_section_init(&hsep->section, &hsep_ops, 1, -1); + hsep->sep = sep; + } + + return &hsep->section; +} + +/* text input field */ +struct dialog_section_text_field { + struct dialog_section section; + unsigned opts; + FIELD *field[2]; + FORM *form; +}; + +static WERROR text_field_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + text_field->field[0] = new_field(section->nlines, section->ncols, + 0, 0, 0, 0); + if (text_field->field[0] == NULL) { + return WERR_NOMEM; + } + set_field_back(text_field->field[0], A_REVERSE); + set_field_opts(text_field->field[0], text_field->opts); + + text_field->form = new_form(text_field->field); + if (text_field->form == NULL) { + return WERR_NOMEM; + } + + set_form_win(text_field->form, dia->window); + set_form_sub(text_field->form, section->window); + set_current_field(text_field->form, text_field->field[0]); + post_form(text_field->form); + + return WERR_OK; +} + +static void text_field_destroy(struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (text_field->form) { + unpost_form(text_field->form); + free_form(text_field->form); + text_field->form = NULL; + } + if (text_field->field[0]) { + free_field(text_field->field[0]); + text_field->field[0] = NULL; + } +} + +static void text_field_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + switch (c) { + case KEY_BACKSPACE: + form_driver(text_field->form, REQ_DEL_PREV); + break; + default: + form_driver(text_field->form, c); + break; + } +} + +static bool text_field_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_UP_CHAR); + return true; + } + return false; +} + +static bool text_field_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_DOWN_CHAR); + return true; + } + return false; +} + +static bool text_field_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_LEFT_CHAR); + + return true; +} + +static bool text_field_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_RIGHT_CHAR); + + return true; +} + +static enum dialog_action text_field_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_NEW_LINE); + return DIALOG_IGNORE; + } + + return DIALOG_OK; +} + +static bool text_field_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + pos_form_cursor(text_field->form); + + return true; +} + +struct dialog_section_ops text_field_ops = { + .create = text_field_create, + .destroy = text_field_destroy, + .on_input = text_field_on_input, + .on_up = text_field_on_up, + .on_down = text_field_on_down, + .on_left = text_field_on_left, + .on_right = text_field_on_right, + .on_enter = text_field_on_enter, + .on_focus = text_field_on_focus +}; + +static int text_field_free(struct dialog_section_text_field *text_field) +{ + dialog_section_destroy(&text_field->section); + return 0; +} + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width) +{ + struct dialog_section_text_field *text_field; + + text_field = talloc_zero(ctx, struct dialog_section_text_field); + if (text_field == NULL) { + return NULL; + } + talloc_set_destructor(text_field, text_field_free); + dialog_section_init(&text_field->section, &text_field_ops, + height, width); + text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK; + + return &text_field->section; +} + +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + return string_trim(ctx, field_buffer(text_field->field[0], 0)); +} + +void dialog_section_text_field_set(struct dialog_section *section, + const char *s) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + set_field_buffer(text_field->field[0], 0, s); +} + +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + int rows, cols, max; + const char **arr; + size_t i; + const char *buf; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + buf = field_buffer(text_field->field[0], 0); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + + arr = talloc_zero_array(ctx, const char *, rows + 1); + if (arr == NULL) { + return NULL; + } + for (i = 0; *buf; ++i, buf += cols) { + SMB_ASSERT(i < rows); + arr[i] = string_trim_n(arr, buf, cols); + } + + return arr; +} + +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array) +{ + int rows, cols, max; + size_t padding, length, index; + const char **arrayp; + char *buf = NULL; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + /* try to fit each string on it's own line. each line + needs to be padded with whitespace manually, since + ncurses fields do not have newlines. */ + for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) { + length = MIN(strlen(*arrayp), cols); + padding = cols - length; + buf = talloc_realloc(ctx, buf, char, + talloc_array_length(buf) + + length + padding + 1); + if (buf == NULL) { + return WERR_NOMEM; + } + memcpy(&buf[index], *arrayp, length); + index += length; + memset(&buf[index], ' ', padding); + index += padding; + buf[index] = '\0'; + } + + set_field_buffer(text_field->field[0], 0, buf); + talloc_free(buf); + + return WERR_OK; +} + +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = strtoll(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; + } + + return rv; +} + + +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = strtoull(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; + } + + return rv; +} + +/* hex editor field */ +struct dialog_section_hexedit { + struct dialog_section section; + struct hexedit *buf; +}; + +#define HEXEDIT_MIN_SIZE 16 +static WERROR hexedit_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit->buf = hexedit_new(dia, section->window, section->nlines, + 0, 0, NULL, HEXEDIT_MIN_SIZE); + if (hexedit->buf == NULL) { + return WERR_NOMEM; + } + + hexedit_refresh(hexedit->buf); + + return WERR_OK; +} + +static void hexedit_destroy(struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + if (hexedit->buf) { + TALLOC_FREE(hexedit->buf); + } +} + +static void hexedit_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + switch (c) { + case KEY_BACKSPACE: + // FIXME hexedit_driver(hexedit->buf, c); + break; + default: + hexedit_driver(hexedit->buf, c); + break; + } +} + +static bool hexedit_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_UP); + + return true; +} + +static bool hexedit_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_DOWN); + + return true; +} + +static bool hexedit_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_LEFT); + + return true; +} + +static bool hexedit_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT); + + return true; +} + +static enum dialog_action hexedit_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + return DIALOG_IGNORE; +} + +static bool hexedit_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_set_cursor(hexedit->buf); + + return true; +} + +struct dialog_section_ops hexedit_ops = { + .create = hexedit_create, + .destroy = hexedit_destroy, + .on_input = hexedit_on_input, + .on_up = hexedit_on_up, + .on_down = hexedit_on_down, + .on_left = hexedit_on_left, + .on_right = hexedit_on_right, + .on_enter = hexedit_on_enter, + .on_focus = hexedit_on_focus +}; + +static int hexedit_free(struct dialog_section_hexedit *hexedit) +{ + dialog_section_destroy(&hexedit->section); + return 0; +} + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height) +{ + struct dialog_section_hexedit *hexedit; + + hexedit = talloc_zero(ctx, struct dialog_section_hexedit); + if (hexedit == NULL) { + return NULL; + } + talloc_set_destructor(hexedit, hexedit_free); + dialog_section_init(&hexedit->section, &hexedit_ops, + height, LINE_WIDTH); + + return &hexedit->section; +} + +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + + rv = hexedit_set_buf(hexedit->buf, data, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + hexedit_set_cursor(hexedit->buf); + } + + return rv; +} + +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + *data = hexedit_get_buf(hexedit->buf); + *size = hexedit_get_buf_len(hexedit->buf); +} + +/* button box */ +struct dialog_section_buttons { + struct dialog_section section; + struct button_spec *spec; + int current_button; +}; + +static void buttons_unhighlight(struct dialog_section_buttons *buttons) +{ + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + wnoutrefresh(buttons->section.window); +} + +static void buttons_highlight(struct dialog_section_buttons *buttons) +{ + struct button_spec *button = &buttons->spec[buttons->current_button]; + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + mvwchgat(buttons->section.window, 0, button->col, + strlen(button->label), A_REVERSE, pair, NULL); + wmove(buttons->section.window, 0, button->col + 2); + wcursyncup(buttons->section.window); + wnoutrefresh(buttons->section.window); +} + +static bool buttons_highlight_next(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button < talloc_array_length(buttons->spec) - 1) { + buttons->current_button++; + buttons_highlight(buttons); + return true; + } + return false; +} + +static bool buttons_highlight_previous(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button > 0) { + buttons->current_button--; + buttons_highlight(buttons); + return true; + } + return false; +} + +static WERROR buttons_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, nbuttons; + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + nbuttons = talloc_array_length(buttons->spec); + for (i = 0; i < nbuttons; ++i) { + struct button_spec *button = &buttons->spec[i]; + mvwaddstr(section->window, 0, button->col, button->label); + } + + buttons->current_button = 0; + + return WERR_OK; +} + +static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_previous(buttons); +} + +static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_next(buttons); +} + +static enum dialog_action buttons_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + struct button_spec *spec = &buttons->spec[buttons->current_button]; + + if (spec->on_enter) { + return spec->on_enter(dia, section); + } + + return spec->action; +} + +static bool buttons_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + if (forward) { + buttons->current_button = 0; + } else { + buttons->current_button = talloc_array_length(buttons->spec) - 1; + } + buttons_highlight(buttons); + + return true; +} + +static void buttons_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + buttons_unhighlight(buttons); +} + +struct dialog_section_ops buttons_ops = { + .create = buttons_create, + .on_tab = buttons_on_tab, + .on_btab = buttons_on_btab, + .on_up = buttons_on_btab, + .on_down = buttons_on_tab, + .on_left = buttons_on_btab, + .on_right = buttons_on_tab, + .on_enter = buttons_on_enter, + .on_focus = buttons_on_focus, + .on_leave_focus = buttons_on_leave_focus +}; + +static int buttons_free(struct dialog_section_buttons *buttons) +{ + dialog_section_destroy(&buttons->section); + return 0; +} + +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec) +{ + struct dialog_section_buttons *buttons; + size_t i, nbuttons; + int width; + + buttons = talloc_zero(ctx, struct dialog_section_buttons); + if (buttons == NULL) { + return NULL; + } + talloc_set_destructor(buttons, buttons_free); + + for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) { + } + buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons); + if (buttons->spec == NULL) { + goto fail; + } + + for (width = 0, i = 0; i < nbuttons; ++i) { + buttons->spec[i] = spec[i]; + buttons->spec[i].label = talloc_asprintf(buttons->spec, + "[ %s ]", + spec[i].label); + if (!buttons->spec[i].label) { + goto fail; + } + + buttons->spec[i].col = width; + width += strlen(buttons->spec[i].label); + if (i != nbuttons - 1) { + ++width; + } + } + + dialog_section_init(&buttons->section, &buttons_ops, 1, width); + + return &buttons->section; + +fail: + talloc_free(buttons); + return NULL; +} + +/* options */ +struct dialog_section_options { + struct dialog_section section; + struct option_spec *spec; + int current_option; + bool single_select; +}; + +static void options_unhighlight(struct dialog_section_options *options) +{ + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + wnoutrefresh(options->section.window); +} + +static void options_highlight(struct dialog_section_options *options) +{ + struct option_spec *option = &options->spec[options->current_option]; + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + mvwchgat(options->section.window, option->row, option->col, + strlen(option->label), A_REVERSE, pair, NULL); + wmove(options->section.window, option->row, option->col + 4); + wcursyncup(options->section.window); + wnoutrefresh(options->section.window); +} + +static void options_render_state(struct dialog_section_options *options) +{ + size_t i, noptions; + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *option = &options->spec[i]; + char c = ' '; + if (*option->state) + c = 'x'; + mvwaddch(options->section.window, + option->row, option->col + 1, c); + wnoutrefresh(options->section.window); + } +} + +static bool options_highlight_next(struct dialog_section_options *options) +{ + if (options->current_option < talloc_array_length(options->spec) - 1) { + options->current_option++; + options_highlight(options); + return true; + } + return false; +} + +static bool options_highlight_previous(struct dialog_section_options *options) +{ + if (options->current_option > 0) { + options->current_option--; + options_highlight(options); + return true; + } + return false; +} + +static WERROR options_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, noptions; + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *option = &options->spec[i]; + mvwaddstr(section->window, option->row, option->col, + option->label); + } + + options->current_option = 0; + options_render_state(options); + + return WERR_OK; +} + +static bool options_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_previous(options); +} + +static bool options_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_next(options); +} + +static void options_on_input(struct dialog *dia, struct dialog_section *section, int c) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (c == ' ') { + struct option_spec *option = &options->spec[options->current_option]; + if (options->single_select) { + size_t i, noptions; + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + *(options->spec[i].state) = false; + } + } + *option->state = !*option->state; + options_unhighlight(options); + options_render_state(options); + options_highlight(options); + } +} + +static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section) +{ + options_on_input(dia, section, ' '); + return DIALOG_OK; +} + +static bool options_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (forward) { + options->current_option = 0; + } else { + options->current_option = talloc_array_length(options->spec) - 1; + } + options_highlight(options); + + return true; +} + +static void options_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + options_unhighlight(options); +} + +struct dialog_section_ops options_ops = { + .create = options_create, + .on_tab = options_on_tab, + .on_btab = options_on_btab, + .on_up = options_on_btab, + .on_down = options_on_tab, + .on_left = options_on_btab, + .on_right = options_on_tab, + .on_input = options_on_input, + .on_enter = options_on_enter, + .on_focus = options_on_focus, + .on_leave_focus = options_on_leave_focus +}; - return selection; +static int options_free(struct dialog_section_options *options) +{ + dialog_section_destroy(&options->section); + return 0; } -static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - const char *msg, va_list ap) +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select) { - struct dialog *dia; - char *str; - int width; -#define MIN_WIDTH 20 + struct dialog_section_options *options; + size_t i, noptions; + int width, maxwidth, maxrows; - str = talloc_vasprintf(ctx, msg, ap); - if (str == NULL) { + options = talloc_zero(ctx, struct dialog_section_options); + if (options == NULL) { return NULL; } + talloc_set_destructor(options, options_free); - width = strlen(str) + 2; - if (width < MIN_WIDTH) { - width = MIN_WIDTH; + for (noptions = 0; spec[noptions].label; ++noptions) { } - dia = dialog_choice_center_new(ctx, title, choices, nlines, width); - if (dia == NULL) { - return NULL; + options->spec = talloc_zero_array(options, struct option_spec, noptions); + if (options->spec == NULL) { + goto fail; } - waddstr(dia->sub_window, str); - talloc_free(str); + maxrows = noptions / maxcol; + if (noptions % maxcol) { + ++maxrows; + } - return dia; + for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) { + options->spec[i] = spec[i]; + options->spec[i].label = talloc_asprintf(options->spec, + "[ ] %s", + spec[i].label); + if (!options->spec[i].label) { + goto fail; + } + + options->spec[i].col = maxwidth; + options->spec[i].row = i % maxrows; + width = MAX(strlen(options->spec[i].label), width); + if (options->spec[i].row == maxrows - 1 || i == noptions - 1) { + maxwidth += width + 1; + width = 0; + } + } + + dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1); + options->single_select = single_select; + + return &options->section; + +fail: + talloc_free(options); + return NULL; } -int dialog_input(TALLOC_CTX *ctx, char **output, const char *title, + +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, const char *msg, ...) { va_list ap; + WERROR err; + enum dialog_action action; struct dialog *dia; - const char *choices[] = { - "Ok", - "Cancel", - NULL + struct dialog_section *section; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } }; - FIELD *field[2] = {0}; - FORM *input; - WINDOW *input_win; - int y, x; - int rv = -1; - bool input_section = true; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); va_start(ap, msg); - dia = dialog_msg_new(ctx, title, choices, 7, msg, ap); + section = dialog_section_label_new_va(dia, msg, ap); va_end(ap); - if (dia == NULL) { - return -1; - } - - getmaxyx(dia->sub_window, y, x); - input_win = derwin(dia->sub_window, 1, x - 2, 2, 1); - if (input_win == NULL) { - goto finish; - } - field[0] = new_field(1, x - 2, 0, 0, 0, 0); - if (field[0] == NULL) { - goto finish; - } + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(section, "input"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); - field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC); - set_field_back(field[0], A_REVERSE); - - input = new_form(field); - form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD); - set_form_win(input, dia->sub_window); - set_form_sub(input, input_win); - set_current_field(input, field[0]); - post_form(input); *output = NULL; - - update_panels(); - doupdate(); - - while (rv == -1) { - int c = dialog_getch(dia); - - if (c == '\t' || c == KEY_BTAB) { - if (input_section) { - int valid = form_driver(input, REQ_VALIDATION); - if (valid == E_OK) { - input_section = false; - if (c == '\t') { - menu_driver(dia->choices, - REQ_FIRST_ITEM); - } else { - menu_driver(dia->choices, - REQ_LAST_ITEM); - } - pos_menu_cursor(dia->choices); - } - } else { - if ((c == '\t' && - current_item_is_last(dia->choices)) || - (c == KEY_BTAB && - current_item_is_first(dia->choices))) { - input_section = true; - set_current_field(input, field[0]); - pos_form_cursor(input); - } else { - handle_menu_input(dia->choices, c); - } - } - } else if (input_section) { - handle_form_input(input, c); - } else { - rv = handle_menu_input(dia->choices, c); - if (rv == DIALOG_OK) { - const char *buf = field_buffer(field[0], 0); - *output = string_trim(ctx, buf); - } - } - update_panels(); - doupdate(); + if (action == DIALOG_OK) { + section = dialog_find_section(dia, "input"); + *output = dialog_section_text_field_get(ctx, section); } -finish: - if (input) { - unpost_form(input); - free_form(input); - } - if (field[0]) { - free_field(field[0]); - } - if (input_win) { - delwin(input_win); - } talloc_free(dia); - return rv; + return action; } int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, const char *title, const char *msg, ...) { va_list ap; + WERROR err; + enum dialog_action action; struct dialog *dia; - const char *choices[] = { - "Ok", - "Cancel", - NULL - }; + struct dialog_section *section; + struct button_spec spec[3]; - if (type == DIA_ALERT) { - choices[1] = NULL; + memset(&spec, '\0', sizeof(spec)); + spec[0].label = "OK"; + spec[0].action = DIALOG_OK; + if (type == DIA_CONFIRM) { + spec[1].label = "Cancel"; + spec[1].action = DIALOG_CANCEL; } + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); va_start(ap, msg); - dia = dialog_msg_new(ctx, title, choices, 5, msg, ap); + section = dialog_section_label_new_va(dia, msg, ap); va_end(ap); - if (dia == NULL) { - return -1; - } + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); - return modal_loop(dia); + return action; } -#define EDIT_WIDTH 50 -#define EDIT_INTERNAL_WIDTH (EDIT_WIDTH - 2) -#define EDIT_INPUT_WIDTH (EDIT_INTERNAL_WIDTH - 2) - -#define EDIT_NAME_LABEL_Y 0 -#define EDIT_NAME_LABEL_X 0 -#define EDIT_NAME_LABEL_WIDTH EDIT_INTERNAL_WIDTH -#define EDIT_NAME_LABEL_HEIGHT 1 -#define EDIT_NAME_INPUT_Y 1 -#define EDIT_NAME_INPUT_X 1 -#define EDIT_NAME_INPUT_WIDTH EDIT_INPUT_WIDTH -#define EDIT_NAME_INPUT_HEIGHT 1 -#define EDIT_DATA_LABEL_Y 3 -#define EDIT_DATA_LABEL_X 0 -#define EDIT_DATA_LABEL_WIDTH EDIT_INTERNAL_WIDTH -#define EDIT_DATA_LABEL_HEIGHT 1 -#define EDIT_DATA_INPUT_Y 4 -#define EDIT_DATA_INPUT_X 1 -#define EDIT_DATA_INPUT_WIDTH EDIT_INPUT_WIDTH -#define EDIT_DATA_HEIGHT_ONELINE 1 -#define EDIT_DATA_HEIGHT_MULTILINE 5 -#define EDIT_DATA_HEIGHT_BUF 10 - -#define EDIT_FORM_WIN_Y 0 -#define EDIT_FORM_WIN_X 0 -#define EDIT_FORM_WIN_HEIGHT_ONELINE \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_ONELINE) - -#define EDIT_FORM_WIN_HEIGHT_MULTILINE \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_MULTILINE) - -#define EDIT_FORM_WIN_HEIGHT_BUF \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT) - -#define EDIT_FORM_WIN_WIDTH EDIT_INTERNAL_WIDTH - -#define EDIT_PAD 5 -#define EDIT_HEIGHT_ONELINE (EDIT_FORM_WIN_HEIGHT_ONELINE + EDIT_PAD) - -#define EDIT_HEIGHT_MULTILINE (EDIT_FORM_WIN_HEIGHT_MULTILINE + EDIT_PAD) - -#define EDIT_HEIGHT_BUF \ - (EDIT_FORM_WIN_HEIGHT_BUF + EDIT_DATA_HEIGHT_BUF + EDIT_PAD) - -#define MAX_FIELDS 5 -#define FLD_NAME 1 -#define FLD_DATA 3 - -#define DIALOG_RESIZE 2 - -enum input_section { - IN_NAME, - IN_DATA, - IN_MENU -}; - -struct edit_dialog { - struct dialog *dia; - WINDOW *input_win; - FORM *input; - FIELD *field[MAX_FIELDS]; - struct hexedit *buf; - enum input_section section; - bool closing; +struct edit_req { + uint32_t type; uint32_t mode; + struct registry_key *key; + const struct value_item *vitem; }; -static int edit_dialog_free(struct edit_dialog *edit) +static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit) { - FIELD **f; + char *tmp; + struct dialog_section *data; - if (edit->input) { - unpost_form(edit->input); - free_form(edit->input); - } - for (f = edit->field; *f; ++f) { - free_field(*f); + if (edit->vitem == NULL) { + return WERR_OK; } - delwin(edit->input_win); - - return 0; -} -static WERROR fill_value_buffer(struct edit_dialog *edit, - const struct value_item *vitem) -{ - char *tmp; + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); - switch (vitem->type) { + switch (edit->mode) { case REG_DWORD: { uint32_t v = 0; - if (vitem->data.length >= 4) { - v = IVAL(vitem->data.data, 0); + if (edit->vitem->data.length >= 4) { + v = IVAL(edit->vitem->data.data, 0); } - tmp = talloc_asprintf(edit, "0x%x", v); + tmp = talloc_asprintf(dia, "%u", (unsigned)v); if (tmp == NULL) { return WERR_NOMEM; } - set_field_buffer(edit->field[FLD_DATA], 0, tmp); + dialog_section_text_field_set(data, tmp); talloc_free(tmp); break; } @@ -635,48 +1736,25 @@ static WERROR fill_value_buffer(struct edit_dialog *edit, case REG_EXPAND_SZ: { const char *s; - if (!pull_reg_sz(edit, &vitem->data, &s)) { + if (!pull_reg_sz(dia, &edit->vitem->data, &s)) { return WERR_NOMEM; } - set_field_buffer(edit->field[FLD_DATA], 0, s); + dialog_section_text_field_set(data, s); break; } case REG_MULTI_SZ: { - int rows, cols, max; - size_t padding, length, index; - const char **array, **arrayp; - char *buf = NULL; + const char **array; - dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max); - if (!pull_reg_multi_sz(edit, &vitem->data, &array)) { + if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) { return WERR_NOMEM; } - - /* try to fit each string on it's own line. each line - needs to be padded with whitespace manually, since - ncurses fields do not have newlines. */ - for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) { - length = MIN(strlen(*arrayp), cols); - padding = cols - length; - buf = talloc_realloc(edit, buf, char, - talloc_array_length(buf) + - length + padding + 1); - if (buf == NULL) { - return WERR_NOMEM; - } - memcpy(&buf[index], *arrayp, length); - index += length; - memset(&buf[index], ' ', padding); - index += padding; - buf[index] = '\0'; - } - - set_field_buffer(edit->field[FLD_DATA], 0, buf); - talloc_free(buf); + return dialog_section_text_field_set_lines(dia, data, array); } case REG_BINARY: - /* initialized upon dialog creation */ - break; + default: + return dialog_section_hexedit_set_buf(data, + edit->vitem->data.data, + edit->vitem->data.length); } return WERR_OK; @@ -694,531 +1772,253 @@ static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key, return W_ERROR_IS_OK(rv); } -static WERROR set_value(struct edit_dialog *edit, struct registry_key *key, - uint32_t type, bool new_value) +static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) { + struct edit_req *edit = arg; WERROR rv; DATA_BLOB blob; - char *name = string_trim(edit, field_buffer(edit->field[FLD_NAME], 0)); - - if (!new_value && !edit->buf && !field_status(edit->field[FLD_DATA])) { - return WERR_OK; - } - if (new_value && value_exists(edit, key, name)) { - return WERR_FILE_EXISTS; + const char *name; + struct dialog_section *name_section, *data; + + name_section = dialog_find_section(dia, "name"); + if (name_section) { + name = dialog_section_text_field_get(dia, name_section); + if (*name == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Value name must not be blank."); + return false; + } + if (value_exists(dia, edit->key, name)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Value named \"%s\" already exists.", + name); + return false; + } + } else { + SMB_ASSERT(edit->vitem); + name = edit->vitem->value_name; } + SMB_ASSERT(name); + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); + + rv = WERR_OK; switch (edit->mode) { case REG_DWORD: { + unsigned long long v; uint32_t val; - int base = 10; - const char *buf = field_buffer(edit->field[FLD_DATA], 0); - if (buf[0] == '0' && tolower(buf[1]) == 'x') { - base = 16; + if (!dialog_section_text_field_get_uint(data, &v)) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must be an integer."); + return false; } - - val = strtoul(buf, NULL, base); - blob = data_blob_talloc(edit, NULL, sizeof(val)); + if (v > UINT32_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must less than %lu.", + (unsigned long)UINT32_MAX); + return false; + } + val = (uint32_t)v; + blob = data_blob_talloc(dia, NULL, sizeof(val)); SIVAL(blob.data, 0, val); - rv = WERR_OK; break; } case REG_SZ: case REG_EXPAND_SZ: { - const char *buf = field_buffer(edit->field[FLD_DATA], 0); - char *str = string_trim(edit, buf); + const char *buf; - if (!str || !push_reg_sz(edit, &blob, str)) { + buf = dialog_section_text_field_get(dia, data); + if (!buf || !push_reg_sz(dia, &blob, buf)) { rv = WERR_NOMEM; } break; } case REG_MULTI_SZ: { - int rows, cols, max; - const char **arr; - size_t i; - const char *buf = field_buffer(edit->field[FLD_DATA], 0); - - dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max); + const char **lines; - arr = talloc_zero_array(edit, const char *, rows + 1); - if (arr == NULL) { - return WERR_NOMEM; - } - for (i = 0; *buf; ++i, buf += cols) { - SMB_ASSERT(i < rows); - arr[i] = string_trim_n(edit, buf, cols); - } - if (!push_reg_multi_sz(edit, &blob, arr)) { + lines = dialog_section_text_field_get_lines(dia, data); + if (!lines || !push_reg_multi_sz(dia, &blob, lines)) { rv = WERR_NOMEM; } break; } - case REG_BINARY: - blob = data_blob_talloc(edit, NULL, edit->buf->len); - memcpy(blob.data, edit->buf->data, edit->buf->len); - break; - } - - rv = reg_val_set(key, name, type, blob); - - return rv; -} + case REG_BINARY: { + const void *buf; + size_t len; -static void section_down(struct edit_dialog *edit) -{ - switch (edit->section) { - case IN_NAME: - if (form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_DATA; - if (edit->buf) { - hexedit_set_cursor(edit->buf); - } else { - set_current_field(edit->input, - edit->field[FLD_DATA]); - pos_form_cursor(edit->input); - } - } - break; - case IN_DATA: - if (edit->buf || - form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_MENU; - menu_driver(edit->dia->choices, REQ_FIRST_ITEM); - pos_menu_cursor(edit->dia->choices); - } - break; - case IN_MENU: - if (current_item_is_last(edit->dia->choices)) { - edit->section = IN_NAME; - set_current_field(edit->input, edit->field[FLD_NAME]); - pos_form_cursor(edit->input); - } else { - menu_driver(edit->dia->choices, REQ_RIGHT_ITEM); - } + dialog_section_hexedit_get_buf(data, &buf, &len); + blob = data_blob_talloc(dia, buf, len); break; } -} - -static void section_up(struct edit_dialog *edit) -{ - switch (edit->section) { - case IN_NAME: - if (form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_MENU; - menu_driver(edit->dia->choices, REQ_LAST_ITEM); - pos_menu_cursor(edit->dia->choices); - } - break; - case IN_DATA: - if (edit->buf || - form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_NAME; - set_current_field(edit->input, edit->field[FLD_NAME]); - pos_form_cursor(edit->input); - } - break; - case IN_MENU: - if (current_item_is_first(edit->dia->choices)) { - edit->section = IN_DATA; - if (edit->buf) { - hexedit_set_cursor(edit->buf); - } else { - set_current_field(edit->input, - edit->field[FLD_DATA]); - pos_form_cursor(edit->input); - } - } else { - menu_driver(edit->dia->choices, REQ_LEFT_ITEM); - } - break; } -} -static void handle_hexedit_input(struct hexedit *buf, int c) -{ - switch (c) { - case KEY_UP: - hexedit_driver(buf, HE_CURSOR_UP); - break; - case KEY_DOWN: - hexedit_driver(buf, HE_CURSOR_DOWN); - break; - case KEY_LEFT: - hexedit_driver(buf, HE_CURSOR_LEFT); - break; - case KEY_RIGHT: - hexedit_driver(buf, HE_CURSOR_RIGHT); - break; - default: - hexedit_driver(buf, c); - break; + if (W_ERROR_IS_OK(rv)) { + rv = reg_val_set(edit->key, name, edit->type, blob); } - hexedit_set_cursor(buf); -} - -static WERROR edit_init_dialog(struct edit_dialog *edit, uint32_t type) -{ - char *title; - int diaheight = -1; - int winheight = -1; - const char *choices[] = { - "Ok", - "Cancel", - "Resize", - NULL - }; + if (!W_ERROR_IS_OK(rv)) { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(dia, DIA_ALERT, "Error", + "Error saving value:\n%s", msg); - switch (edit->mode) { - case REG_MULTI_SZ: - diaheight = EDIT_HEIGHT_MULTILINE; - winheight = EDIT_FORM_WIN_HEIGHT_MULTILINE; - choices[2] = NULL; - break; - case REG_BINARY: - diaheight = EDIT_HEIGHT_BUF; - winheight = EDIT_FORM_WIN_HEIGHT_BUF; - break; - default: - diaheight = EDIT_HEIGHT_ONELINE; - winheight = EDIT_FORM_WIN_HEIGHT_ONELINE; - choices[2] = NULL; - break; + return false; } - title = talloc_asprintf(edit, "Edit %s value", str_regtype(type)); - if (title == NULL) { - return WERR_NOMEM; - } - edit->dia = dialog_choice_center_new(edit, title, choices, diaheight, - EDIT_WIDTH); - talloc_free(title); - if (edit->dia == NULL) { - return WERR_NOMEM; - } - edit->input_win = derwin(edit->dia->sub_window, winheight, - EDIT_FORM_WIN_WIDTH, - EDIT_FORM_WIN_Y, EDIT_FORM_WIN_X); - if (edit->input_win == NULL) { - return WERR_NOMEM; - } + return true; - return WERR_OK; } -static WERROR edit_init_form(struct edit_dialog *edit, - const struct value_item *vitem) +WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary) { + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct edit_req edit; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; - edit->field[0] = new_field(EDIT_NAME_LABEL_HEIGHT, - EDIT_NAME_LABEL_WIDTH, - EDIT_NAME_LABEL_Y, - EDIT_NAME_LABEL_X, 0, 0); - if (edit->field[0] == NULL) { - return WERR_NOMEM; - } - set_field_buffer(edit->field[0], 0, "Name"); - field_opts_off(edit->field[0], O_EDIT); - - edit->field[FLD_NAME] = new_field(EDIT_NAME_INPUT_HEIGHT, - EDIT_NAME_INPUT_WIDTH, - EDIT_NAME_INPUT_Y, - EDIT_NAME_INPUT_X, 0, 0); - if (edit->field[FLD_NAME] == NULL) { - return WERR_NOMEM; - } - - edit->field[2] = new_field(EDIT_DATA_LABEL_HEIGHT, - EDIT_DATA_LABEL_WIDTH, - EDIT_DATA_LABEL_Y, - EDIT_DATA_LABEL_X, 0, 0); - if (edit->field[2] == NULL) { - return WERR_NOMEM; + edit.key = key; + edit.vitem = vitem; + edit.type = type; + edit.mode = type; + if (force_binary || (vitem && vitem->unprintable)) { + edit.mode = REG_BINARY; } - set_field_buffer(edit->field[2], 0, "Data"); - field_opts_off(edit->field[2], O_EDIT); - - if (edit->mode == REG_BINARY) { - size_t len = 8; - const void *buf = NULL; - - if (vitem) { - len = vitem->data.length; - buf = vitem->data.data; - } - edit->buf = hexedit_new(edit, edit->dia->sub_window, - EDIT_DATA_HEIGHT_BUF, - EDIT_DATA_INPUT_Y, - EDIT_DATA_INPUT_X, - buf, len); - if (edit->buf == NULL) { - return WERR_NOMEM; - } - hexedit_refresh(edit->buf); - hexedit_set_cursor(edit->buf); - } else { - int val_rows = EDIT_DATA_HEIGHT_ONELINE; - if (edit->mode == REG_MULTI_SZ) { - val_rows = EDIT_DATA_HEIGHT_MULTILINE; - } - edit->field[FLD_DATA] = new_field(val_rows, - EDIT_DATA_INPUT_WIDTH, - EDIT_DATA_INPUT_Y, - EDIT_DATA_INPUT_X, 0, 0); - if (edit->field[FLD_DATA] == NULL) { - return WERR_NOMEM; - } - } + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1); + dialog_set_submit_cb(dia, edit_on_submit, &edit); - set_field_back(edit->field[FLD_NAME], A_REVERSE); - field_opts_off(edit->field[FLD_NAME], O_BLANK | O_AUTOSKIP | O_STATIC); - if (edit->field[FLD_DATA]) { - set_field_back(edit->field[FLD_DATA], A_REVERSE); - field_opts_off(edit->field[FLD_DATA], - O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP); - if (edit->mode == REG_DWORD) { - set_field_type(edit->field[FLD_DATA], TYPE_REGEXP, - "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$"); - } - } + section = dialog_section_label_new(dia, "Type"); + dialog_append_section(dia, section); + section = dialog_section_label_new(dia, "%s", + str_regtype(type)); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_label_new(dia, "Name"); + dialog_append_section(dia, section); if (vitem) { - set_field_buffer(edit->field[FLD_NAME], 0, vitem->value_name); - field_opts_off(edit->field[FLD_NAME], O_EDIT); - fill_value_buffer(edit, vitem); - } - - edit->input = new_form(edit->field); - if (edit->input == NULL) { - return WERR_NOMEM; - } - form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD); - - set_form_win(edit->input, edit->dia->sub_window); - set_form_sub(edit->input, edit->input_win); - set_current_field(edit->input, edit->field[FLD_NAME]); - post_form(edit->input); - - return WERR_OK; -} - -static WERROR handle_editor_input(struct edit_dialog *edit, - struct registry_key *key, - uint32_t type, - const struct value_item *vitem, int c) -{ - WERROR rv = WERR_OK; - int selection; - - if (edit->section == IN_NAME) { - handle_form_input(edit->input, c); - } else if (edit->section == IN_DATA) { - if (edit->buf) { - handle_hexedit_input(edit->buf, c); - } else { - handle_form_input(edit->input, c); - } + section = dialog_section_label_new(dia, "%s", + vitem->value_name); } else { - selection = handle_menu_input(edit->dia->choices, c); - if (selection == DIALOG_OK) { - rv = set_value(edit, key, type, vitem == NULL); - if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) { - dialog_notice(edit, DIA_ALERT, - "Value exists", - "Value name already exists."); - selection = -1; - } else { - edit->closing = true; - } - } else if (selection == DIALOG_RESIZE) { - char *n; - size_t newlen = 0; - - dialog_input(edit, &n, "Resize buffer", - "Enter new size"); - if (n) { - newlen = strtoul(n, NULL, 10); - edit->section = IN_DATA; - hexedit_resize_buffer(edit->buf, newlen); - hexedit_refresh(edit->buf); - hexedit_set_cursor(edit->buf); - talloc_free(n); - } - } else if (selection == DIALOG_CANCEL) { - edit->closing = true; - } - } - - return rv; -} - -WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, - uint32_t type, const struct value_item *vitem, - bool force_binary) -{ - struct edit_dialog *edit; - WERROR rv = WERR_NOMEM; - - edit = talloc_zero(ctx, struct edit_dialog); - if (edit == NULL) { - return rv; + section = dialog_section_text_field_new(dia, 1, 50); + dialog_section_set_name(section, "name"); } - talloc_set_destructor(edit, edit_dialog_free); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); - edit->mode = type; - if (force_binary || (vitem && vitem->unprintable)) { - edit->mode = REG_BINARY; - } + section = dialog_section_label_new(dia, "Data"); + dialog_append_section(dia, section); - rv = edit_init_dialog(edit, type); - if (!W_ERROR_IS_OK(rv)) { - goto finish; - } - rv = edit_init_form(edit, vitem); - if (!W_ERROR_IS_OK(rv)) { - goto finish; + switch (edit.mode) { + case REG_DWORD: + case REG_SZ: + case REG_EXPAND_SZ: + section = dialog_section_text_field_new(dia, 1, 50); + break; + case REG_MULTI_SZ: + section = dialog_section_text_field_new(dia, 10, 50); + break; + case REG_BINARY: + default: + section = dialog_section_hexedit_new(dia, 10); + break; } - update_panels(); - doupdate(); + dialog_section_set_name(section, "data"); + dialog_append_section(dia, section); - edit->section = IN_NAME; - edit->closing = false; + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); - do { - int c = dialog_getch(edit->dia); + dialog_create(dia); - if (c == '\t') { - section_down(edit); - } else if (c == KEY_BTAB) { - section_up(edit); - } else { - rv = handle_editor_input(edit, key, type, vitem, c); - } + err = fill_value_buffer(dia, &edit); + if (!W_ERROR_IS_OK(err)) { + return err; + } - update_panels(); - doupdate(); - } while (!edit->closing); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); -finish: - talloc_free(edit); + talloc_free(dia); - return rv; + return WERR_OK; } int dialog_select_type(TALLOC_CTX *ctx, int *type) { + WERROR err; + enum dialog_action action; struct dialog *dia; - const char *choices[] = { - "OK", - "Cancel", - NULL - }; + struct dialog_section *section; const char *reg_types[] = { + "REG_BINARY", "REG_DWORD", - "REG_SZ", "REG_EXPAND_SZ", "REG_MULTI_SZ", - "REG_BINARY", + "REG_SZ" }; -#define NTYPES (sizeof(reg_types) / sizeof(const char*)) - ITEM **item; - MENU *list; - WINDOW *type_win; - int sel = -1; - size_t i; - - dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20); - if (dia == NULL) { - return -1; - } - - mvwprintw(dia->sub_window, 0, 0, "Choose type:"); - type_win = derwin(dia->sub_window, 6, 18, 1, 0); - if (type_win == NULL) { - goto finish; - } - - item = talloc_zero_array(dia, ITEM *, NTYPES + 1); - if (item == NULL) { - goto finish; - } + #define NTYPES ARRAY_SIZE(reg_types) + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + bool flags[NTYPES] = { true }; + struct option_spec opsec[NTYPES + 1]; + unsigned i; + memset(&opsec, '\0', sizeof(opsec)); for (i = 0; i < NTYPES; ++i) { - int t = regtype_by_string(reg_types[i]); - - item[i] = new_item(reg_types[i], reg_types[i]); - if (item[i] == NULL) { - goto finish; - } - set_item_userptr(item[i], (void*)(uintptr_t)t); - } - - list = new_menu(item); - if (list == NULL) { - goto finish; - } - - set_menu_format(list, 7, 1); - set_menu_win(list, dia->sub_window); - set_menu_sub(list, type_win); - menu_opts_off(list, O_SHOWDESC); - set_menu_mark(list, "* "); - post_menu(list); - - update_panels(); - doupdate(); - - while (sel == -1) { - ITEM *it; - int c = dialog_getch(dia); - - switch (c) { - case KEY_UP: - menu_driver(list, REQ_UP_ITEM); - break; - case KEY_DOWN: - menu_driver(list, REQ_DOWN_ITEM); - break; - case KEY_LEFT: - menu_driver(dia->choices, REQ_LEFT_ITEM); - break; - case KEY_RIGHT: - menu_driver(dia->choices, REQ_RIGHT_ITEM); - break; - case '\n': - case KEY_ENTER: - it = current_item(list); - *type = (int)(uintptr_t)item_userptr(it); - it = current_item(dia->choices); - sel = (int)(uintptr_t)item_userptr(it); - break; + opsec[i].label = reg_types[i]; + opsec[i].state = &flags[i]; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1); + + section = dialog_section_label_new(dia, "Select type for new value:"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, opsec, 2, true); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + + dialog_modal_loop(dia, &err, &action); + if (action == DIALOG_OK) { + for (i = 0; i < NTYPES; ++i) { + if (flags[i]) { + *type = regtype_by_string(reg_types[i]); + break; + } } - - update_panels(); - doupdate(); } -finish: - if (list) { - unpost_menu(list); - free_menu(list); - } - if (item) { - ITEM **it; - for (it = item; *it; ++it) { - free_item(*it); - } - } - if (type_win) { - delwin(type_win); - } talloc_free(dia); - return sel; + return action; } int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index f57bcd1..0018f9f 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -24,44 +24,190 @@ #include #include +struct dialog; +struct dialog_section; + +/* dialog submit cb. return true to close dialog, false to keep + it open */ +typedef bool (*dialog_submit_cb)(struct dialog *, struct dialog_section *, + void *); + struct dialog { + char *title; WINDOW *window; - WINDOW *sub_window; - WINDOW *menu_window; PANEL *panel; - MENU *choices; - ITEM **choice_items; + int x; + int y; + short color; bool centered; + dialog_submit_cb submit; + void *submit_arg; + struct dialog_section *head_section; + struct dialog_section *tail_section; + struct dialog_section *current_section; +}; + +enum dialog_action { + DIALOG_IGNORE, + DIALOG_OK, + DIALOG_CANCEL }; -struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, - int ncols, int y, int x); +struct dialog_section_ops { + /* create section */ + WERROR (*create)(struct dialog *, struct dialog_section *); + + /* (optional) cleanup the section */ + void (*destroy)(struct dialog_section *); + + /* (optional) handle character input */ + void (*on_input)(struct dialog *, struct dialog_section *, int c); + + /* (optional) handle a tab character. return true if section dealt + with the tab internally, or false to advance focus to + the next dialog section. */ + bool (*on_tab)(struct dialog *, struct dialog_section *); -struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, - int nlines, int ncols); + /* (optional) handle a btab character. return true if section dealt + with the tab internally, or false to move focus to + the previous dialog section. */ + bool (*on_btab)(struct dialog *, struct dialog_section *); -struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, int ncols, - int y, int x); + /* */ + bool (*on_up)(struct dialog *, struct dialog_section *); -struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols); + /* */ + bool (*on_down)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_left)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_right)(struct dialog *, struct dialog_section *); + + /* (optional) handle enter key. return DIALOG_OK to submit + dialog, DIALOG_CANCEL to close dialog, or DIALOG_IGNORE to + handle the enter internally. */ + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + + /* (optional) called when this section is about to take focus. forward + is set to true when focus has landed here from forward traversal, + such as from a tab. return true to accept focus, false to pass to an + adjacent section. */ + bool (*on_focus)(struct dialog *, struct dialog_section *, bool forward); + + /* (optional) called when focus is leaving this section */ + void (*on_leave_focus)(struct dialog *, struct dialog_section *); +}; + +enum section_justify { + SECTION_JUSTIFY_LEFT, + SECTION_JUSTIFY_CENTER, + SECTION_JUSTIFY_RIGHT, +}; + +struct dialog_section { + char *name; + int nlines; + int ncols; + WINDOW *window; + enum section_justify justify; + const struct dialog_section_ops *ops; + struct dialog_section *next; + struct dialog_section *prev; +}; + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, + const char *title, int y, int x); + +void dialog_section_destroy(struct dialog_section *section); +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols); + +void dialog_section_set_name(struct dialog_section *section, const char *name); +const char *dialog_section_get_name(struct dialog_section *section); +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify); + +void dialog_append_section(struct dialog *dia, + struct dialog_section *section); +struct dialog_section *dialog_find_section(struct dialog *dia, + const char *name); + +WERROR dialog_create(struct dialog *dia); +void dialog_show(struct dialog *dia); +void dialog_destroy(struct dialog *dia); +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg); +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action); +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action); + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap); +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...); + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep); + + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width); +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section); +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section); +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out); +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out); +void dialog_section_text_field_set(struct dialog_section *section, + const char *s); +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array); + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height); +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size); +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size); + +struct button_spec { + const char *label; + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + enum dialog_action action; + + /* internal */ + int col; +}; +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec); + +struct option_spec { + const char *label; + bool *state; + + /* internal */ + int col; + int row; +}; +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select); enum dialog_type { DIA_ALERT, DIA_CONFIRM }; -enum dialog_selection { - DIALOG_OK = 0, - DIALOG_CANCEL = 1 -}; - int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, const char *title, const char *msg, ...); -int dialog_input(TALLOC_CTX *ctx, char **output, const char *title, +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, const char *msg, ...); struct registry_key; -- 2.1.1 From 7f5fc8dbe0d185067d5575b8940eb57767d3583c Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sat, 26 Jul 2014 19:49:33 -0700 Subject: [PATCH 28/45] regedit: move cursor to edited value in list and report edit errors Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 061d3e9a66f7f71cc6532fa6b0bcf47bf0fe3e35) --- source3/utils/regedit.c | 41 ++++++++++++++++++++++++++++----------- source3/utils/regedit_dialog.c | 26 ++++++++++++++++--------- source3/utils/regedit_dialog.h | 7 ++++--- source3/utils/regedit_valuelist.c | 22 +++++++++++++++++++++ source3/utils/regedit_valuelist.h | 4 ++++ 5 files changed, 77 insertions(+), 23 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 5b7885c..cc3598b 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -406,6 +406,8 @@ static void handle_value_input(struct regedit *regedit, int c) { struct value_item *vitem; bool binmode = false; + WERROR err; + int sel; switch (c) { case KEY_DOWN: @@ -423,26 +425,45 @@ static void handle_value_input(struct regedit *regedit, int c) vitem = value_list_get_current_item(regedit->vl); if (vitem) { struct tree_node *node; + const char *name = NULL; node = tree_view_get_current_node(regedit->keys); - dialog_edit_value(regedit, node->key, vitem->type, - vitem, binmode); - tree_node_reopen_key(node); - value_list_load(regedit->vl, node->key); + sel = dialog_edit_value(regedit, node->key, vitem->type, + vitem, binmode, &err, &name); + if (!W_ERROR_IS_OK(err)) { + const char *msg = get_friendly_werror_msg(err); + dialog_notice(regedit, DIA_ALERT, "Error", + "Error editing value:\n%s", msg); + } else if (sel == DIALOG_OK) { + tree_node_reopen_key(node); + value_list_load(regedit->vl, node->key); + value_list_set_current_item_by_name(regedit->vl, + name); + talloc_free(discard_const(name)); + } } break; case 'n': case 'N': { int new_type; - int sel; sel = dialog_select_type(regedit, &new_type); if (sel == DIALOG_OK) { struct tree_node *node; + const char *name = NULL; node = tree_view_get_current_node(regedit->keys); - dialog_edit_value(regedit, node->key, new_type, NULL, - false); - tree_node_reopen_key(node); - value_list_load(regedit->vl, node->key); + sel = dialog_edit_value(regedit, node->key, new_type, + NULL, false, &err, &name); + if (!W_ERROR_IS_OK(err)) { + const char *msg = get_friendly_werror_msg(err); + dialog_notice(regedit, DIA_ALERT, "Error", + "Error creating value:\n%s", msg); + } else if (sel == DIALOG_OK) { + tree_node_reopen_key(node); + value_list_load(regedit->vl, node->key); + value_list_set_current_item_by_name(regedit->vl, + name); + talloc_free(discard_const(name)); + } } break; } @@ -450,8 +471,6 @@ static void handle_value_input(struct regedit *regedit, int c) case 'D': vitem = value_list_get_current_item(regedit->vl); if (vitem) { - int sel; - sel = dialog_notice(regedit, DIA_CONFIRM, "Delete Value", "Really delete value \"%s\"?", diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 119219d..3cef7a0 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1871,11 +1871,11 @@ static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, } -WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, - uint32_t type, const struct value_item *vitem, - bool force_binary) +int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary, WERROR *err, + const char **name) { - WERROR err; enum dialog_action action; struct dialog *dia; struct dialog_section *section; @@ -1947,17 +1947,25 @@ WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, dialog_create(dia); - err = fill_value_buffer(dia, &edit); - if (!W_ERROR_IS_OK(err)) { - return err; + *err = fill_value_buffer(dia, &edit); + if (!W_ERROR_IS_OK(*err)) { + return DIALOG_CANCEL; } dialog_show(dia); - dialog_modal_loop(dia, &err, &action); + dialog_modal_loop(dia, err, &action); + + if (action == DIALOG_OK && name) { + if (vitem) { + *name = talloc_strdup(ctx, vitem->value_name); + } else if ((section = dialog_find_section(dia, "name"))) { + *name = dialog_section_text_field_get(ctx, section); + } + } talloc_free(dia); - return WERR_OK; + return action; } int dialog_select_type(TALLOC_CTX *ctx, int *type) diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index 0018f9f..c0d89ff 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -213,9 +213,10 @@ int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, struct registry_key; struct value_item; -WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, - uint32_t type, const struct value_item *vitem, - bool force_binary); +int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary, WERROR *err, + const char **name); int dialog_select_type(TALLOC_CTX *ctx, int *type); diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c index 9557c8c..aa387a7 100644 --- a/source3/utils/regedit_valuelist.c +++ b/source3/utils/regedit_valuelist.c @@ -392,6 +392,28 @@ struct value_item *value_list_get_current_item(struct value_list *vl) multilist_get_current_row(vl->list)); } +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, item); +} + +void value_list_set_current_item(struct value_list *vl, + const struct value_item *item) +{ + multilist_set_current_row(vl->list, item); +} + void value_list_driver(struct value_list *vl, int c) { multilist_driver(vl->list, c); diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h index ea67075..b84b4ff 100644 --- a/source3/utils/regedit_valuelist.h +++ b/source3/utils/regedit_valuelist.h @@ -52,6 +52,10 @@ 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); struct value_item *value_list_get_current_item(struct value_list *vl); +void value_list_set_current_item(struct value_list *vl, + const struct value_item *item); +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); #endif -- 2.1.1 From 7ecc5393665d402bf833ed4c6c5668ad6936d193 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sun, 27 Jul 2014 17:39:06 -0700 Subject: [PATCH 29/45] regedit: use the right function to reopen a hive Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 1b6445ae03521d788bf247eb550d1b51113fc8df) --- source3/utils/regedit.c | 15 ++++++++++----- source3/utils/regedit_treeview.c | 17 ++++++++++++++--- source3/utils/regedit_treeview.h | 3 ++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index cc3598b..d77c2e4 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -187,7 +187,8 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, } else { /* Reopen the parent key to make sure the new subkey will be noticed. */ - tree_node_reopen_key(parent); + tree_node_reopen_key(regedit->registry_context, + parent); } list = tree_node_first(node); @@ -377,7 +378,8 @@ static void handle_tree_input(struct regedit *regedit, int c) rv = reg_key_del(node, parent->key, node->name); if (W_ERROR_IS_OK(rv)) { - tree_node_reopen_key(parent); + tree_node_reopen_key(regedit->registry_context, + parent); tree_view_clear(regedit->keys); pop = tree_node_pop(&node); talloc_free(pop); @@ -434,7 +436,8 @@ static void handle_value_input(struct regedit *regedit, int c) dialog_notice(regedit, DIA_ALERT, "Error", "Error editing value:\n%s", msg); } else if (sel == DIALOG_OK) { - tree_node_reopen_key(node); + tree_node_reopen_key(regedit->registry_context, + node); value_list_load(regedit->vl, node->key); value_list_set_current_item_by_name(regedit->vl, name); @@ -458,7 +461,8 @@ static void handle_value_input(struct regedit *regedit, int c) dialog_notice(regedit, DIA_ALERT, "Error", "Error creating value:\n%s", msg); } else if (sel == DIALOG_OK) { - tree_node_reopen_key(node); + tree_node_reopen_key(regedit->registry_context, + node); value_list_load(regedit->vl, node->key); value_list_set_current_item_by_name(regedit->vl, name); @@ -480,7 +484,8 @@ static void handle_value_input(struct regedit *regedit, int c) node = tree_view_get_current_node(regedit->keys); reg_del_value(regedit, node->key, vitem->value_name); - tree_node_reopen_key(node); + tree_node_reopen_key(regedit->registry_context, + node); value_list_load(regedit->vl, node->key); } } diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index 47d9282..885e6d0 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -197,13 +197,24 @@ static uint32_t get_num_subkeys(struct tree_node *node) return 0; } -WERROR tree_node_reopen_key(struct tree_node *node) +WERROR tree_node_reopen_key(struct registry_context *ctx, + struct tree_node *node) { SMB_ASSERT(node->parent != NULL); SMB_ASSERT(node->name != NULL); TALLOC_FREE(node->key); - return reg_open_key(node->parent, node->parent->key, node->name, - &node->key); + + if (tree_node_is_top_level(node)) { + WERROR rv; + struct registry_key *key; + rv = reg_get_predefined_key_by_name(ctx, node->name, &key); + if (W_ERROR_IS_OK(rv)) { + node->key = talloc_steal(node, key); + } + return rv; + } + + return reg_open_key(node, node->parent->key, node->name, &node->key); } bool tree_node_has_children(struct tree_node *node) diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h index 4d1851a..919507f 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -74,7 +74,8 @@ void tree_view_clear(struct tree_view *view); WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root); WERROR tree_view_set_path(struct tree_view *view, const char **path); WERROR tree_view_update(struct tree_view *view, struct tree_node *list); -WERROR tree_node_reopen_key(struct tree_node *node); +WERROR tree_node_reopen_key(struct registry_context *ctx, + struct tree_node *node); bool tree_node_has_children(struct tree_node *node); WERROR tree_node_load_children(struct tree_node *node); void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node); -- 2.1.1 From 0355ae24828cab54c20fcace0913db5b5b1c6033 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sun, 27 Jul 2014 17:48:05 -0700 Subject: [PATCH 30/45] regedit: clear value list after creating new key Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit cc22cb5cd44fdcc078211d2b82502ca219b1e928) --- source3/utils/regedit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index d77c2e4..3ff833a 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -198,6 +198,7 @@ static void add_reg_key(struct regedit *regedit, struct tree_node *node, node = new_node; } tree_view_set_current_node(regedit->keys, node); + load_values(regedit); } else { msg = get_friendly_werror_msg(rv); dialog_notice(regedit, DIA_ALERT, "New Key", -- 2.1.1 From a78090bbe9498a2a7bbb09f09310e916723a957c Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 28 Jul 2014 23:04:10 -0700 Subject: [PATCH 31/45] regedit: use pad as a canvas for dialogs Drawing in a pad allows the dialog to maintain the same size even when the terminal window is shrunk to some awkwardly small size. It also helps avoid hacks needed to update positions of subwindows when the panel is moved. Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit c272178ccc6bb2f3b4a6621ef463489741c7e040) --- source3/utils/regedit_dialog.c | 79 ++++++++++++++++++++++++++++++------------ source3/utils/regedit_dialog.h | 1 + 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 3cef7a0..d8ffa9f 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -343,6 +343,11 @@ WERROR dialog_create(struct dialog *dia) /* create window for dialog */ nlines += 4; ncols += 6; + dia->pad = newpad(nlines, ncols); + if (dia->pad == NULL) { + rv = WERR_NOMEM; + goto fail; + } dia->centered = false; if (dia->y < 0 || dia->x < 0) { dia->centered = true; @@ -360,18 +365,19 @@ WERROR dialog_create(struct dialog *dia) } /* setup color and border */ - wbkgdset(dia->window, ' ' | COLOR_PAIR(dia->color)); - wclear(dia->window); - mvwhline(dia->window, 1, 2, 0, ncols - 4); - mvwhline(dia->window, nlines - 2, 2, 0, ncols - 4); - mvwvline(dia->window, 2, 1, 0, nlines - 4); - mvwvline(dia->window, 2, ncols - 2, 0, nlines - 4); - mvwaddch(dia->window, 1, 1, ACS_ULCORNER); - mvwaddch(dia->window, 1, ncols - 2, ACS_URCORNER); - mvwaddch(dia->window, nlines - 2, 1, ACS_LLCORNER); - mvwaddch(dia->window, nlines - 2, ncols - 2, ACS_LRCORNER); + getmaxyx(dia->pad, nlines, ncols); + wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color)); + wclear(dia->pad); + mvwhline(dia->pad, 1, 2, 0, ncols - 4); + mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4); + mvwvline(dia->pad, 2, 1, 0, nlines - 4); + mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4); + mvwaddch(dia->pad, 1, 1, ACS_ULCORNER); + mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER); + mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER); + mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER); col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2; - mvwprintw(dia->window, 1, col, " %s ", dia->title); + mvwprintw(dia->pad, 1, col, " %s ", dia->title); /* create subwindows for each section */ row = 2; @@ -389,7 +395,7 @@ WERROR dialog_create(struct dialog *dia) break; } - section->window = derwin(dia->window, section->nlines, + section->window = subpad(dia->pad, section->nlines, section->ncols, row, col); if (section->window == NULL) { rv = WERR_NOMEM; @@ -410,15 +416,30 @@ fail: void dialog_show(struct dialog *dia) { - struct dialog_section *section; + int nlines, ncols; + int pad_y, pad_x; + int y, x; + int rv; + touchwin(dia->pad); + getmaxyx(dia->window, nlines, ncols); + getmaxyx(dia->pad, pad_y, pad_x); + y = 0; + if (pad_y > nlines) { + y = (pad_y - nlines) / 2; + } + x = 0; + if (pad_x > ncols) { + x = (pad_x - ncols) / 2; + } + rv = copywin(dia->pad, dia->window, y, x, 0, 0, + nlines - 1, ncols - 1, false); + SMB_ASSERT(rv == OK); + + getyx(dia->pad, pad_y, pad_x); + wmove(dia->window, pad_y - y, pad_x - x); touchwin(dia->window); wnoutrefresh(dia->window); - section = dia->head_section; - do { - wnoutrefresh(section->window); - section = section->next; - } while (section != dia->head_section); } void dialog_destroy(struct dialog *dia) @@ -448,9 +469,16 @@ static int dialog_getch(struct dialog *dia) c = regedit_getch(); if (c == KEY_RESIZE) { int nlines, ncols, y, x; + int pad_nlines, pad_ncols; + int win_nlines, win_ncols; - getmaxyx(dia->window, nlines, ncols); + getmaxyx(dia->window, win_nlines, win_ncols); + getmaxyx(dia->pad, pad_nlines, pad_ncols); getbegyx(dia->window, y, x); + + nlines = pad_nlines; + ncols = pad_ncols; + if (dia->centered) { center_above_window(&nlines, &ncols, &y, &x); } else { @@ -469,6 +497,10 @@ static int dialog_getch(struct dialog *dia) } } } + if (nlines != win_nlines || ncols != win_ncols) { + wresize(dia->window, nlines, ncols); + replace_panel(dia->panel, dia->window); + } move_panel(dia->panel, y, x); } @@ -542,6 +574,7 @@ void dialog_modal_loop(struct dialog *dia, WERROR *err, enum dialog_action *action) { do { + dialog_show(dia); update_panels(); doupdate(); } while (dialog_handle_input(dia, err, action)); @@ -649,10 +682,10 @@ static WERROR hsep_create(struct dialog *dia, struct dialog_section *section) /* change the border characters around this section to tee chars */ getparyx(section->window, y, x); - mvwaddch(dia->window, y, x - 1, ACS_HLINE); - mvwaddch(dia->window, y, x - 2, ACS_LTEE); - mvwaddch(dia->window, y, x + section->ncols, ACS_HLINE); - mvwaddch(dia->window, y, x + section->ncols + 1, ACS_RTEE); + mvwaddch(dia->pad, y, x - 1, ACS_HLINE); + mvwaddch(dia->pad, y, x - 2, ACS_LTEE); + mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE); + mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE); } return WERR_OK; diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index c0d89ff..18b9b98 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -35,6 +35,7 @@ typedef bool (*dialog_submit_cb)(struct dialog *, struct dialog_section *, struct dialog { char *title; WINDOW *window; + WINDOW *pad; PANEL *panel; int x; int y; -- 2.1.1 From 00534bc6ca7cddfa2823ef12a86764f5335c7c3c Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Mon, 28 Jul 2014 23:23:32 -0700 Subject: [PATCH 32/45] regedit: don't use subwindows in hexedit Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 982c06f55a0ef8a80a0f87c9e77200ce952f85b3) --- source3/utils/regedit_dialog.c | 4 ++-- source3/utils/regedit_hexedit.c | 45 ++++++++++------------------------------- source3/utils/regedit_hexedit.h | 4 ++-- 3 files changed, 15 insertions(+), 38 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index d8ffa9f..3c57773 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1037,8 +1037,8 @@ static WERROR hexedit_create(struct dialog *dia, struct dialog_section_hexedit *hexedit = talloc_get_type_abort(section, struct dialog_section_hexedit); - hexedit->buf = hexedit_new(dia, section->window, section->nlines, - 0, 0, NULL, HEXEDIT_MIN_SIZE); + hexedit->buf = hexedit_new(dia, section->window, NULL, + HEXEDIT_MIN_SIZE); if (hexedit->buf == NULL) { return WERR_NOMEM; } diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c index 847c5d8..bdc4255 100644 --- a/source3/utils/regedit_hexedit.c +++ b/source3/utils/regedit_hexedit.c @@ -44,32 +44,19 @@ struct hexedit { int nibble; uint8_t *data; WINDOW *win; - WINDOW *status_line; }; static int max_rows(WINDOW *win) { - int maxy, maxx; + int maxy; - getmaxyx(win, maxy, maxx); + maxy = getmaxy(win); return maxy - 1; } -static int hexedit_free(struct hexedit *buf) -{ - if (buf->status_line) { - delwin(buf->status_line); - } - if (buf->win) { - delwin(buf->win); - } - - return 0; -} - -struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, - int y, int x, const void *data, size_t sz) +struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data, + size_t sz) { WERROR rv; struct hexedit *buf; @@ -79,19 +66,7 @@ struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, return NULL; } - talloc_set_destructor(buf, hexedit_free); - - buf->win = derwin(parent, nlines, LINE_WIDTH, y, x); - if (buf->win == NULL) { - goto fail; - } - - buf->status_line = derwin(buf->win, 1, LINE_WIDTH, max_rows(buf->win), - 0); - if (buf->status_line == NULL) { - goto fail; - } - wattron(buf->status_line, A_REVERSE | A_STANDOUT); + buf->win = parent; rv = hexedit_set_buf(buf, data, sz); if (!W_ERROR_IS_OK(rv)) { @@ -147,18 +122,20 @@ static size_t bytes_per_screen(WINDOW *win) void hexedit_set_cursor(struct hexedit *buf) { - werase(buf->status_line); + wmove(buf->win, max_rows(buf->win), 0); + wattron(buf->win, A_REVERSE | A_STANDOUT); + wclrtoeol(buf->win); if (buf->len) { - wprintw(buf->status_line, "Len:%lu Off:%lu Val:0x%X", buf->len, + wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len, buf->cursor_offset, buf->data[buf->cursor_offset]); } else { - wprintw(buf->status_line, "Len:%lu (empty)", buf->len); + wprintw(buf->win, "Len:%lu (empty)", buf->len); } + wattroff(buf->win, A_REVERSE | A_STANDOUT); wmove(buf->win, buf->cursor_y, buf->cursor_x); wcursyncup(buf->win); wsyncup(buf->win); untouchwin(buf->win); - wnoutrefresh(buf->status_line); } void hexedit_refresh(struct hexedit *buf) diff --git a/source3/utils/regedit_hexedit.h b/source3/utils/regedit_hexedit.h index af2432a..dc13c21 100644 --- a/source3/utils/regedit_hexedit.h +++ b/source3/utils/regedit_hexedit.h @@ -34,8 +34,8 @@ enum { #define LINE_WIDTH 44 struct hexedit; -struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines, - int y, int x, const void *data, size_t sz); +struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data, + size_t sz); WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz); const void *hexedit_get_buf(struct hexedit *buf); size_t hexedit_get_buf_len(struct hexedit *buf); -- 2.1.1 From af6eb2058a37e699d2aa8442fdaae3908f15b5a1 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 30 Jul 2014 22:04:50 -0700 Subject: [PATCH 33/45] regedit: flesh out search dialog and simplify search opts Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 09245177a4031fb1a1319071e745f0df10b1ee53) --- source3/utils/regedit.c | 11 ++---- source3/utils/regedit.h | 9 ++--- source3/utils/regedit_dialog.c | 86 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 3ff833a..c1b94ac 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -537,18 +537,15 @@ static void handle_main_input(struct regedit *regedit, int c) struct regedit_search_opts *opts; opts = ®edit->active_search; - if (opts->query) { - talloc_free(discard_const(opts->query)); - } rv = dialog_search_input(regedit, opts); if (rv == DIALOG_OK) { SMB_ASSERT(opts->query != NULL); - opts->match = find_substring; + opts->match = find_substring_nocase; opts->node = regedit->keys->root; - if (opts->search_nocase) { - opts->match = find_substring_nocase; + if (opts->search_case) { + opts->match = find_substring; } - if (opts->search_relative) { + if (!opts->search_recursive) { opts->node = tree_view_get_current_node(regedit->keys); } diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h index 688bc72..c99aeba 100644 --- a/source3/utils/regedit.h +++ b/source3/utils/regedit.h @@ -65,11 +65,10 @@ struct regedit_search_opts { const char *query; regedit_search_match_fn_t match; struct tree_node *node; - unsigned int search_key:1; - unsigned int search_value:1; - unsigned int search_recursive:1; - unsigned int search_relative:1; - unsigned int search_nocase:1; + bool search_key; + bool search_value; + bool search_recursive; + bool search_case; }; #define PAIR_YELLOW_CYAN 1 diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 3c57773..20d7651 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -2062,16 +2062,88 @@ int dialog_select_type(TALLOC_CTX *ctx, int *type) return action; } +struct search_req { + TALLOC_CTX *ctx; + struct regedit_search_opts *opts; +}; + +static bool search_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct search_req *search = arg; + struct dialog_section *query; + + query = dialog_find_section(dia, "query"); + SMB_ASSERT(query != NULL); + + if (!search->opts->search_key && !search->opts->search_value) { + dialog_notice(dia, DIA_ALERT, "Error", + "Must search a key and/or a value"); + return false; + } + + talloc_free(discard_const(search->opts->query)); + search->opts->query = dialog_section_text_field_get(search->ctx, query); + SMB_ASSERT(search->opts->query != NULL); + if (search->opts->query[0] == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Query must not be blank."); + return false; + } + + return true; +} + int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) { - int rv; - // TODO + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section, *query; + struct search_req search; + struct button_spec spec[] = { + {.label = "Search", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + struct option_spec search_opts[] = { + {.label = "Search Keys", .state = &opts->search_key}, + {.label = "Search Values", .state = &opts->search_value}, + {.label = "Recursive", .state = &opts->search_recursive}, + {.label = "Case Sensitive", .state = &opts->search_case}, + { 0 } + }; + + if (!opts->search_key && !opts->search_value) { + opts->search_key = true; + } + + search.ctx = ctx; + search.opts = opts; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1); + dialog_set_submit_cb(dia, search_on_submit, &search); + section = dialog_section_label_new(dia, "Query"); + dialog_append_section(dia, section); + query = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(query, "query"); + dialog_append_section(dia, query); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, search_opts, 2, false); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); - opts->search_key = 1; - opts->search_recursive = 1; - opts->search_nocase = 1; + dialog_create(dia); + if (opts->query) { + dialog_section_text_field_set(query, opts->query); + } - rv = dialog_input(ctx, &opts->query, "Search", "Query"); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); - return rv; + return action; } -- 2.1.1 From d3a75bbb458a12592c635e2bd6e2357ef175804e Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Thu, 31 Jul 2014 23:24:19 -0700 Subject: [PATCH 34/45] 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 (cherry picked from commit bcab659516382d5dc51b1558a895a9a26d81c6b8) --- 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(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index c1b94ac..a5fb913 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 c99aeba..0928f9e 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 885e6d0..cf071de 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 919507f..ba0cd0a 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 aa387a7..f12a81e 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 b84b4ff..1178389 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 -- 2.1.1 From 4e934e82abad977593c9d16d2dd3b5b249fe09ba Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sat, 2 Aug 2014 17:29:31 -0700 Subject: [PATCH 35/45] regedit: find previous items Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit fd95b0aa96067d4c9ce60b73168bf9fe5c95ffac) --- source3/utils/regedit.c | 28 +++++++++++++++++++-------- source3/utils/regedit_treeview.c | 41 ++++++++++++++++++++++++++++++++++++++++ source3/utils/regedit_treeview.h | 1 + 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index a5fb913..34a63d7 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -222,6 +222,11 @@ static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, bool search_key, need_sync; char *save_value_name; WERROR rv; + bool (*iterate)(struct tree_node **, bool, WERROR *); + struct value_item *(*find_item)(struct value_list *, + struct value_item *, + const char *, + regedit_search_match_fn_t); opts = ®edit->active_search; @@ -237,6 +242,13 @@ static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, save_value_name = NULL; search_key = opts->search_key; need_sync = false; + iterate = tree_node_next; + find_item = value_list_find_next_item; + + if (flags & SEARCH_PREV) { + iterate = tree_node_prev; + find_item = value_list_find_prev_item; + } if (opts->search_value) { struct value_item *it; @@ -258,7 +270,7 @@ static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, if (!vitem && (flags & SEARCH_REPEAT)) { if (opts->search_value) { search_key = false; - } else if (!tree_node_next(&node, opts->search_recursive, &rv)) { + } else if (!iterate(&node, opts->search_recursive, &rv)) { beep(); return rv; } @@ -283,10 +295,8 @@ static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, } need_sync = true; } - found_value = value_list_find_next_item(regedit->vl, - vitem, - opts->query, - opts->match); + found_value = find_item(regedit->vl, vitem, opts->query, + opts->match); if (found_value) { found = node; } else { @@ -294,7 +304,7 @@ static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, search_key = opts->search_key; } } - } while (!found && tree_node_next(&node, opts->search_recursive, &rv)); + } while (!found && iterate(&node, opts->search_recursive, &rv)); if (!W_ERROR_IS_OK(rv)) { goto out; @@ -589,7 +599,7 @@ static void handle_main_input(struct regedit *regedit, int c) if (rv == DIALOG_OK) { SMB_ASSERT(opts->query != NULL); opts->match = find_substring_nocase; - node = regedit->keys->root; + node = regedit->keys->root->child_head; if (opts->search_case) { opts->match = find_substring; } @@ -602,9 +612,11 @@ static void handle_main_input(struct regedit *regedit, int c) break; } case 'x': - case 'X': regedit_search_repeat(regedit, SEARCH_NEXT); break; + case 'X': + regedit_search_repeat(regedit, SEARCH_PREV); + break; case '\t': regedit->tree_input = !regedit->tree_input; print_heading(regedit); diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c index cf071de..4c68fea 100644 --- a/source3/utils/regedit_treeview.c +++ b/source3/utils/regedit_treeview.c @@ -342,6 +342,30 @@ static WERROR next_depth_first(struct tree_node **node) return rv; } +static WERROR prev_depth_first(struct tree_node **node) +{ + WERROR rv = WERR_OK; + + SMB_ASSERT(node != NULL && *node != NULL); + + if ((*node)->previous) { + *node = (*node)->previous; + while (tree_node_has_children(*node)) { + rv = tree_node_load_children(*node); + if (W_ERROR_IS_OK(rv)) { + SMB_ASSERT((*node)->child_head != NULL); + *node = tree_node_last((*node)->child_head); + } + } + } else if (!tree_node_is_top_level(*node)) { + *node = (*node)->parent; + } else { + *node = NULL; + } + + return rv; +} + bool tree_node_next(struct tree_node **node, bool depth, WERROR *err) { *err = WERR_OK; @@ -359,6 +383,23 @@ bool tree_node_next(struct tree_node **node, bool depth, WERROR *err) return *node != NULL && W_ERROR_IS_OK(*err); } +bool tree_node_prev(struct tree_node **node, bool depth, WERROR *err) +{ + *err = WERR_OK; + + if (*node == NULL) { + return false; + } + + if (depth) { + *err = prev_depth_first(node); + } else { + *node = (*node)->previous; + } + + 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 ba0cd0a..4b892bb 100644 --- a/source3/utils/regedit_treeview.h +++ b/source3/utils/regedit_treeview.h @@ -61,6 +61,7 @@ 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); +bool tree_node_prev(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); -- 2.1.1 From d770b0414cc8b60485b2d81b1f4a7ce4b6eaebda Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Wed, 6 Aug 2014 20:15:38 -0700 Subject: [PATCH 36/45] regedit: adjust some variable names to make them more distinct Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 58db858619339084c86a02fd226eaff0854c730f) --- source3/utils/regedit_dialog.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 20d7651..6ab550b 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1223,7 +1223,7 @@ static void buttons_unhighlight(struct dialog_section_buttons *buttons) static void buttons_highlight(struct dialog_section_buttons *buttons) { - struct button_spec *button = &buttons->spec[buttons->current_button]; + struct button_spec *spec = &buttons->spec[buttons->current_button]; short pair; attr_t attr; @@ -1235,9 +1235,9 @@ static void buttons_highlight(struct dialog_section_buttons *buttons) */ (wattr_get)(buttons->section.window, &attr, &pair, NULL); mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); - mvwchgat(buttons->section.window, 0, button->col, - strlen(button->label), A_REVERSE, pair, NULL); - wmove(buttons->section.window, 0, button->col + 2); + mvwchgat(buttons->section.window, 0, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(buttons->section.window, 0, spec->col + 2); wcursyncup(buttons->section.window); wnoutrefresh(buttons->section.window); } @@ -1271,8 +1271,8 @@ static WERROR buttons_create(struct dialog *dia, nbuttons = talloc_array_length(buttons->spec); for (i = 0; i < nbuttons; ++i) { - struct button_spec *button = &buttons->spec[i]; - mvwaddstr(section->window, 0, button->col, button->label); + struct button_spec *spec = &buttons->spec[i]; + mvwaddstr(section->window, 0, spec->col, spec->label); } buttons->current_button = 0; @@ -1428,7 +1428,7 @@ static void options_unhighlight(struct dialog_section_options *options) static void options_highlight(struct dialog_section_options *options) { - struct option_spec *option = &options->spec[options->current_option]; + struct option_spec *spec = &options->spec[options->current_option]; short pair; attr_t attr; size_t row; @@ -1443,9 +1443,9 @@ static void options_highlight(struct dialog_section_options *options) for (row = 0; row < options->section.nlines; ++row) { mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); } - mvwchgat(options->section.window, option->row, option->col, - strlen(option->label), A_REVERSE, pair, NULL); - wmove(options->section.window, option->row, option->col + 4); + mvwchgat(options->section.window, spec->row, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(options->section.window, spec->row, spec->col + 4); wcursyncup(options->section.window); wnoutrefresh(options->section.window); } @@ -1456,12 +1456,12 @@ static void options_render_state(struct dialog_section_options *options) noptions = talloc_array_length(options->spec); for (i = 0; i < noptions; ++i) { - struct option_spec *option = &options->spec[i]; + struct option_spec *spec = &options->spec[i]; char c = ' '; - if (*option->state) + if (*spec->state) c = 'x'; mvwaddch(options->section.window, - option->row, option->col + 1, c); + spec->row, spec->col + 1, c); wnoutrefresh(options->section.window); } } @@ -1487,7 +1487,7 @@ static bool options_highlight_previous(struct dialog_section_options *options) } static WERROR options_create(struct dialog *dia, - struct dialog_section *section) + struct dialog_section *section) { size_t i, noptions; struct dialog_section_options *options = @@ -1495,9 +1495,9 @@ static WERROR options_create(struct dialog *dia, noptions = talloc_array_length(options->spec); for (i = 0; i < noptions; ++i) { - struct option_spec *option = &options->spec[i]; - mvwaddstr(section->window, option->row, option->col, - option->label); + struct option_spec *spec = &options->spec[i]; + mvwaddstr(section->window, spec->row, spec->col, + spec->label); } options->current_option = 0; @@ -1528,7 +1528,7 @@ static void options_on_input(struct dialog *dia, struct dialog_section *section, talloc_get_type_abort(section, struct dialog_section_options); if (c == ' ') { - struct option_spec *option = &options->spec[options->current_option]; + struct option_spec *spec = &options->spec[options->current_option]; if (options->single_select) { size_t i, noptions; noptions = talloc_array_length(options->spec); @@ -1536,7 +1536,7 @@ static void options_on_input(struct dialog *dia, struct dialog_section *section, *(options->spec[i].state) = false; } } - *option->state = !*option->state; + *spec->state = !*spec->state; options_unhighlight(options); options_render_state(options); options_highlight(options); -- 2.1.1 From d1460c8e6e6fb3dea6372ccf3af3cf358e3be361 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 8 Aug 2014 19:58:03 -0700 Subject: [PATCH 37/45] regedit: handle DEL key in text fields Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 00765544af53cdfcc6f602b1825f14b97b96beb1) --- source3/utils/regedit_dialog.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 6ab550b..b807c4e 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -777,6 +777,9 @@ static void text_field_on_input(struct dialog *dia, case KEY_BACKSPACE: form_driver(text_field->form, REQ_DEL_PREV); break; + case KEY_DC: + form_driver(text_field->form, REQ_DEL_CHAR); + break; default: form_driver(text_field->form, c); break; -- 2.1.1 From 8693571eb93e5f440e4661cedaf714dc935825bc Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 8 Aug 2014 22:25:50 -0700 Subject: [PATCH 38/45] regedit: don't expand single line text field buffer with cursor movement Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 5bbed3d93dfddb0386401a032dc3321bc7f67e33) --- source3/utils/regedit_dialog.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index b807c4e..b5ab400 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -721,8 +721,18 @@ struct dialog_section_text_field { unsigned opts; FIELD *field[2]; FORM *form; + int length; }; +static int get_cursor_col(struct dialog_section_text_field *field) +{ + int col; + + col = field->form->curcol + field->form->begincol; + + return col; +} + static WERROR text_field_create(struct dialog *dia, struct dialog_section *section) { @@ -775,12 +785,20 @@ static void text_field_on_input(struct dialog *dia, switch (c) { case KEY_BACKSPACE: + if (text_field->length) { + text_field->length--; + } form_driver(text_field->form, REQ_DEL_PREV); break; + case '\x7f': case KEY_DC: + if (text_field->length) { + text_field->length--; + } form_driver(text_field->form, REQ_DEL_CHAR); break; default: + text_field->length++; form_driver(text_field->form, c); break; } @@ -829,7 +847,10 @@ static bool text_field_on_right(struct dialog *dia, struct dialog_section_text_field *text_field = talloc_get_type_abort(section, struct dialog_section_text_field); - form_driver(text_field->form, REQ_RIGHT_CHAR); + if (section->nlines > 1 || + get_cursor_col(text_field) < text_field->length) { + form_driver(text_field->form, REQ_RIGHT_CHAR); + } return true; } @@ -841,6 +862,7 @@ static enum dialog_action text_field_on_enter(struct dialog *dia, talloc_get_type_abort(section, struct dialog_section_text_field); if (section->nlines > 1) { + text_field->length += text_field->form->cols; form_driver(text_field->form, REQ_NEW_LINE); return DIALOG_IGNORE; } @@ -911,6 +933,7 @@ void dialog_section_text_field_set(struct dialog_section *section, struct dialog_section_text_field *text_field = talloc_get_type_abort(section, struct dialog_section_text_field); + text_field->length = strlen(s); set_field_buffer(text_field->field[0], 0, s); } -- 2.1.1 From d757550db0e7a99be8621611c9b9b3745454ab31 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 8 Aug 2014 23:36:48 -0700 Subject: [PATCH 39/45] regedit: add a number input box Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 8acb87e42e8004c6a2f04085825fadad3cfbc5b2) --- source3/utils/regedit_dialog.c | 124 +++++++++++++++++++++++++++++++++++++---- source3/utils/regedit_dialog.h | 4 ++ 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index b5ab400..dff44ea 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1675,11 +1675,75 @@ fail: } -int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, - const char *msg, ...) +enum input_type { + DLG_IN_LONG, + DLG_IN_ULONG, + DLG_IN_STR, +}; + +struct input_req { + TALLOC_CTX *ctx; + enum input_type type; + union { + void *out; + unsigned long *out_ulong; + long *out_long; + const char **out_str; + } out; +}; + +static bool input_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct input_req *req = arg; + struct dialog_section *data; + unsigned long long out_ulong; + long long out_long; + + data = dialog_find_section(dia, "input"); + + switch (req->type) { + case DLG_IN_LONG: + if (!dialog_section_text_field_get_int(data, &out_long)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number."); + return false; + } + if (out_long < LONG_MIN || out_long > LONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_long = out_long; + break; + case DLG_IN_ULONG: + if (!dialog_section_text_field_get_uint(data, &out_ulong)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number greater than zero."); + return false; + } + if (out_ulong > ULONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_ulong = out_ulong; + break; + case DLG_IN_STR: + *req->out.out_str = dialog_section_text_field_get(req->ctx, data); + break; + } + + return true; +} + +static int dialog_input_internal(TALLOC_CTX *ctx, void *output, + enum input_type type, + const char *title, + const char *msg, va_list ap) { - va_list ap; WERROR err; + struct input_req req; enum dialog_action action; struct dialog *dia; struct dialog_section *section; @@ -1689,10 +1753,14 @@ int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, { 0 } }; + req.ctx = ctx; + req.type = type; + req.out.out = output; + *req.out.out_str = NULL; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); - va_start(ap, msg); + dialog_set_submit_cb(dia, input_on_submit, &req); section = dialog_section_label_new_va(dia, msg, ap); - va_end(ap); dialog_append_section(dia, section); section = dialog_section_hsep_new(dia, ' '); dialog_append_section(dia, section); @@ -1708,18 +1776,50 @@ int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, dialog_create(dia); dialog_show(dia); dialog_modal_loop(dia, &err, &action); - - *output = NULL; - if (action == DIALOG_OK) { - section = dialog_find_section(dia, "input"); - *output = dialog_section_text_field_get(ctx, section); - } - talloc_free(dia); return action; } +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, + const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_STR, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_ULONG, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_long(TALLOC_CTX *ctx, long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_LONG, title, msg, ap); + va_end(ap); + + return rv; +} + int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, const char *title, const char *msg, ...) { diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index 18b9b98..283e0e2 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -210,6 +210,10 @@ int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, const char *msg, ...); +int dialog_input_long(TALLOC_CTX *ctx, long *output, + const char *title, const char *msg, ...); +int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output, + const char *title, const char *msg, ...); struct registry_key; struct value_item; -- 2.1.1 From 4395aef745b03179a47841aecf9fca216036498b Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Fri, 8 Aug 2014 23:55:25 -0700 Subject: [PATCH 40/45] regedit: add a button to resize hexedit buffer Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 4badab3a6062a473b9651564ce43bb51c63457c1) --- source3/utils/regedit_dialog.c | 48 ++++++++++++++++++++++++++++++++++++++++-- source3/utils/regedit_dialog.h | 2 ++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index dff44ea..6f0aeda 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1224,6 +1224,23 @@ void dialog_section_hexedit_get_buf(struct dialog_section *section, *size = hexedit_get_buf_len(hexedit->buf); } +WERROR dialog_section_hexedit_resize(struct dialog_section *section, + size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + rv = hexedit_resize_buffer(hexedit->buf, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + } + + return rv; +} + + /* button box */ struct dialog_section_buttons { struct dialog_section section; @@ -2030,6 +2047,22 @@ static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, } +static enum dialog_action edit_on_resize(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section *data; + unsigned long size; + int rv; + + data = dialog_find_section(dia, "data"); + rv = dialog_input_ulong(dia, &size, "Resize", "Enter size of buffer"); + if (rv == DIALOG_OK) { + dialog_section_hexedit_resize(data, size); + } + + return DIALOG_IGNORE; +} + int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, uint32_t type, const struct value_item *vitem, bool force_binary, WERROR *err, @@ -2039,11 +2072,18 @@ int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, struct dialog *dia; struct dialog_section *section; struct edit_req edit; - struct button_spec spec[] = { + struct button_spec buttons[] = { {.label = "OK", .action = DIALOG_OK}, {.label = "Cancel", .action = DIALOG_CANCEL}, { 0 } }; + struct button_spec buttons_hexedit[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Resize Buffer", .on_enter = edit_on_resize}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + edit.key = key; edit.vitem = vitem; @@ -2100,7 +2140,11 @@ int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, section = dialog_section_hsep_new(dia, 0); dialog_append_section(dia, section); - section = dialog_section_buttons_new(dia, spec); + if (edit.mode == REG_BINARY) { + section = dialog_section_buttons_new(dia, buttons_hexedit); + } else { + section = dialog_section_buttons_new(dia, buttons); + } dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); dialog_append_section(dia, section); diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index 283e0e2..5c7c84a 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -175,6 +175,8 @@ WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, const void *data, size_t size); void dialog_section_hexedit_get_buf(struct dialog_section *section, const void **data, size_t *size); +WERROR dialog_section_hexedit_resize(struct dialog_section *section, + size_t size); struct button_spec { const char *label; -- 2.1.1 From 32a18fd381eab2935ac3f26063dff9e4abda94c0 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sat, 9 Aug 2014 13:39:59 -0700 Subject: [PATCH 41/45] regedit: grow hexedit buffer as the user types Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 3c6541c44e2bfca2d1dfb7a971064d2c7143106f) --- source3/utils/regedit_dialog.c | 2 +- source3/utils/regedit_hexedit.c | 68 +++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 6f0aeda..3dc18dd 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1056,7 +1056,7 @@ struct dialog_section_hexedit { struct hexedit *buf; }; -#define HEXEDIT_MIN_SIZE 16 +#define HEXEDIT_MIN_SIZE 1 static WERROR hexedit_create(struct dialog *dia, struct dialog_section *section) { diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c index bdc4255..8a7c8a1 100644 --- a/source3/utils/regedit_hexedit.c +++ b/source3/utils/regedit_hexedit.c @@ -223,17 +223,6 @@ static int offset_to_hex_col(size_t pos) return -1; } -static bool scroll_down(struct hexedit *buf) -{ - if (buf->offset + bytes_per_screen(buf->win) >= buf->len) { - return false; - } - - buf->offset += BYTES_PER_LINE; - - return true; -} - static bool scroll_up(struct hexedit *buf) { if (buf->offset == 0) { @@ -247,17 +236,34 @@ static bool scroll_up(struct hexedit *buf) static void cursor_down(struct hexedit *buf) { + size_t space; + bool need_refresh = false; + + space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE; + if (space > buf->len) { + return; + } + if (buf->cursor_y + 1 == max_rows(buf->win)) { - if (scroll_down(buf)) { - hexedit_refresh(buf); - } + buf->offset += BYTES_PER_LINE; + need_refresh = true; } else { - if (buf->cursor_offset + BYTES_PER_LINE >= buf->len) { - return; - } buf->cursor_y++; } + if (buf->cursor_offset + BYTES_PER_LINE > buf->len) { + buf->nibble = 0; + buf->cursor_offset = buf->len; + buf->cursor_line_offset = buf->len - space; + if (buf->cursor_x >= ASCII_COL) { + buf->cursor_x = ASCII_COL + buf->cursor_line_offset; + } else { + buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset); + } + } + if (need_refresh) { + hexedit_refresh(buf); + } calc_cursor_offset(buf); } @@ -308,7 +314,7 @@ static void cursor_left(struct hexedit *buf) } else if (buf->cursor_x == ASCII_COL) { size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE; if (off + 7 >= buf->len) { - size_t lastpos = buf->len - off - 1; + size_t lastpos = buf->len - off; buf->cursor_x = offset_to_hex_col(lastpos) + 1; buf->cursor_line_offset = lastpos; } else { @@ -342,7 +348,7 @@ static void cursor_right(struct hexedit *buf) return; } if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) && - buf->cursor_offset + 1 == buf->len) { + buf->cursor_offset == buf->len) { if (buf->cursor_x < ASCII_COL) { new_x = ASCII_COL; buf->cursor_line_offset = 0; @@ -383,6 +389,10 @@ static void do_edit(struct hexedit *buf, int c) return; } + if (buf->cursor_offset == buf->len) { + hexedit_resize_buffer(buf, buf->len + 1); + } + byte = buf->data + buf->cursor_offset; if (buf->cursor_x >= ASCII_COL) { @@ -395,7 +405,11 @@ static void do_edit(struct hexedit *buf, int c) } mvwaddch(buf->win, buf->cursor_y, ASCII_COL + buf->cursor_line_offset, c); - cursor_right(buf); + if (buf->cursor_x + 1 != ASCII_COL_END) { + cursor_right(buf); + } else { + cursor_down(buf); + } } else { if (!isxdigit(c)) { return; @@ -423,8 +437,12 @@ static void do_edit(struct hexedit *buf, int c) if (buf->cursor_x + 1 != HEX_COL2_END) { cursor_right(buf); + } else { + cursor_down(buf); } } + + hexedit_refresh(buf); } void hexedit_driver(struct hexedit *buf, int c) @@ -458,7 +476,7 @@ WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz) { /* reset the cursor if it'll be out of bounds after the resize */ - if (buf->cursor_offset >= newsz) { + if (buf->cursor_offset > newsz) { buf->cursor_y = 0; buf->cursor_x = HEX_COL1; buf->offset = 0; @@ -470,12 +488,16 @@ WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz) if (newsz > buf->len) { if (newsz > buf->alloc_size) { uint8_t *d; - d = talloc_realloc(buf, buf->data, uint8_t, newsz); + buf->alloc_size *= 2; + if (newsz > buf->alloc_size) { + buf->alloc_size = newsz; + } + d = talloc_realloc(buf, buf->data, uint8_t, + buf->alloc_size); if (d == NULL) { return WERR_NOMEM; } buf->data = d; - buf->alloc_size = newsz; } memset(buf->data + buf->len, '\0', newsz - buf->len); buf->len = newsz; -- 2.1.1 From 3024abd70548dc8f569043b6cc686aad973de82f Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sat, 9 Aug 2014 17:11:00 -0700 Subject: [PATCH 42/45] regedit: handle del and backspace in hexeditor Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit b96c43219480c442d0264834101e5bee675e3b04) --- source3/utils/regedit_dialog.c | 6 +++- source3/utils/regedit_hexedit.c | 72 +++++++++++++++++++++++++++++++++++------ source3/utils/regedit_hexedit.h | 4 ++- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index 3dc18dd..d0b486f 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -1093,7 +1093,11 @@ static void hexedit_on_input(struct dialog *dia, switch (c) { case KEY_BACKSPACE: - // FIXME hexedit_driver(hexedit->buf, c); + hexedit_driver(hexedit->buf, HE_BACKSPACE); + break; + case '\x7f': + case KEY_DC: + hexedit_driver(hexedit->buf, HE_DELETE); break; default: hexedit_driver(hexedit->buf, c); diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c index 8a7c8a1..377eef9 100644 --- a/source3/utils/regedit_hexedit.c +++ b/source3/utils/regedit_hexedit.c @@ -125,11 +125,12 @@ void hexedit_set_cursor(struct hexedit *buf) wmove(buf->win, max_rows(buf->win), 0); wattron(buf->win, A_REVERSE | A_STANDOUT); wclrtoeol(buf->win); - if (buf->len) { + if (buf->cursor_offset < buf->len) { wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len, buf->cursor_offset, buf->data[buf->cursor_offset]); } else { - wprintw(buf->win, "Len:%lu (empty)", buf->len); + wprintw(buf->win, "Len:%lu Off:%lu", buf->len, + buf->cursor_offset); } wattroff(buf->win, A_REVERSE | A_STANDOUT); wmove(buf->win, buf->cursor_y, buf->cursor_x); @@ -341,9 +342,6 @@ static void cursor_right(struct hexedit *buf) { int new_x = buf->cursor_x + 1; - if (buf->len == 0) { - return; - } if (new_x == ASCII_COL_END) { return; } @@ -385,10 +383,6 @@ static void do_edit(struct hexedit *buf, int c) { uint8_t *byte; - if (buf->len == 0) { - return; - } - if (buf->cursor_offset == buf->len) { hexedit_resize_buffer(buf, buf->len + 1); } @@ -445,6 +439,60 @@ static void do_edit(struct hexedit *buf, int c) hexedit_refresh(buf); } +static void erase_at(struct hexedit *buf, size_t pos) +{ + if (pos >= buf->len) { + return; + } + + if (pos < buf->len - 1) { + /* squeeze the character out of the buffer */ + uint8_t *p = buf->data + pos; + uint8_t *end = buf->data + buf->len; + memmove(p, p + 1, end - p - 1); + } + + buf->len--; + hexedit_refresh(buf); +} + +static void do_backspace(struct hexedit *buf) +{ + size_t off; + bool erase = true; + + if (buf->cursor_offset == 0) { + return; + } + + off = buf->cursor_offset; + if (buf->cursor_x == ASCII_COL) { + cursor_up(buf); + buf->cursor_line_offset = 7; + buf->cursor_x = ASCII_COL_END - 1; + calc_cursor_offset(buf); + } else if (buf->cursor_x == HEX_COL1) { + cursor_up(buf); + buf->cursor_line_offset = 7; + buf->cursor_x = HEX_COL2_END - 1; + buf->nibble = 1; + calc_cursor_offset(buf); + } else { + if (buf->cursor_x < ASCII_COL && buf->nibble) { + erase = false; + } + cursor_left(buf); + } + if (erase) { + erase_at(buf, off - 1); + } +} + +static void do_delete(struct hexedit *buf) +{ + erase_at(buf, buf->cursor_offset); +} + void hexedit_driver(struct hexedit *buf, int c) { switch (c) { @@ -464,6 +512,12 @@ void hexedit_driver(struct hexedit *buf, int c) break; case HE_CURSOR_PGDN: break; + case HE_BACKSPACE: + do_backspace(buf); + break; + case HE_DELETE: + do_delete(buf); + break; default: do_edit(buf, c & 0xff); break; diff --git a/source3/utils/regedit_hexedit.h b/source3/utils/regedit_hexedit.h index dc13c21..dfbe27a 100644 --- a/source3/utils/regedit_hexedit.h +++ b/source3/utils/regedit_hexedit.h @@ -28,7 +28,9 @@ enum { HE_CURSOR_LEFT = 0x1200, HE_CURSOR_RIGHT = 0x1300, HE_CURSOR_PGUP = 0x1400, - HE_CURSOR_PGDN = 0x1500 + HE_CURSOR_PGDN = 0x1500, + HE_BACKSPACE = 0x1600, + HE_DELETE = 0x1700, }; #define LINE_WIDTH 44 -- 2.1.1 From ae12c3a9f0e782ff56bd7abf79c9ac25fcbf7015 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Sun, 10 Aug 2014 18:26:14 -0700 Subject: [PATCH 43/45] regedit: handle pgup/pgdn/home/end keys on lists Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 3b80f256bac0842f4470f9d5361426d7cff88ea9) --- source3/utils/regedit.c | 28 ++++++++++++++++++ source3/utils/regedit_list.c | 67 ++++++++++++++++++++++++++++++++++++++++++-- source3/utils/regedit_list.h | 6 +++- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 34a63d7..9cb4fca 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -378,6 +378,22 @@ static void handle_tree_input(struct regedit *regedit, int c) tree_view_driver(regedit->keys, ML_CURSOR_UP); load_values(regedit); break; + case KEY_NPAGE: + tree_view_driver(regedit->keys, ML_CURSOR_PGDN); + load_values(regedit); + break; + case KEY_PPAGE: + tree_view_driver(regedit->keys, ML_CURSOR_PGUP); + load_values(regedit); + break; + case KEY_HOME: + tree_view_driver(regedit->keys, ML_CURSOR_HOME); + load_values(regedit); + break; + case KEY_END: + tree_view_driver(regedit->keys, ML_CURSOR_END); + load_values(regedit); + break; case '\n': case KEY_ENTER: case KEY_RIGHT: @@ -476,6 +492,18 @@ static void handle_value_input(struct regedit *regedit, int c) case KEY_UP: value_list_driver(regedit->vl, ML_CURSOR_UP); break; + case KEY_NPAGE: + value_list_driver(regedit->vl, ML_CURSOR_PGDN); + break; + case KEY_PPAGE: + value_list_driver(regedit->vl, ML_CURSOR_PGUP); + break; + case KEY_HOME: + value_list_driver(regedit->vl, ML_CURSOR_HOME); + break; + case KEY_END: + value_list_driver(regedit->vl, ML_CURSOR_END); + break; case 'b': case 'B': binmode = true; diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c index ac4e240..c2b3705 100644 --- a/source3/utils/regedit_list.c +++ b/source3/utils/regedit_list.c @@ -405,21 +405,33 @@ WERROR multilist_set_data(struct multilist *list, const void *data) return WERR_OK; } -static void fix_start_row(struct multilist *list) +static int get_window_height(struct multilist *list) { int height; - /* adjust start_row so that the cursor appears on the screen */ - height = list->window_height; if (list->cb->get_column_header) { height--; } + + return height; +} + +static void fix_start_row(struct multilist *list) +{ + int height; + + /* adjust start_row so that the cursor appears on the screen */ + + height = get_window_height(list); if (list->cursor_row < list->start_row) { list->start_row = list->cursor_row; } else if (list->cursor_row >= list->start_row + height) { list->start_row = list->cursor_row - height + 1; } + if (list->nrows > height && list->nrows - list->start_row < height) { + list->start_row = list->nrows - height; + } } WERROR multilist_set_window(struct multilist *list, WINDOW *window) @@ -457,6 +469,10 @@ void multilist_refresh(struct multilist *list) { int window_start_row, height; + if (list->nrows == 0) { + return; + } + /* copy from pad, starting at start_row, to the window, accounting for the column header (if present). */ height = MIN(list->window_height, list->nrows); @@ -474,6 +490,7 @@ void multilist_refresh(struct multilist *list) void multilist_driver(struct multilist *list, int c) { + unsigned page; const void *tmp; if (list->nrows == 0) { @@ -497,6 +514,50 @@ void multilist_driver(struct multilist *list, int c) list->cursor_row++; tmp = data_get_next_row(list, list->current_row); break; + case ML_CURSOR_PGUP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->cursor_row) { + list->cursor_row = 0; + } else { + list->cursor_row -= page; + list->start_row -= page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_PGDN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->nrows - list->cursor_row - 1) { + list->cursor_row = list->nrows - 1; + } else { + list->cursor_row += page; + list->start_row += page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_HOME: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row = 0; + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_END: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row = list->nrows - 1; + tmp = data_get_row_n(list, list->cursor_row); + break; } SMB_ASSERT(tmp); diff --git a/source3/utils/regedit_list.h b/source3/utils/regedit_list.h index 4b1840a..abd6ffd 100644 --- a/source3/utils/regedit_list.h +++ b/source3/utils/regedit_list.h @@ -69,7 +69,11 @@ void multilist_refresh(struct multilist *list); enum { ML_CURSOR_UP, - ML_CURSOR_DOWN + ML_CURSOR_DOWN, + ML_CURSOR_PGUP, + ML_CURSOR_PGDN, + ML_CURSOR_HOME, + ML_CURSOR_END }; void multilist_driver(struct multilist *list, int c); const void *multilist_get_current_row(struct multilist *list); -- 2.1.1 From 43060387ad1459f9ba616eb9166d48cc29509004 Mon Sep 17 00:00:00 2001 From: Chris Davis Date: Thu, 7 Aug 2014 06:17:28 -0700 Subject: [PATCH 44/45] regedit: print error msg if opening registry fails Signed-off-by: Chris Davis Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam (cherry picked from commit 61cc5185e5ccf1bb6d1201063ac3a62e3b7fcfbb) --- source3/utils/regedit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index 9cb4fca..fe2caf6 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -796,6 +796,8 @@ int main(int argc, const char **argv) rv = reg_open_samba3(frame, &ctx); if (!W_ERROR_IS_OK(rv)) { + fprintf(stderr, "Unable to open registry: %s\n", + win_errstr(rv)); TALLOC_FREE(frame); return 1; -- 2.1.1 From 168f63bdca19e7d7a0d8e1ad5474f90f491d9c50 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Wed, 1 Oct 2014 11:36:36 +0200 Subject: [PATCH 45/45] regedit: remove an old comment Signed-off-by: Michael Adam Reviewed-by: Andreas Schneider Autobuild-User(master): Michael Adam Autobuild-Date(master): Wed Oct 1 16:55:05 CEST 2014 on sn-devel-104 (cherry picked from commit fa9630e368df2446c5893b2fe51bada7a38760aa) --- source3/utils/regedit.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c index fe2caf6..bff23ec 100644 --- a/source3/utils/regedit.c +++ b/source3/utils/regedit.c @@ -792,8 +792,6 @@ int main(int argc, const char **argv) exit(1); } - /* some simple tests */ - rv = reg_open_samba3(frame, &ctx); if (!W_ERROR_IS_OK(rv)) { fprintf(stderr, "Unable to open registry: %s\n", -- 2.1.1