regedit: Use color only when available.
[Samba/id10ts.git] / source3 / utils / regedit.c
blobbd9765e5db830fc3401de4ece91925e39f5368fe
1 /*
2 * Samba Unix/Linux SMB client library
3 * Registry Editor
4 * Copyright (C) Christopher Davis 2012
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "popt_common.h"
22 #include "lib/util/data_blob.h"
23 #include "lib/registry/registry.h"
24 #include "regedit.h"
25 #include "regedit_treeview.h"
26 #include "regedit_valuelist.h"
27 #include "regedit_dialog.h"
28 #include <ncurses.h>
29 #include <menu.h>
30 #include <panel.h>
32 #define KEY_START_X 0
33 #define KEY_START_Y 3
34 #define KEY_WIDTH (COLS / 4)
35 #define KEY_HEIGHT (LINES - KEY_START_Y - 2)
36 #define VAL_START_X KEY_WIDTH
37 #define VAL_START_Y 3
38 #define VAL_WIDTH (COLS - KEY_WIDTH)
39 #define VAL_HEIGHT (LINES - VAL_START_Y - 2)
40 #define HEADING_START_Y (KEY_START_Y - 1)
41 #define HELP1_START_Y (LINES - 2)
42 #define HELP1_START_X 0
43 #define HELP1_WIDTH (LINES)
44 #define HELP2_START_Y (LINES - 1)
45 #define HELP2_START_X 0
46 #define HELP2_WIDTH (LINES)
47 #define PATH_START_Y 0
48 #define PATH_START_X 6
49 #define PATH_MAX_Y (COLS - 1)
50 #define PATH_WIDTH (COLS - 6)
51 #define PATH_WIDTH_MAX 1024
53 struct regedit {
54 WINDOW *main_window;
55 WINDOW *path_label;
56 size_t path_len;
57 struct value_list *vl;
58 struct tree_view *keys;
59 bool tree_input;
62 static struct regedit *regedit_main = NULL;
64 static void show_path(struct regedit *regedit)
66 int start_pad = 0;
67 int start_win = PATH_START_X;
69 if (PATH_START_X + regedit->path_len > COLS) {
70 start_pad = 3 + PATH_START_X + regedit->path_len - COLS;
71 mvprintw(PATH_START_Y, start_win, "...");
72 start_win += 3;
74 copywin(regedit->path_label, regedit->main_window, 0, start_pad,
75 PATH_START_Y, start_win, PATH_START_Y, PATH_MAX_Y, false);
78 static void print_path(struct regedit *regedit, struct tree_node *node)
80 regedit->path_len = tree_node_print_path(regedit->path_label, node);
81 show_path(regedit);
84 /* load all available hives */
85 static struct tree_node *load_hives(TALLOC_CTX *mem_ctx,
86 struct registry_context *ctx)
88 const char *hives[] = {
89 "HKEY_CLASSES_ROOT",
90 "HKEY_CURRENT_USER",
91 "HKEY_LOCAL_MACHINE",
92 "HKEY_PERFORMANCE_DATA",
93 "HKEY_USERS",
94 "HKEY_CURRENT_CONFIG",
95 "HKEY_DYN_DATA",
96 "HKEY_PERFORMANCE_TEXT",
97 "HKEY_PERFORMANCE_NLSTEXT",
98 NULL
100 struct tree_node *root, *prev, *node;
101 struct registry_key *key;
102 WERROR rv;
103 size_t i;
105 root = NULL;
106 prev = NULL;
108 for (i = 0; hives[i] != NULL; ++i) {
109 rv = reg_get_predefined_key_by_name(ctx, hives[i], &key);
110 if (!W_ERROR_IS_OK(rv)) {
111 continue;
114 node = tree_node_new(mem_ctx, NULL, hives[i], key);
115 if (node == NULL) {
116 return NULL;
119 if (root == NULL) {
120 root = node;
122 if (prev) {
123 tree_node_append(prev, node);
125 prev = node;
128 return root;
131 static void print_help(struct regedit *regedit)
133 const char *khelp = "[n] New Key [s] New Subkey [d] Del Key "
134 "[LEFT] Ascend [RIGHT] Descend";
135 const char *vhelp = "[n] New Value [d] Del Value [ENTER] Edit "
136 "[b] Edit binary";
137 const char *msg = "KEYS";
138 const char *help = khelp;
139 const char *genhelp = "[TAB] Switch sections [q] Quit regedit "
140 "[UP] List up [DOWN] List down";
141 int i, pad;
143 if (!regedit->tree_input) {
144 msg = "VALUES";
145 help = vhelp;
148 move(HELP1_START_Y, HELP1_START_X);
149 clrtoeol();
150 attron(A_REVERSE);
151 mvaddstr(HELP1_START_Y, HELP1_START_X, help);
152 pad = COLS - strlen(msg) - strlen(help);
153 for (i = 0; i < pad; ++i) {
154 addch(' ');
156 attron(A_BOLD);
157 addstr(msg);
158 attroff(A_REVERSE | A_BOLD);
160 move(HELP2_START_Y, HELP2_START_X);
161 clrtoeol();
162 mvaddstr(HELP2_START_Y, HELP2_START_X, genhelp);
165 static void print_heading(struct regedit *regedit)
167 move(HEADING_START_Y, 0);
168 clrtoeol();
170 if (regedit->tree_input) {
171 attron(A_REVERSE);
172 } else {
173 attroff(A_REVERSE);
175 mvprintw(HEADING_START_Y, KEY_START_X, "Key");
176 attroff(A_REVERSE);
178 if (!regedit->tree_input) {
179 attron(A_REVERSE);
180 } else {
181 attroff(A_REVERSE);
183 mvprintw(HEADING_START_Y, VAL_START_X, "Value");
184 attroff(A_REVERSE);
186 print_help(regedit);
189 static void load_values(struct regedit *regedit)
191 struct tree_node *node;
193 node = item_userptr(current_item(regedit->keys->menu));
194 value_list_load(regedit->vl, node->key);
197 static void add_reg_key(struct regedit *regedit, struct tree_node *node,
198 bool subkey)
200 char *name;
201 const char *msg;
203 if (!subkey && !node->parent) {
204 return;
207 msg = "Enter name of new key";
208 if (subkey) {
209 msg = "Enter name of new subkey";
211 dialog_input(regedit, &name, "New Key", msg);
212 if (name) {
213 WERROR rv;
214 struct registry_key *new_key;
215 struct tree_node *new_node;
216 struct tree_node *list;
217 struct tree_node *parent;
219 if (subkey) {
220 parent = node;
221 list = node->child_head;
222 } else {
223 parent = node->parent;
224 list = tree_node_first(node);
225 SMB_ASSERT(list != NULL);
227 rv = reg_key_add_name(regedit, parent->key, name,
228 NULL, NULL, &new_key);
229 if (W_ERROR_IS_OK(rv)) {
230 /* The list of subkeys may not be present in
231 cache yet, so if not, don't bother allocating
232 a new node for the key. */
233 if (list) {
234 new_node = tree_node_new(parent, parent,
235 name, new_key);
236 SMB_ASSERT(new_node);
237 tree_node_append_last(list, new_node);
240 list = tree_node_first(node);
241 tree_view_clear(regedit->keys);
242 tree_view_update(regedit->keys, list);
243 } else {
244 dialog_notice(regedit, DIA_ALERT, "New Key",
245 "Failed to create key.");
247 talloc_free(name);
251 static void handle_tree_input(struct regedit *regedit, int c)
253 struct tree_node *node;
255 switch (c) {
256 case KEY_DOWN:
257 menu_driver(regedit->keys->menu, REQ_DOWN_ITEM);
258 load_values(regedit);
259 break;
260 case KEY_UP:
261 menu_driver(regedit->keys->menu, REQ_UP_ITEM);
262 load_values(regedit);
263 break;
264 case '\n':
265 case KEY_ENTER:
266 case KEY_RIGHT:
267 node = item_userptr(current_item(regedit->keys->menu));
268 if (node && tree_node_has_children(node)) {
269 tree_node_load_children(node);
270 print_path(regedit, node->child_head);
271 tree_view_update(regedit->keys, node->child_head);
272 value_list_load(regedit->vl, node->child_head->key);
274 break;
275 case KEY_LEFT:
276 node = item_userptr(current_item(regedit->keys->menu));
277 if (node && node->parent) {
278 print_path(regedit, node->parent);
279 node = tree_node_first(node->parent);
280 tree_view_update(regedit->keys, node);
281 value_list_load(regedit->vl, node->key);
283 break;
284 case 'n':
285 case 'N':
286 node = item_userptr(current_item(regedit->keys->menu));
287 add_reg_key(regedit, node, false);
288 break;
289 case 's':
290 case 'S':
291 node = item_userptr(current_item(regedit->keys->menu));
292 add_reg_key(regedit, node, true);
293 break;
294 case 'd':
295 case 'D': {
296 int sel;
298 node = item_userptr(current_item(regedit->keys->menu));
299 if (!node->parent) {
300 break;
302 sel = dialog_notice(regedit, DIA_CONFIRM,
303 "Delete Key",
304 "Really delete key \"%s\"?",
305 node->name);
306 if (sel == DIALOG_OK) {
307 WERROR rv;
308 struct tree_node *pop;
309 struct tree_node *parent = node->parent;
311 rv = reg_key_del(node, parent->key, node->name);
312 if (W_ERROR_IS_OK(rv)) {
313 tree_view_clear(regedit->keys);
314 pop = tree_node_pop(&node);
315 tree_node_free_recursive(pop);
316 node = parent->child_head;
317 if (node == NULL) {
318 node = tree_node_first(parent);
319 print_path(regedit, node);
321 tree_view_update(regedit->keys, node);
322 value_list_load(regedit->vl, node->key);
323 } else {
324 dialog_notice(regedit, DIA_ALERT, "Delete Key",
325 "Failed to delete key.");
328 break;
332 tree_view_show(regedit->keys);
333 value_list_show(regedit->vl);
336 static void handle_value_input(struct regedit *regedit, int c)
338 struct value_item *vitem;
339 bool binmode = false;
341 switch (c) {
342 case KEY_DOWN:
343 menu_driver(regedit->vl->menu, REQ_DOWN_ITEM);
344 break;
345 case KEY_UP:
346 menu_driver(regedit->vl->menu, REQ_UP_ITEM);
347 break;
348 case 'b':
349 case 'B':
350 binmode = true;
351 /* Falthrough... */
352 case '\n':
353 case KEY_ENTER:
354 vitem = item_userptr(current_item(regedit->vl->menu));
355 if (vitem) {
356 struct tree_node *node;
357 node = item_userptr(current_item(regedit->keys->menu));
358 dialog_edit_value(regedit, node->key, vitem->type,
359 vitem, binmode);
360 value_list_load(regedit->vl, node->key);
362 break;
363 case 'n':
364 case 'N': {
365 int new_type;
366 int sel;
368 sel = dialog_select_type(regedit, &new_type);
369 if (sel == DIALOG_OK) {
370 struct tree_node *node;
371 node = item_userptr(current_item(regedit->keys->menu));
372 dialog_edit_value(regedit, node->key, new_type, NULL,
373 false);
374 value_list_load(regedit->vl, node->key);
376 break;
378 case 'd':
379 case 'D':
380 vitem = item_userptr(current_item(regedit->vl->menu));
381 if (vitem) {
382 int sel;
384 sel = dialog_notice(regedit, DIA_CONFIRM,
385 "Delete Value",
386 "Really delete value \"%s\"?",
387 vitem->value_name);
388 if (sel == DIALOG_OK) {
389 ITEM *it = current_item(regedit->keys->menu);
390 struct tree_node *node = item_userptr(it);
391 reg_del_value(regedit, node->key,
392 vitem->value_name);
393 value_list_load(regedit->vl, node->key);
396 break;
399 value_list_show(regedit->vl);
402 static void handle_main_input(struct regedit *regedit, int c)
404 switch (c) {
405 case '\t':
406 regedit->tree_input = !regedit->tree_input;
407 print_heading(regedit);
408 break;
409 default:
410 if (regedit->tree_input) {
411 handle_tree_input(regedit, c);
412 } else {
413 handle_value_input(regedit, c);
418 int regedit_getch(void)
420 int c;
422 SMB_ASSERT(regedit_main);
424 c = getch();
425 if (c == KEY_RESIZE) {
426 tree_view_resize(regedit_main->keys, KEY_HEIGHT, KEY_WIDTH,
427 KEY_START_Y, KEY_START_X);
428 value_list_resize(regedit_main->vl, VAL_HEIGHT, VAL_WIDTH,
429 VAL_START_Y, VAL_START_X);
430 print_heading(regedit_main);
431 show_path(regedit_main);
434 return c;
437 static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx)
439 struct regedit *regedit;
440 struct tree_node *root;
441 bool colors;
442 int key;
444 initscr();
446 cbreak();
447 noecho();
449 colors = has_colors();
450 if (colors) {
451 start_color();
452 use_default_colors();
455 regedit = talloc_zero(mem_ctx, struct regedit);
456 SMB_ASSERT(regedit != NULL);
457 regedit_main = regedit;
459 regedit->main_window = stdscr;
460 keypad(regedit->main_window, TRUE);
462 mvwprintw(regedit->main_window, 0, 0, "Path: ");
463 regedit->path_label = newpad(1, PATH_WIDTH_MAX);
464 SMB_ASSERT(regedit->path_label);
465 wprintw(regedit->path_label, "/");
466 show_path(regedit_main);
468 root = load_hives(regedit, ctx);
469 SMB_ASSERT(root != NULL);
471 regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH,
472 KEY_START_Y, KEY_START_X);
473 SMB_ASSERT(regedit->keys != NULL);
475 regedit->vl = value_list_new(regedit, VAL_HEIGHT, VAL_WIDTH,
476 VAL_START_Y, VAL_START_X);
477 SMB_ASSERT(regedit->vl != NULL);
479 regedit->tree_input = true;
480 print_heading(regedit);
482 tree_view_show(regedit->keys);
483 menu_driver(regedit->keys->menu, REQ_FIRST_ITEM);
484 load_values(regedit);
485 value_list_show(regedit->vl);
487 update_panels();
488 doupdate();
490 do {
491 key = regedit_getch();
493 handle_main_input(regedit, key);
494 update_panels();
495 doupdate();
496 } while (key != 'q' || key == 'Q');
498 endwin();
501 int main(int argc, char **argv)
503 struct poptOption long_options[] = {
504 POPT_AUTOHELP
505 /* ... */
506 POPT_COMMON_SAMBA
507 POPT_COMMON_CONNECTION
508 POPT_COMMON_CREDENTIALS
509 POPT_TABLEEND
511 int opt;
512 poptContext pc;
513 struct user_auth_info *auth_info;
514 TALLOC_CTX *frame;
515 struct registry_context *ctx;
516 WERROR rv;
518 frame = talloc_stackframe();
520 setup_logging("regedit", DEBUG_DEFAULT_STDERR);
521 lp_set_cmdline("log level", "0");
523 /* process options */
524 auth_info = user_auth_info_init(frame);
525 if (auth_info == NULL) {
526 exit(1);
528 popt_common_set_auth_info(auth_info);
529 pc = poptGetContext("regedit", argc, (const char **)argv, long_options, 0);
531 while ((opt = poptGetNextOpt(pc)) != -1) {
532 /* TODO */
535 if (!lp_load_global(get_dyn_CONFIGFILE())) {
536 DEBUG(0, ("ERROR loading config file...\n"));
537 exit(1);
540 /* some simple tests */
542 rv = reg_open_samba3(frame, &ctx);
543 if (!W_ERROR_IS_OK(rv)) {
544 TALLOC_FREE(frame);
546 return 1;
549 display_window(frame, ctx);
551 TALLOC_FREE(frame);
553 return 0;