regedit: Restore cursor position when user tabs to a field.
[metze/samba/wip.git] / source3 / utils / regedit_dialog.c
1 /*
2  * Samba Unix/Linux SMB client library
3  * Registry Editor
4  * Copyright (C) Christopher Davis 2012
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include "includes.h"
21 #include "regedit.h"
22 #include "regedit_dialog.h"
23 #include "regedit_valuelist.h"
24 #include "regedit_hexedit.h"
25 #include "util_reg.h"
26 #include "lib/registry/registry.h"
27 #include <stdarg.h>
28 #include <form.h>
29
30 static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
31 {
32         char *str;
33
34         str = talloc_strndup(ctx, buf, n);
35
36         if (str) {
37                 trim_string(str, " ", " ");
38         }
39
40         return str;
41 }
42
43 static char *string_trim(TALLOC_CTX *ctx, const char *buf)
44 {
45         char *str;
46
47         str = talloc_strdup(ctx, buf);
48
49         if (str) {
50                 trim_string(str, " ", " ");
51         }
52
53         return str;
54 }
55
56 static int dialog_free(struct dialog *dia)
57 {
58         if (dia->window) {
59                 delwin(dia->window);
60         }
61         if (dia->sub_window) {
62                 delwin(dia->sub_window);
63         }
64         if (dia->panel) {
65                 del_panel(dia->panel);
66         }
67         if (dia->choices) {
68                 unpost_menu(dia->choices);
69                 free_menu(dia->choices);
70         }
71         if (dia->choice_items) {
72                 ITEM **it;
73                 for (it = dia->choice_items; *it != NULL; ++it) {
74                         free_item(*it);
75                 }
76         }
77
78         return 0;
79 }
80
81 struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines,
82                           int ncols, int y, int x)
83 {
84         struct dialog *dia;
85
86         dia = talloc_zero(ctx, struct dialog);
87         if (dia == NULL) {
88                 return NULL;
89         }
90
91         talloc_set_destructor(dia, dialog_free);
92
93         dia->window = newwin(nlines, ncols, y, x);
94         if (dia->window == NULL) {
95                 goto fail;
96         }
97
98         box(dia->window, 0, 0);
99         mvwaddstr(dia->window, 0, 1, title);
100
101         /* body of the dialog within the box outline */
102         dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1);
103         if (dia->sub_window == NULL) {
104                 goto fail;
105         }
106
107         dia->panel = new_panel(dia->window);
108         if (dia->panel == NULL) {
109                 goto fail;
110         }
111
112         return dia;
113
114 fail:
115         talloc_free(dia);
116
117         return NULL;
118
119 }
120
121 static void center_dialog_above_window(int *nlines, int *ncols,
122                                        int *y, int *x)
123 {
124         int centery, centerx;
125
126         centery = LINES / 2;
127         centerx = COLS / 2;
128         *y = 0;
129         *x = 0;
130
131         if (*nlines > LINES) {
132                 *nlines = LINES;
133         }
134         if (*ncols > COLS) {
135                 *ncols = COLS;
136         }
137
138         if (*nlines/2 < centery) {
139                 *y = centery - *nlines / 2;
140         }
141         if (*ncols/2 < centerx) {
142                 *x = centerx - *ncols / 2;
143         }
144 }
145
146 static int dialog_getch(struct dialog *dia)
147 {
148         int c;
149
150         c = regedit_getch();
151
152         if (c == KEY_RESIZE) {
153                 int nlines, ncols, y, x;
154
155                 getmaxyx(dia->window, nlines, ncols);
156                 getbegyx(dia->window, y, x);
157                 if (dia->centered) {
158                         center_dialog_above_window(&nlines, &ncols, &y, &x);
159                 } else {
160                         if (nlines + y > LINES) {
161                                 if (nlines > LINES) {
162                                         y = 0;
163                                 } else {
164                                         y = LINES - nlines;
165                                 }
166                         }
167                         if (ncols + x > COLS) {
168                                 if (ncols > COLS) {
169                                         x = 0;
170                                 } else {
171                                         x = COLS - ncols;
172                                 }
173                         }
174                 }
175                 move_panel(dia->panel, y, x);
176                 doupdate();
177         }
178
179         return c;
180 }
181
182 struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, int nlines,
183                                  int ncols)
184 {
185         struct dialog *dia;
186         int y, x;
187
188         center_dialog_above_window(&nlines, &ncols, &y, &x);
189
190         dia = dialog_new(ctx, title, nlines, ncols, y, x);
191         if (dia) {
192                 dia->centered = true;
193         }
194
195         return dia;
196 }
197
198 struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title,
199                                  const char **choices, int nlines,
200                                  int ncols, int y, int x)
201 {
202         size_t nchoices, i;
203         struct dialog *dia;
204
205         dia = dialog_new(ctx, title, nlines, ncols, y, x);
206         if (dia == NULL) {
207                 return NULL;
208         }
209
210         dia->menu_window = derwin(dia->sub_window, 1, ncols - 3,
211                                   nlines - 3, 0);
212         if (dia->menu_window == NULL) {
213                 goto fail;
214         }
215
216         for (nchoices = 0; choices[nchoices] != NULL; ++nchoices)
217                 ;
218         dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1);
219         if (dia->choice_items == NULL) {
220                 goto fail;
221         }
222         for (i = 0; i < nchoices; ++i) {
223                 char *desc = talloc_strdup(dia, choices[i]);
224                 if (desc == NULL) {
225                         goto fail;
226                 }
227                 dia->choice_items[i] = new_item(desc, desc);
228                 if (dia->choice_items[i] == NULL) {
229                         goto fail;
230                 }
231                 /* store choice index */
232                 set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i);
233         }
234
235         dia->choices = new_menu(dia->choice_items);
236         if (dia->choices == NULL) {
237                 goto fail;
238         }
239
240         set_menu_format(dia->choices, 1, ncols);
241         set_menu_win(dia->choices, dia->sub_window);
242         set_menu_sub(dia->choices, dia->menu_window);
243         menu_opts_off(dia->choices, O_SHOWDESC);
244         set_menu_mark(dia->choices, "* ");
245         post_menu(dia->choices);
246         wmove(dia->sub_window, 0, 0);
247
248         return dia;
249
250 fail:
251         talloc_free(dia);
252
253         return NULL;
254 }
255
256 struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title,
257                                         const char **choices, int nlines,
258                                         int ncols)
259 {
260         int y, x;
261         struct dialog *dia;
262         center_dialog_above_window(&nlines, &ncols, &y, &x);
263
264         dia = dialog_choice_new(ctx, title, choices, nlines, ncols, y, x);
265         if (dia) {
266                 dia->centered = true;
267         }
268
269         return dia;
270 }
271
272 static int handle_menu_input(MENU *menu, int c)
273 {
274         ITEM *item;
275
276         switch (c) {
277         case KEY_LEFT:
278                 menu_driver(menu, REQ_LEFT_ITEM);
279                 break;
280         case KEY_RIGHT:
281                 menu_driver(menu, REQ_RIGHT_ITEM);
282                 break;
283         case KEY_ENTER:
284         case '\n':
285                 item = current_item(menu);
286                 return (int)(uintptr_t)item_userptr(item);
287         }
288
289         return -1;
290 }
291
292 static void handle_form_input(FORM *frm, int c)
293 {
294         switch (c) {
295         case '\n':
296                 form_driver(frm, REQ_NEW_LINE);
297                 break;
298         case KEY_UP:
299                 form_driver(frm, REQ_UP_CHAR);
300                 break;
301         case KEY_DOWN:
302                 form_driver(frm, REQ_DOWN_CHAR);
303                 break;
304         case '\b':
305         case KEY_BACKSPACE:
306                 form_driver(frm, REQ_DEL_PREV);
307                 break;
308         case KEY_LEFT:
309                 form_driver(frm, REQ_LEFT_CHAR);
310                 break;
311         case KEY_RIGHT:
312                 form_driver(frm, REQ_RIGHT_CHAR);
313                 break;
314         default:
315                 form_driver(frm, c);
316                 break;
317         }
318 }
319
320 static int modal_loop(struct dialog *dia)
321 {
322         int c;
323         int selection = -1;
324
325         update_panels();
326         doupdate();
327
328         while (selection == -1) {
329                 c = dialog_getch(dia);
330                 selection = handle_menu_input(dia->choices, c);
331                 update_panels();
332                 doupdate();
333         }
334
335         talloc_free(dia);
336
337         return selection;
338 }
339
340 static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title,
341                                      const char **choices, int nlines,
342                                      const char *msg, va_list ap)
343 {
344         struct dialog *dia;
345         char *str;
346         int width;
347 #define MIN_WIDTH 20
348
349         str = talloc_vasprintf(ctx, msg, ap);
350         if (str == NULL) {
351                 return NULL;
352         }
353
354         width = strlen(str) + 2;
355         if (width < MIN_WIDTH) {
356                 width = MIN_WIDTH;
357         }
358         dia = dialog_choice_center_new(ctx, title, choices, nlines, width);
359         if (dia == NULL) {
360                 return NULL;
361         }
362
363         waddstr(dia->sub_window, str);
364         talloc_free(str);
365
366         return dia;
367 }
368
369 int dialog_input(TALLOC_CTX *ctx, char **output, const char *title,
370                  const char *msg, ...)
371 {
372         va_list ap;
373         struct dialog *dia;
374         const char *choices[] = {
375                 "Ok",
376                 "Cancel",
377                 NULL
378         };
379         FIELD *field[2] = {0};
380         FORM *input;
381         WINDOW *input_win;
382         int y, x;
383         int rv = -1;
384         bool input_section = true;
385
386         va_start(ap, msg);
387         dia = dialog_msg_new(ctx, title, choices, 7, msg, ap);
388         va_end(ap);
389         if (dia == NULL) {
390                 return -1;
391         }
392
393         getmaxyx(dia->sub_window, y, x);
394         input_win = derwin(dia->sub_window, 1, x - 2, 2, 1);
395         if (input_win == NULL) {
396                 goto finish;
397         }
398         field[0] = new_field(1, x - 2, 0, 0, 0, 0);
399         if (field[0] == NULL) {
400                 goto finish;
401         }
402
403         field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
404         set_field_back(field[0], A_REVERSE);
405
406         input = new_form(field);
407         form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD);
408         set_form_win(input, dia->sub_window);
409         set_form_sub(input, input_win);
410         set_current_field(input, field[0]);
411         post_form(input);
412         *output = NULL;
413
414         update_panels();
415         doupdate();
416
417         while (rv == -1) {
418                 int c = dialog_getch(dia);
419
420                 if (c == '\t' || c == KEY_BTAB) {
421                         if (input_section) {
422                                 if (form_driver(input,REQ_VALIDATION) == E_OK) {
423                                         input_section = false;
424                                         menu_driver(dia->choices, REQ_FIRST_ITEM);
425                                 }
426                         } else {
427                                 input_section = true;
428                                 set_current_field(input, field[0]);
429                                 pos_form_cursor(input);
430                         }
431                 } else if (input_section) {
432                         handle_form_input(input, c);
433                 } else {
434                         rv = handle_menu_input(dia->choices, c);
435                         if (rv == DIALOG_OK) {
436                                 const char *buf = field_buffer(field[0], 0);
437                                 *output = string_trim(ctx, buf);
438                         }
439                 }
440                 update_panels();
441                 doupdate();
442         }
443
444 finish:
445         if (input) {
446                 unpost_form(input);
447                 free_form(input);
448         }
449         if (field[0]) {
450                 free_field(field[0]);
451         }
452         if (input_win) {
453                 delwin(input_win);
454         }
455         talloc_free(dia);
456
457         return rv;
458 }
459
460 int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
461                   const char *title, const char *msg, ...)
462 {
463         va_list ap;
464         struct dialog *dia;
465         const char *choices[] = {
466                 "Ok",
467                 "Cancel",
468                 NULL
469         };
470
471         if (type == DIA_ALERT) {
472                 choices[1] = NULL;
473         }
474
475         va_start(ap, msg);
476         dia = dialog_msg_new(ctx, title, choices, 5, msg, ap);
477         va_end(ap);
478         if (dia == NULL) {
479                 return -1;
480         }
481
482         return modal_loop(dia);
483 }
484
485 #define EDIT_WIDTH              50
486 #define EDIT_INTERNAL_WIDTH     (EDIT_WIDTH - 2)
487 #define EDIT_INPUT_WIDTH        (EDIT_INTERNAL_WIDTH - 2)
488
489 #define EDIT_NAME_LABEL_Y       0
490 #define EDIT_NAME_LABEL_X       0
491 #define EDIT_NAME_LABEL_WIDTH   EDIT_INTERNAL_WIDTH
492 #define EDIT_NAME_LABEL_HEIGHT  1
493 #define EDIT_NAME_INPUT_Y       1
494 #define EDIT_NAME_INPUT_X       1
495 #define EDIT_NAME_INPUT_WIDTH   EDIT_INPUT_WIDTH
496 #define EDIT_NAME_INPUT_HEIGHT  1
497
498 #define EDIT_DATA_LABEL_Y       3
499 #define EDIT_DATA_LABEL_X       0
500 #define EDIT_DATA_LABEL_WIDTH   EDIT_INTERNAL_WIDTH
501 #define EDIT_DATA_LABEL_HEIGHT  1
502 #define EDIT_DATA_INPUT_Y       4
503 #define EDIT_DATA_INPUT_X       1
504 #define EDIT_DATA_INPUT_WIDTH   EDIT_INPUT_WIDTH
505 #define EDIT_DATA_HEIGHT_ONELINE        1
506 #define EDIT_DATA_HEIGHT_MULTILINE      5
507 #define EDIT_DATA_HEIGHT_BUF            10
508
509 #define EDIT_FORM_WIN_Y                 0
510 #define EDIT_FORM_WIN_X                 0
511 #define EDIT_FORM_WIN_HEIGHT_ONELINE    \
512         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
513          EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_ONELINE)
514
515 #define EDIT_FORM_WIN_HEIGHT_MULTILINE  \
516         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
517          EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_MULTILINE)
518
519 #define EDIT_FORM_WIN_HEIGHT_BUF        \
520         (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
521          EDIT_DATA_LABEL_HEIGHT)
522
523 #define EDIT_FORM_WIN_WIDTH             EDIT_INTERNAL_WIDTH
524
525 #define EDIT_PAD                5
526 #define EDIT_HEIGHT_ONELINE     (EDIT_FORM_WIN_HEIGHT_ONELINE + EDIT_PAD)
527
528 #define EDIT_HEIGHT_MULTILINE   (EDIT_FORM_WIN_HEIGHT_MULTILINE + EDIT_PAD)
529
530 #define EDIT_HEIGHT_BUF         \
531         (EDIT_FORM_WIN_HEIGHT_BUF + EDIT_DATA_HEIGHT_BUF + EDIT_PAD)
532
533 #define MAX_FIELDS 5
534 #define FLD_NAME 1
535 #define FLD_DATA 3
536
537 enum input_section {
538         IN_NAME,
539         IN_DATA,
540         IN_MENU
541 };
542
543 struct edit_dialog {
544         struct dialog *dia;
545         WINDOW *input_win;
546         FORM *input;
547         FIELD *field[MAX_FIELDS];
548         struct hexedit *buf;
549         enum input_section section;
550 };
551
552 static int edit_dialog_free(struct edit_dialog *edit)
553 {
554         FIELD **f;
555
556         if (edit->input) {
557                 unpost_form(edit->input);
558                 free_form(edit->input);
559         }
560         for (f = edit->field; *f; ++f) {
561                 free_field(*f);
562         }
563         delwin(edit->input_win);
564
565         return 0;
566 }
567
568 static WERROR fill_value_buffer(struct edit_dialog *edit,
569                                 const struct value_item *vitem)
570 {
571         char *tmp;
572
573         switch (vitem->type) {
574         case REG_DWORD: {
575                 uint32_t v = 0;
576                 if (vitem->data.length >= 4) {
577                         v = IVAL(vitem->data.data, 0);
578                 }
579                 tmp = talloc_asprintf(edit, "0x%x", v);
580                 if (tmp == NULL) {
581                         return WERR_NOMEM;
582                 }
583                 set_field_buffer(edit->field[FLD_DATA], 0, tmp);
584                 talloc_free(tmp);
585                 break;
586         }
587         case REG_SZ:
588         case REG_EXPAND_SZ: {
589                 const char *s;
590
591                 if (!pull_reg_sz(edit, &vitem->data, &s)) {
592                         return WERR_NOMEM;
593                 }
594                 set_field_buffer(edit->field[FLD_DATA], 0, s);
595                 break;
596         }
597         case REG_MULTI_SZ: {
598                 const char **p, **a;
599                 char *buf = NULL;
600
601                 if (!pull_reg_multi_sz(edit, &vitem->data, &a)) {
602                         return WERR_NOMEM;
603                 }
604                 for (p = a; *p != NULL; ++p) {
605                         if (buf == NULL) {
606                                 buf = talloc_asprintf(edit, "%s\n", *p);
607                         } else {
608                                 buf = talloc_asprintf_append(buf, "%s\n", *p);
609                         }
610                         if (buf == NULL) {
611                                 return WERR_NOMEM;
612                         }
613                 }
614                 set_field_buffer(edit->field[FLD_DATA], 0, buf);
615                 talloc_free(buf);
616         }
617         case REG_BINARY:
618                 /* initialized upon dialog creation */
619                 break;
620         }
621
622         return WERR_OK;
623 }
624
625 static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
626                          const char *name)
627 {
628         uint32_t type;
629         DATA_BLOB blob;
630         WERROR rv;
631
632         rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
633
634         return W_ERROR_IS_OK(rv);
635 }
636
637 static WERROR set_value(struct edit_dialog *edit, struct registry_key *key,
638                         uint32_t type, bool new_value)
639 {
640         WERROR rv;
641         DATA_BLOB blob;
642         char *name = string_trim(edit, field_buffer(edit->field[FLD_NAME], 0));
643
644         if (!new_value && !edit->buf && !field_status(edit->field[FLD_DATA])) {
645                 return WERR_OK;
646         }
647         if (new_value && value_exists(edit, key, name)) {
648                 return WERR_FILE_EXISTS;
649         }
650
651         switch (type) {
652         case REG_DWORD: {
653                 uint32_t val;
654                 int base = 10;
655                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
656
657                 if (buf[0] == '0' && tolower(buf[1]) == 'x') {
658                         base = 16;
659                 }
660
661                 val = strtoul(buf, NULL, base);
662                 blob = data_blob_talloc(edit, NULL, sizeof(val));
663                 SIVAL(blob.data, 0, val);
664                 rv = WERR_OK;
665                 break;
666         }
667         case REG_SZ:
668         case REG_EXPAND_SZ: {
669                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
670                 char *str = string_trim(edit, buf);
671
672                 if (!str || !push_reg_sz(edit, &blob, str)) {
673                         rv = WERR_NOMEM;
674                 }
675                 break;
676         }
677         case REG_MULTI_SZ: {
678                 int rows, cols, max;
679                 const char **arr;
680                 size_t i;
681                 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
682
683                 dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max);
684
685                 arr = talloc_zero_array(edit, const char *, rows + 1);
686                 if (arr == NULL) {
687                         return WERR_NOMEM;
688                 }
689                 for (i = 0; *buf; ++i, buf += cols) {
690                         SMB_ASSERT(i < rows);
691                         arr[i] = string_trim_n(edit, buf, cols);
692                 }
693                 if (!push_reg_multi_sz(edit, &blob, arr)) {
694                         rv = WERR_NOMEM;
695                 }
696                 break;
697         }
698         case REG_BINARY:
699                 blob = data_blob_talloc(edit, NULL, edit->buf->len);
700                 memcpy(blob.data, edit->buf->data, edit->buf->len);
701                 break;
702         }
703
704         rv = reg_val_set(key, name, type, blob);
705
706         return rv;
707 }
708
709 static void section_down(struct edit_dialog *edit)
710 {
711         switch (edit->section) {
712         case IN_NAME:
713                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
714                         edit->section = IN_DATA;
715                         if (edit->buf) {
716                                 hexedit_set_cursor(edit->buf);
717                         } else {
718                                 set_current_field(edit->input,
719                                                   edit->field[FLD_DATA]);
720                                 pos_form_cursor(edit->input);
721                         }
722                 }
723                 break;
724         case IN_DATA:
725                 if (edit->buf ||
726                     form_driver(edit->input, REQ_VALIDATION) == E_OK) {
727                         edit->section = IN_MENU;
728                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
729                 }
730                 break;
731         case IN_MENU:
732                 edit->section = IN_NAME;
733                 set_current_field(edit->input, edit->field[FLD_NAME]);
734                 pos_form_cursor(edit->input);
735                 break;
736         }
737         update_panels();
738         doupdate();
739 }
740
741 static void section_up(struct edit_dialog *edit)
742 {
743         switch (edit->section) {
744         case IN_NAME:
745                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
746                         edit->section = IN_MENU;
747                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
748                 }
749                 break;
750         case IN_DATA:
751                 if (edit->buf ||
752                     form_driver(edit->input, REQ_VALIDATION) == E_OK) {
753                         edit->section = IN_NAME;
754                         set_current_field(edit->input, edit->field[FLD_NAME]);
755                         pos_form_cursor(edit->input);
756                 }
757                 break;
758         case IN_MENU:
759                 edit->section = IN_DATA;
760                 if (edit->buf) {
761                         hexedit_set_cursor(edit->buf);
762                 } else {
763                         set_current_field(edit->input, edit->field[FLD_DATA]);
764                         pos_form_cursor(edit->input);
765                 }
766                 break;
767         }
768         update_panels();
769         doupdate();
770 }
771
772 static void handle_hexedit_input(struct hexedit *buf, int c)
773 {
774         switch (c) {
775         case KEY_UP:
776                 hexedit_driver(buf, HE_CURSOR_UP);
777                 break;
778         case KEY_DOWN:
779                 hexedit_driver(buf, HE_CURSOR_DOWN);
780                 break;
781         case KEY_LEFT:
782                 hexedit_driver(buf, HE_CURSOR_LEFT);
783                 break;
784         case KEY_RIGHT:
785                 hexedit_driver(buf, HE_CURSOR_RIGHT);
786                 break;
787         default:
788                 hexedit_driver(buf, c);
789                 break;
790         }
791
792         hexedit_set_cursor(buf);
793 }
794
795 static WERROR edit_init_dialog(struct edit_dialog *edit, uint32_t type)
796 {
797         char *title;
798         int diaheight = -1;
799         int winheight = -1;
800         const char *choices[] = {
801                 "Ok",
802                 "Cancel",
803                 "Resize",
804                 NULL
805         };
806
807         switch (type) {
808         case REG_MULTI_SZ:
809                 diaheight = EDIT_HEIGHT_MULTILINE;
810                 winheight = EDIT_FORM_WIN_HEIGHT_MULTILINE;
811                 choices[2] = NULL;
812                 break;
813         case REG_BINARY:
814                 diaheight = EDIT_HEIGHT_BUF;
815                 winheight = EDIT_FORM_WIN_HEIGHT_BUF;
816                 break;
817         default:
818                 diaheight = EDIT_HEIGHT_ONELINE;
819                 winheight = EDIT_FORM_WIN_HEIGHT_ONELINE;
820                 choices[2] = NULL;
821                 break;
822         }
823
824         title = talloc_asprintf(edit, "Edit %s value", str_regtype(type));
825         if (title == NULL) {
826                 return WERR_NOMEM;
827         }
828         edit->dia = dialog_choice_center_new(edit, title, choices, diaheight,
829                                              EDIT_WIDTH);
830         talloc_free(title);
831         if (edit->dia == NULL) {
832                 return WERR_NOMEM;
833         }
834         edit->input_win = derwin(edit->dia->sub_window, winheight,
835                                  EDIT_FORM_WIN_WIDTH,
836                                  EDIT_FORM_WIN_Y, EDIT_FORM_WIN_X);
837         if (edit->input_win == NULL) {
838                 return WERR_NOMEM;
839         }
840
841         return WERR_OK;
842 }
843
844 static WERROR edit_init_form(struct edit_dialog *edit, uint32_t type,
845                              const struct value_item *vitem)
846 {
847
848         edit->field[0] = new_field(EDIT_NAME_LABEL_HEIGHT,
849                                    EDIT_NAME_LABEL_WIDTH,
850                                    EDIT_NAME_LABEL_Y,
851                                    EDIT_NAME_LABEL_X, 0, 0);
852         if (edit->field[0] == NULL) {
853                 return WERR_NOMEM;
854         }
855         set_field_buffer(edit->field[0], 0, "Name");
856         field_opts_off(edit->field[0], O_EDIT);
857
858         edit->field[FLD_NAME] = new_field(EDIT_NAME_INPUT_HEIGHT,
859                                           EDIT_NAME_INPUT_WIDTH,
860                                           EDIT_NAME_INPUT_Y,
861                                           EDIT_NAME_INPUT_X, 0, 0);
862         if (edit->field[FLD_NAME] == NULL) {
863                 return WERR_NOMEM;
864         }
865
866         edit->field[2] = new_field(EDIT_DATA_LABEL_HEIGHT,
867                                    EDIT_DATA_LABEL_WIDTH,
868                                    EDIT_DATA_LABEL_Y,
869                                    EDIT_DATA_LABEL_X, 0, 0);
870         if (edit->field[2] == NULL) {
871                 return WERR_NOMEM;
872         }
873         set_field_buffer(edit->field[2], 0, "Data");
874         field_opts_off(edit->field[2], O_EDIT);
875
876         if (type == REG_BINARY) {
877                 size_t len = 8;
878                 const void *buf = NULL;
879
880                 if (vitem) {
881                         len = vitem->data.length;
882                         buf = vitem->data.data;
883                 }
884                 edit->buf = hexedit_new(edit, edit->dia->sub_window,
885                                         EDIT_DATA_HEIGHT_BUF,
886                                         EDIT_DATA_INPUT_Y,
887                                         EDIT_DATA_INPUT_X,
888                                         buf, len);
889                 if (edit->buf == NULL) {
890                         return WERR_NOMEM;
891                 }
892                 hexedit_refresh(edit->buf);
893                 hexedit_set_cursor(edit->buf);
894         } else {
895                 int val_rows = EDIT_DATA_HEIGHT_ONELINE;
896
897                 if (type == REG_MULTI_SZ) {
898                         val_rows = EDIT_DATA_HEIGHT_MULTILINE;
899                 }
900                 edit->field[FLD_DATA] = new_field(val_rows,
901                                                   EDIT_DATA_INPUT_WIDTH,
902                                                   EDIT_DATA_INPUT_Y,
903                                                   EDIT_DATA_INPUT_X, 0, 0);
904                 if (edit->field[FLD_DATA] == NULL) {
905                         return WERR_NOMEM;
906                 }
907         }
908
909         set_field_back(edit->field[FLD_NAME], A_REVERSE);
910         field_opts_off(edit->field[FLD_NAME], O_BLANK | O_AUTOSKIP | O_STATIC);
911         if (edit->field[FLD_DATA]) {
912                 set_field_back(edit->field[FLD_DATA], A_REVERSE);
913                 field_opts_off(edit->field[FLD_DATA],
914                                O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP);
915                 if (type == REG_DWORD) {
916                         set_field_type(edit->field[FLD_DATA], TYPE_REGEXP,
917                                        "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$");
918                 }
919         }
920
921         if (vitem) {
922                 set_field_buffer(edit->field[FLD_NAME], 0, vitem->value_name);
923                 field_opts_off(edit->field[FLD_NAME], O_EDIT);
924                 fill_value_buffer(edit, vitem);
925         }
926
927         edit->input = new_form(edit->field);
928         if (edit->input == NULL) {
929                 return WERR_NOMEM;
930         }
931         form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD);
932
933         set_form_win(edit->input, edit->dia->sub_window);
934         set_form_sub(edit->input, edit->input_win);
935         set_current_field(edit->input, edit->field[FLD_NAME]);
936         post_form(edit->input);
937
938         return WERR_OK;
939 }
940
941 WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, uint32_t type,
942                       const struct value_item *vitem)
943 {
944         struct edit_dialog *edit;
945 #define DIALOG_RESIZE 2
946         WERROR rv = WERR_NOMEM;
947         int selection;
948
949         edit = talloc_zero(ctx, struct edit_dialog);
950         if (edit == NULL) {
951                 return rv;
952         }
953         talloc_set_destructor(edit, edit_dialog_free);
954
955         rv = edit_init_dialog(edit, type);
956         if (!W_ERROR_IS_OK(rv)) {
957                 goto finish;
958         }
959         rv = edit_init_form(edit, type, vitem);
960         if (!W_ERROR_IS_OK(rv)) {
961                 goto finish;
962         }
963
964         update_panels();
965         doupdate();
966
967         edit->section = IN_NAME;
968
969         while (1) {
970                 int c = dialog_getch(edit->dia);
971                 if (c == '\t') {
972                         section_down(edit);
973                         continue;
974                 } else if (c == KEY_BTAB) {
975                         section_up(edit);
976                         continue;
977                 }
978
979                 if (edit->section == IN_NAME) {
980                         handle_form_input(edit->input, c);
981                 } else if (edit->section == IN_DATA) {
982                         if (edit->buf) {
983                                 handle_hexedit_input(edit->buf, c);
984                         } else {
985                                 handle_form_input(edit->input, c);
986                         }
987                 } else {
988                         selection = handle_menu_input(edit->dia->choices, c);
989                         if (selection == DIALOG_OK) {
990                                 rv = set_value(edit, key, type, vitem == NULL);
991                                 if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) {
992                                         dialog_notice(edit, DIA_ALERT,
993                                                       "Value exists",
994                                                       "Value name already exists.");
995                                         selection = -1;
996                                 } else {
997                                         goto finish;
998                                 }
999                         } else if (selection == DIALOG_RESIZE) {
1000                                 char *n;
1001                                 size_t newlen = 0;
1002
1003                                 dialog_input(edit, &n, "Resize buffer",
1004                                              "Enter new size");
1005                                 if (n) {
1006                                         newlen = strtoul(n, NULL, 10);
1007                                         hexedit_resize_buffer(edit->buf, newlen);
1008                                         hexedit_refresh(edit->buf);
1009                                         talloc_free(n);
1010                                 }
1011                         } else if (selection == DIALOG_CANCEL) {
1012                                 rv = WERR_OK;
1013                                 goto finish;
1014                         }
1015                 }
1016
1017                 update_panels();
1018                 doupdate();
1019         }
1020
1021 finish:
1022         talloc_free(edit);
1023
1024         return rv;
1025 }
1026
1027 int dialog_select_type(TALLOC_CTX *ctx, int *type)
1028 {
1029         struct dialog *dia;
1030         const char *choices[] = {
1031                 "OK",
1032                 "Cancel",
1033                 NULL
1034         };
1035         const char *reg_types[] = {
1036                 "REG_DWORD",
1037                 "REG_SZ",
1038                 "REG_EXPAND_SZ",
1039                 "REG_MULTI_SZ",
1040                 "REG_BINARY",
1041         };
1042 #define NTYPES (sizeof(reg_types) / sizeof(const char*))
1043         ITEM **item;
1044         MENU *list;
1045         WINDOW *type_win;
1046         int sel = -1;
1047         size_t i;
1048
1049         dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20);
1050         if (dia == NULL) {
1051                 return -1;
1052         }
1053
1054         mvwprintw(dia->sub_window, 0, 0, "Choose type:");
1055         type_win = derwin(dia->sub_window, 6, 18, 1, 0);
1056         if (type_win == NULL) {
1057                 goto finish;
1058         }
1059
1060         item = talloc_zero_array(dia, ITEM *, NTYPES + 1);
1061         if (item == NULL) {
1062                 goto finish;
1063         }
1064
1065         for (i = 0; i < NTYPES; ++i) {
1066                 int t = regtype_by_string(reg_types[i]);
1067
1068                 item[i] = new_item(reg_types[i], reg_types[i]);
1069                 if (item[i] == NULL) {
1070                         goto finish;
1071                 }
1072                 set_item_userptr(item[i], (void*)(uintptr_t)t);
1073         }
1074
1075         list = new_menu(item);
1076         if (list == NULL) {
1077                 goto finish;
1078         }
1079
1080         set_menu_format(list, 7, 1);
1081         set_menu_win(list, dia->sub_window);
1082         set_menu_sub(list, type_win);
1083         menu_opts_off(list, O_SHOWDESC);
1084         set_menu_mark(list, "* ");
1085         post_menu(list);
1086
1087         update_panels();
1088         doupdate();
1089
1090         while (sel == -1) {
1091                 ITEM *it;
1092                 int c = dialog_getch(dia);
1093
1094                 switch (c) {
1095                 case KEY_UP:
1096                         menu_driver(list, REQ_UP_ITEM);
1097                         break;
1098                 case KEY_DOWN:
1099                         menu_driver(list, REQ_DOWN_ITEM);
1100                         break;
1101                 case KEY_LEFT:
1102                         menu_driver(dia->choices, REQ_LEFT_ITEM);
1103                         break;
1104                 case KEY_RIGHT:
1105                         menu_driver(dia->choices, REQ_RIGHT_ITEM);
1106                         break;
1107                 case '\n':
1108                 case KEY_ENTER:
1109                         it = current_item(list);
1110                         *type = (int)(uintptr_t)item_userptr(it);
1111                         it = current_item(dia->choices);
1112                         sel = (int)(uintptr_t)item_userptr(it);
1113                         break;
1114                 }
1115
1116                 update_panels();
1117                 doupdate();
1118         }
1119
1120 finish:
1121         if (list) {
1122                 unpost_menu(list);
1123                 free_menu(list);
1124         }
1125         if (item) {
1126                 ITEM **it;
1127                 for (it = item; *it; ++it) {
1128                         free_item(*it);
1129                 }
1130         }
1131         if (type_win) {
1132                 delwin(type_win);
1133         }
1134         talloc_free(dia);
1135
1136         return sel;
1137 }