regedit: don't expand single line text field buffer with cursor movement
[obnox/samba/samba-obnox.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         dialog_destroy(dia);
59
60         return 0;
61 }
62
63 static bool default_validator(struct dialog *dia, struct dialog_section *sect,
64                               void *arg)
65 {
66         return true;
67 }
68
69 struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title,
70                           int y, int x)
71 {
72         struct dialog *dia;
73
74         dia = talloc_zero(ctx, struct dialog);
75         if (dia == NULL) {
76                 return NULL;
77         }
78
79         talloc_set_destructor(dia, dialog_free);
80
81         dia->title = talloc_strdup(dia, title);
82         if (dia->title == NULL) {
83                 goto fail;
84         }
85         dia->x = x;
86         dia->y = y;
87         dia->color = color;
88         dia->submit = default_validator;
89
90         return dia;
91
92 fail:
93         talloc_free(dia);
94
95         return NULL;
96
97 }
98
99 void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg)
100 {
101         dia->submit = cb;
102         dia->submit_arg = arg;
103 }
104
105 static void center_above_window(int *nlines, int *ncols, int *y, int *x)
106 {
107         int centery, centerx;
108
109         centery = LINES / 2;
110         centerx = COLS / 2;
111         *y = 0;
112         *x = 0;
113
114         if (*nlines > LINES) {
115                 *nlines = LINES;
116         }
117         if (*ncols > COLS) {
118                 *ncols = COLS;
119         }
120
121         if (*nlines/2 < centery) {
122                 *y = centery - *nlines / 2;
123         }
124         if (*ncols/2 < centerx) {
125                 *x = centerx - *ncols / 2;
126         }
127 }
128
129 void dialog_section_destroy(struct dialog_section *section)
130 {
131         if (section->ops->destroy) {
132                 section->ops->destroy(section);
133         }
134         if (section->window) {
135                 delwin(section->window);
136                 section->window = NULL;
137         }
138 }
139
140 void dialog_section_init(struct dialog_section *section,
141                          const struct dialog_section_ops *ops,
142                          int nlines, int ncols)
143 {
144         section->ops = ops;
145         section->nlines = nlines;
146         section->ncols = ncols;
147 }
148
149 const char *dialog_section_get_name(struct dialog_section *section)
150 {
151         return section->name;
152 }
153
154 void dialog_section_set_name(struct dialog_section *section, const char *name)
155 {
156         TALLOC_FREE(section->name);
157         section->name = talloc_strdup(section, name);
158 }
159
160 void dialog_section_set_justify(struct dialog_section *section,
161                                 enum section_justify justify)
162 {
163         section->justify = justify;
164 }
165
166 /* append a section to the dialog's circular list */
167 void dialog_append_section(struct dialog *dia,
168                            struct dialog_section *section)
169 {
170         SMB_ASSERT(section != NULL);
171
172         if (!dia->head_section) {
173                 dia->head_section = section;
174         }
175         if (dia->tail_section) {
176                 dia->tail_section->next = section;
177         }
178         section->prev = dia->tail_section;
179         section->next = dia->head_section;
180         dia->head_section->prev = section;
181         dia->tail_section = section;
182 }
183
184 struct dialog_section *dialog_find_section(struct dialog *dia, const char *name)
185 {
186         struct dialog_section *section = dia->head_section;
187
188         do {
189                 if (section->name && strequal(section->name, name)) {
190                         return section;
191                 }
192                 section = section->next;
193         } while (section != dia->head_section);
194
195         return NULL;
196 }
197
198 static void section_on_input(struct dialog *dia, int c)
199 {
200         struct dialog_section *section = dia->current_section;
201
202         if (!section->ops->on_input) {
203                 return;
204         }
205         section->ops->on_input(dia, section, c);
206 }
207
208 static bool section_on_tab(struct dialog *dia)
209 {
210         struct dialog_section *section = dia->current_section;
211
212         if (!section || !section->ops->on_tab) {
213                 return false;
214         }
215         return section->ops->on_tab(dia, section);
216 }
217
218 static bool section_on_btab(struct dialog *dia)
219 {
220         struct dialog_section *section = dia->current_section;
221
222         if (!section || !section->ops->on_btab) {
223                 return false;
224         }
225         return section->ops->on_btab(dia, section);
226 }
227
228 static bool section_on_up(struct dialog *dia)
229 {
230         struct dialog_section *section = dia->current_section;
231
232         if (!section || !section->ops->on_up) {
233                 return false;
234         }
235         return section->ops->on_up(dia, section);
236 }
237
238 static bool section_on_down(struct dialog *dia)
239 {
240         struct dialog_section *section = dia->current_section;
241
242         if (!section || !section->ops->on_down) {
243                 return false;
244         }
245         return section->ops->on_down(dia, section);
246 }
247
248 static bool section_on_left(struct dialog *dia)
249 {
250         struct dialog_section *section = dia->current_section;
251
252         if (!section || !section->ops->on_left) {
253                 return false;
254         }
255         return section->ops->on_left(dia, section);
256 }
257
258 static bool section_on_right(struct dialog *dia)
259 {
260         struct dialog_section *section = dia->current_section;
261
262         if (!section || !section->ops->on_right) {
263                 return false;
264         }
265         return section->ops->on_right(dia, section);
266 }
267
268 static enum dialog_action section_on_enter(struct dialog *dia)
269 {
270         struct dialog_section *section = dia->current_section;
271
272         if (!section || !section->ops->on_enter) {
273                 return DIALOG_OK;
274         }
275         return section->ops->on_enter(dia, section);
276 }
277
278 static bool section_on_focus(struct dialog *dia, bool forward)
279 {
280         struct dialog_section *section = dia->current_section;
281
282         if (!section->ops->on_focus) {
283                 return false;
284         }
285         return section->ops->on_focus(dia, section, forward);
286 }
287
288 static void section_on_leave_focus(struct dialog *dia)
289 {
290         struct dialog_section *section = dia->current_section;
291
292         if (section->ops->on_leave_focus) {
293                 section->ops->on_leave_focus(dia, section);
294         }
295 }
296
297 static void section_set_next_focus(struct dialog *dia)
298 {
299         section_on_leave_focus(dia);
300
301         do {
302                 dia->current_section = dia->current_section->next;
303         } while (!section_on_focus(dia, true));
304 }
305
306 static void section_set_previous_focus(struct dialog *dia)
307 {
308         section_on_leave_focus(dia);
309
310         do {
311                 dia->current_section = dia->current_section->prev;
312         } while (!section_on_focus(dia, false));
313 }
314
315 WERROR dialog_create(struct dialog *dia)
316 {
317         WERROR rv = WERR_OK;
318         int row, col;
319         int nlines, ncols;
320         struct dialog_section *section;
321
322         nlines = 0;
323         ncols = 0;
324         SMB_ASSERT(dia->head_section != NULL);
325
326         /* calculate total size based on sections */
327         section = dia->head_section;
328         do {
329                 nlines += section->nlines;
330                 ncols = MAX(ncols, section->ncols);
331                 section = section->next;
332         } while (section != dia->head_section);
333
334         /* fill in widths for sections that expand */
335         section = dia->head_section;
336         do {
337                 if (section->ncols < 0) {
338                         section->ncols = ncols;
339                 }
340                 section = section->next;
341         } while (section != dia->head_section);
342
343         /* create window for dialog */
344         nlines += 4;
345         ncols += 6;
346         dia->pad = newpad(nlines, ncols);
347         if (dia->pad == NULL) {
348                 rv = WERR_NOMEM;
349                 goto fail;
350         }
351         dia->centered = false;
352         if (dia->y < 0 || dia->x < 0) {
353                 dia->centered = true;
354                 center_above_window(&nlines, &ncols, &dia->y, &dia->x);
355         }
356         dia->window = newwin(nlines, ncols, dia->y, dia->x);
357         if (dia->window == NULL) {
358                 rv = WERR_NOMEM;
359                 goto fail;
360         }
361         dia->panel = new_panel(dia->window);
362         if (dia->panel == NULL) {
363                 rv = WERR_NOMEM;
364                 goto fail;
365         }
366
367         /* setup color and border */
368         getmaxyx(dia->pad, nlines, ncols);
369         wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color));
370         wclear(dia->pad);
371         mvwhline(dia->pad, 1, 2, 0, ncols - 4);
372         mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4);
373         mvwvline(dia->pad, 2, 1, 0, nlines - 4);
374         mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4);
375         mvwaddch(dia->pad, 1, 1, ACS_ULCORNER);
376         mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER);
377         mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER);
378         mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER);
379         col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2;
380         mvwprintw(dia->pad, 1, col, " %s ", dia->title);
381
382         /* create subwindows for each section */
383         row = 2;
384         section = dia->head_section;
385         do {
386                 col = 3;
387
388                 switch (section->justify) {
389                 case SECTION_JUSTIFY_LEFT:
390                         break;
391                 case SECTION_JUSTIFY_CENTER:
392                         col += (ncols - 6)/ 2 - section->ncols / 2;
393                         break;
394                 case SECTION_JUSTIFY_RIGHT:
395                         break;
396                 }
397
398                 section->window = subpad(dia->pad, section->nlines,
399                                          section->ncols, row, col);
400                 if (section->window == NULL) {
401                         rv = WERR_NOMEM;
402                         goto fail;
403                 }
404                 SMB_ASSERT(section->ops->create != NULL);
405                 rv = section->ops->create(dia, section);
406                 row += section->nlines;
407                 section = section->next;
408         } while (section != dia->head_section && W_ERROR_IS_OK(rv));
409
410         dia->current_section = dia->head_section;
411         section_set_next_focus(dia);
412
413 fail:
414         return rv;
415 }
416
417 void dialog_show(struct dialog *dia)
418 {
419         int nlines, ncols;
420         int pad_y, pad_x;
421         int y, x;
422         int rv;
423
424         touchwin(dia->pad);
425         getmaxyx(dia->window, nlines, ncols);
426         getmaxyx(dia->pad, pad_y, pad_x);
427         y = 0;
428         if (pad_y > nlines) {
429                 y = (pad_y - nlines) / 2;
430         }
431         x = 0;
432         if (pad_x > ncols) {
433                 x = (pad_x - ncols) / 2;
434         }
435         rv = copywin(dia->pad, dia->window, y, x, 0, 0,
436                      nlines - 1, ncols - 1, false);
437         SMB_ASSERT(rv == OK);
438
439         getyx(dia->pad, pad_y, pad_x);
440         wmove(dia->window, pad_y - y, pad_x - x);
441         touchwin(dia->window);
442         wnoutrefresh(dia->window);
443 }
444
445 void dialog_destroy(struct dialog *dia)
446 {
447         struct dialog_section *section;
448
449         section = dia->head_section;
450         do {
451                 dialog_section_destroy(section);
452                 section = section->next;
453         } while (section != dia->head_section);
454
455         if (dia->panel) {
456                 del_panel(dia->panel);
457                 dia->panel = NULL;
458         }
459         if (dia->window) {
460                 delwin(dia->window);
461                 dia->window = NULL;
462         }
463 }
464
465 static int dialog_getch(struct dialog *dia)
466 {
467         int c;
468
469         c = regedit_getch();
470         if (c == KEY_RESIZE) {
471                 int nlines, ncols, y, x;
472                 int pad_nlines, pad_ncols;
473                 int win_nlines, win_ncols;
474
475                 getmaxyx(dia->window, win_nlines, win_ncols);
476                 getmaxyx(dia->pad, pad_nlines, pad_ncols);
477                 getbegyx(dia->window, y, x);
478
479                 nlines = pad_nlines;
480                 ncols = pad_ncols;
481
482                 if (dia->centered) {
483                         center_above_window(&nlines, &ncols, &y, &x);
484                 } else {
485                         if (nlines + y > LINES) {
486                                 if (nlines > LINES) {
487                                         y = 0;
488                                 } else {
489                                         y = LINES - nlines;
490                                 }
491                         }
492                         if (ncols + x > COLS) {
493                                 if (ncols > COLS) {
494                                         x = 0;
495                                 } else {
496                                         x = COLS - ncols;
497                                 }
498                         }
499                 }
500                 if (nlines != win_nlines || ncols != win_ncols) {
501                         wresize(dia->window, nlines, ncols);
502                         replace_panel(dia->panel, dia->window);
503                 }
504                 move_panel(dia->panel, y, x);
505         }
506
507         return c;
508 }
509
510 bool dialog_handle_input(struct dialog *dia, WERROR *err,
511                          enum dialog_action *action)
512 {
513         int c;
514
515         *err = WERR_OK;
516
517         c = dialog_getch(dia);
518
519         switch (c) {
520         case '\t':
521                 if (!section_on_tab(dia)) {
522                         section_set_next_focus(dia);
523                 }
524                 break;
525         case KEY_BTAB:
526                 if (!section_on_btab(dia)) {
527                         section_set_previous_focus(dia);
528                 }
529                 break;
530         case KEY_UP:
531                 if (!section_on_up(dia)) {
532                         section_set_previous_focus(dia);
533                 }
534                 break;
535         case KEY_DOWN:
536                 if (!section_on_down(dia)) {
537                         section_set_next_focus(dia);
538                 }
539                 break;
540         case KEY_LEFT:
541                 if (!section_on_left(dia)) {
542                         section_set_previous_focus(dia);
543                 }
544                 break;
545         case KEY_RIGHT:
546                 if (!section_on_right(dia)) {
547                         section_set_next_focus(dia);
548                 }
549                 break;
550         case '\n':
551         case KEY_ENTER:
552                 *action = section_on_enter(dia);
553                 switch (*action) {
554                 case DIALOG_IGNORE:
555                         break;
556                 case DIALOG_CANCEL:
557                         return false;
558                 case DIALOG_OK:
559                         return !dia->submit(dia, dia->current_section,
560                                             dia->submit_arg);
561                 }
562                 break;
563         case 27: /* ESC */
564                 return false;
565         default:
566                 section_on_input(dia, c);
567                 break;
568         }
569
570         return true;
571 }
572
573 void dialog_modal_loop(struct dialog *dia, WERROR *err,
574                        enum dialog_action *action)
575 {
576         do {
577                 dialog_show(dia);
578                 update_panels();
579                 doupdate();
580         } while (dialog_handle_input(dia, err, action));
581 }
582
583 /* text label */
584 struct dialog_section_label {
585         struct dialog_section section;
586         char **text;
587 };
588
589 static WERROR label_create(struct dialog *dia, struct dialog_section *section)
590 {
591         int row;
592         struct dialog_section_label *label =
593                 talloc_get_type_abort(section, struct dialog_section_label);
594
595         for (row = 0; row < section->nlines; ++row) {
596                 mvwaddstr(section->window, row, 0, label->text[row]);
597         }
598
599         return WERR_OK;
600 }
601
602 struct dialog_section_ops label_ops = {
603         .create = label_create,
604 };
605
606 static int label_free(struct dialog_section_label *label)
607 {
608         dialog_section_destroy(&label->section);
609         return 0;
610 }
611
612 struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx,
613                                                    const char *msg, va_list ap)
614 {
615         struct dialog_section_label *label;
616         char *tmp, *ptmp, *line, *saveptr;
617         int nlines, ncols;
618
619         label = talloc_zero(ctx, struct dialog_section_label);
620         if (label == NULL) {
621                 return NULL;
622         }
623         talloc_set_destructor(label, label_free);
624         tmp = talloc_vasprintf(label, msg, ap);
625         if (tmp == NULL) {
626                 goto fail;
627         }
628
629         for (nlines = 0, ncols = 0, ptmp = tmp;
630              (line = strtok_r(ptmp, "\n", &saveptr)) != NULL;
631              ++nlines) {
632                 ptmp = NULL;
633                 label->text = talloc_realloc(label, label->text,
634                                              char *, nlines + 1);
635                 if (label->text == NULL) {
636                         goto fail;
637                 }
638                 ncols = MAX(ncols, strlen(line));
639                 label->text[nlines] = talloc_strdup(label->text, line);
640                 if (label->text[nlines] == NULL) {
641                         goto fail;
642                 }
643         }
644         talloc_free(tmp);
645         dialog_section_init(&label->section, &label_ops, nlines, ncols);
646
647         return &label->section;
648
649 fail:
650         talloc_free(label);
651         return NULL;
652 }
653
654 struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx,
655                                                 const char *msg, ...)
656 {
657         va_list ap;
658         struct dialog_section *rv;
659
660         va_start(ap, msg);
661         rv = dialog_section_label_new_va(ctx, msg, ap);
662         va_end(ap);
663
664         return rv;
665 }
666
667 /* horizontal separator */
668 struct dialog_section_hsep {
669         struct dialog_section section;
670         int sep;
671 };
672
673 static WERROR hsep_create(struct dialog *dia, struct dialog_section *section)
674 {
675         int y, x;
676         struct dialog_section_hsep *hsep =
677                 talloc_get_type_abort(section, struct dialog_section_hsep);
678
679         whline(section->window, hsep->sep, section->ncols);
680
681         if (hsep->sep == 0 || hsep->sep == ACS_HLINE) {
682                 /* change the border characters around this section to
683                    tee chars */
684                 getparyx(section->window, y, x);
685                 mvwaddch(dia->pad, y, x - 1, ACS_HLINE);
686                 mvwaddch(dia->pad, y, x - 2, ACS_LTEE);
687                 mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE);
688                 mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE);
689         }
690
691         return WERR_OK;
692 }
693
694 struct dialog_section_ops hsep_ops = {
695         .create = hsep_create
696 };
697
698 static int hsep_free(struct dialog_section_hsep *hsep)
699 {
700         dialog_section_destroy(&hsep->section);
701         return 0;
702 }
703
704 struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep)
705 {
706         struct dialog_section_hsep *hsep;
707
708         hsep = talloc_zero(ctx, struct dialog_section_hsep);
709         if (hsep) {
710                 talloc_set_destructor(hsep, hsep_free);
711                 dialog_section_init(&hsep->section, &hsep_ops, 1, -1);
712                 hsep->sep = sep;
713         }
714
715         return &hsep->section;
716 }
717
718 /* text input field */
719 struct dialog_section_text_field {
720         struct dialog_section section;
721         unsigned opts;
722         FIELD *field[2];
723         FORM *form;
724         int length;
725 };
726
727 static int get_cursor_col(struct dialog_section_text_field *field)
728 {
729         int col;
730
731         col = field->form->curcol + field->form->begincol;
732
733         return col;
734 }
735
736 static WERROR text_field_create(struct dialog *dia,
737                                 struct dialog_section *section)
738 {
739         struct dialog_section_text_field *text_field =
740                 talloc_get_type_abort(section, struct dialog_section_text_field);
741
742         text_field->field[0] = new_field(section->nlines, section->ncols,
743                                          0, 0, 0, 0);
744         if (text_field->field[0] == NULL) {
745                 return WERR_NOMEM;
746         }
747         set_field_back(text_field->field[0], A_REVERSE);
748         set_field_opts(text_field->field[0], text_field->opts);
749
750         text_field->form = new_form(text_field->field);
751         if (text_field->form == NULL) {
752                 return WERR_NOMEM;
753         }
754
755         set_form_win(text_field->form, dia->window);
756         set_form_sub(text_field->form, section->window);
757         set_current_field(text_field->form, text_field->field[0]);
758         post_form(text_field->form);
759
760         return WERR_OK;
761 }
762
763 static void text_field_destroy(struct dialog_section *section)
764 {
765         struct dialog_section_text_field *text_field =
766                 talloc_get_type_abort(section, struct dialog_section_text_field);
767
768         if (text_field->form) {
769                 unpost_form(text_field->form);
770                 free_form(text_field->form);
771                 text_field->form = NULL;
772         }
773         if (text_field->field[0]) {
774                 free_field(text_field->field[0]);
775                 text_field->field[0] = NULL;
776         }
777 }
778
779 static void text_field_on_input(struct dialog *dia,
780                                 struct dialog_section *section,
781                                 int c)
782 {
783         struct dialog_section_text_field *text_field =
784                 talloc_get_type_abort(section, struct dialog_section_text_field);
785
786         switch (c) {
787         case KEY_BACKSPACE:
788                 if (text_field->length) {
789                         text_field->length--;
790                 }
791                 form_driver(text_field->form, REQ_DEL_PREV);
792                 break;
793         case '\x7f':
794         case KEY_DC:
795                 if (text_field->length) {
796                         text_field->length--;
797                 }
798                 form_driver(text_field->form, REQ_DEL_CHAR);
799                 break;
800         default:
801                 text_field->length++;
802                 form_driver(text_field->form, c);
803                 break;
804         }
805 }
806
807 static bool text_field_on_up(struct dialog *dia,
808                              struct dialog_section *section)
809 {
810         struct dialog_section_text_field *text_field =
811                 talloc_get_type_abort(section, struct dialog_section_text_field);
812
813         if (section->nlines > 1) {
814                 form_driver(text_field->form, REQ_UP_CHAR);
815                 return true;
816         }
817         return false;
818 }
819
820 static bool text_field_on_down(struct dialog *dia,
821                                struct dialog_section *section)
822 {
823         struct dialog_section_text_field *text_field =
824                 talloc_get_type_abort(section, struct dialog_section_text_field);
825
826         if (section->nlines > 1) {
827                 form_driver(text_field->form, REQ_DOWN_CHAR);
828                 return true;
829         }
830         return false;
831 }
832
833 static bool text_field_on_left(struct dialog *dia,
834                                struct dialog_section *section)
835 {
836         struct dialog_section_text_field *text_field =
837                 talloc_get_type_abort(section, struct dialog_section_text_field);
838
839         form_driver(text_field->form, REQ_LEFT_CHAR);
840
841         return true;
842 }
843
844 static bool text_field_on_right(struct dialog *dia,
845                                 struct dialog_section *section)
846 {
847         struct dialog_section_text_field *text_field =
848                 talloc_get_type_abort(section, struct dialog_section_text_field);
849
850         if (section->nlines > 1 ||
851             get_cursor_col(text_field) < text_field->length) {
852                 form_driver(text_field->form, REQ_RIGHT_CHAR);
853         }
854
855         return true;
856 }
857
858 static enum dialog_action text_field_on_enter(struct dialog *dia,
859                                               struct dialog_section *section)
860 {
861         struct dialog_section_text_field *text_field =
862                 talloc_get_type_abort(section, struct dialog_section_text_field);
863
864         if (section->nlines > 1) {
865                 text_field->length += text_field->form->cols;
866                 form_driver(text_field->form, REQ_NEW_LINE);
867                 return DIALOG_IGNORE;
868         }
869
870         return DIALOG_OK;
871 }
872
873 static bool text_field_on_focus(struct dialog *dia,
874                                 struct dialog_section *section, bool forward)
875 {
876         struct dialog_section_text_field *text_field =
877                 talloc_get_type_abort(section, struct dialog_section_text_field);
878
879         pos_form_cursor(text_field->form);
880
881         return true;
882 }
883
884 struct dialog_section_ops text_field_ops = {
885         .create = text_field_create,
886         .destroy = text_field_destroy,
887         .on_input = text_field_on_input,
888         .on_up = text_field_on_up,
889         .on_down = text_field_on_down,
890         .on_left = text_field_on_left,
891         .on_right = text_field_on_right,
892         .on_enter = text_field_on_enter,
893         .on_focus = text_field_on_focus
894 };
895
896 static int text_field_free(struct dialog_section_text_field *text_field)
897 {
898         dialog_section_destroy(&text_field->section);
899         return 0;
900 }
901
902 struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx,
903                                                      int height, int width)
904 {
905         struct dialog_section_text_field *text_field;
906
907         text_field = talloc_zero(ctx, struct dialog_section_text_field);
908         if (text_field == NULL) {
909                 return NULL;
910         }
911         talloc_set_destructor(text_field, text_field_free);
912         dialog_section_init(&text_field->section, &text_field_ops,
913                             height, width);
914         text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK;
915
916         return &text_field->section;
917 }
918
919 const char *dialog_section_text_field_get(TALLOC_CTX *ctx,
920                                           struct dialog_section *section)
921 {
922         struct dialog_section_text_field *text_field =
923                 talloc_get_type_abort(section, struct dialog_section_text_field);
924
925         form_driver(text_field->form, REQ_VALIDATION);
926
927         return string_trim(ctx, field_buffer(text_field->field[0], 0));
928 }
929
930 void dialog_section_text_field_set(struct dialog_section *section,
931                                    const char *s)
932 {
933         struct dialog_section_text_field *text_field =
934                 talloc_get_type_abort(section, struct dialog_section_text_field);
935
936         text_field->length = strlen(s);
937         set_field_buffer(text_field->field[0], 0, s);
938 }
939
940 const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx,
941                                                  struct dialog_section *section)
942 {
943         int rows, cols, max;
944         const char **arr;
945         size_t i;
946         const char *buf;
947         struct dialog_section_text_field *text_field =
948                 talloc_get_type_abort(section, struct dialog_section_text_field);
949
950         form_driver(text_field->form, REQ_VALIDATION);
951         buf = field_buffer(text_field->field[0], 0);
952
953         dynamic_field_info(text_field->field[0], &rows, &cols, &max);
954
955         arr = talloc_zero_array(ctx, const char *, rows + 1);
956         if (arr == NULL) {
957                 return NULL;
958         }
959         for (i = 0; *buf; ++i, buf += cols) {
960                 SMB_ASSERT(i < rows);
961                 arr[i] = string_trim_n(arr, buf, cols);
962         }
963
964         return arr;
965 }
966
967 WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx,
968                                            struct dialog_section *section,
969                                            const char **array)
970 {
971         int rows, cols, max;
972         size_t padding, length, index;
973         const char **arrayp;
974         char *buf = NULL;
975         struct dialog_section_text_field *text_field =
976                 talloc_get_type_abort(section, struct dialog_section_text_field);
977
978         dynamic_field_info(text_field->field[0], &rows, &cols, &max);
979         /* try to fit each string on it's own line. each line
980            needs to be padded with whitespace manually, since
981            ncurses fields do not have newlines. */
982         for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) {
983                 length = MIN(strlen(*arrayp), cols);
984                 padding = cols - length;
985                 buf = talloc_realloc(ctx, buf, char,
986                                      talloc_array_length(buf) +
987                                      length + padding + 1);
988                 if (buf == NULL) {
989                         return WERR_NOMEM;
990                 }
991                 memcpy(&buf[index], *arrayp, length);
992                 index += length;
993                 memset(&buf[index], ' ', padding);
994                 index += padding;
995                 buf[index] = '\0';
996         }
997
998         set_field_buffer(text_field->field[0], 0, buf);
999         talloc_free(buf);
1000
1001         return WERR_OK;
1002 }
1003
1004 bool dialog_section_text_field_get_int(struct dialog_section *section,
1005                                        long long *out)
1006 {
1007         bool rv;
1008         const char *buf;
1009         char *endp;
1010         struct dialog_section_text_field *text_field =
1011                 talloc_get_type_abort(section, struct dialog_section_text_field);
1012
1013         form_driver(text_field->form, REQ_VALIDATION);
1014
1015         buf = string_trim(section, field_buffer(text_field->field[0], 0));
1016         if (buf == NULL) {
1017                 return false;
1018         }
1019         *out = strtoll(buf, &endp, 0);
1020         rv = true;
1021         if (endp == buf || endp == NULL || endp[0] != '\0') {
1022                 rv = false;
1023         }
1024
1025         return rv;
1026 }
1027
1028
1029 bool dialog_section_text_field_get_uint(struct dialog_section *section,
1030                                         unsigned long long *out)
1031 {
1032         bool rv;
1033         const char *buf;
1034         char *endp;
1035         struct dialog_section_text_field *text_field =
1036                 talloc_get_type_abort(section, struct dialog_section_text_field);
1037
1038         form_driver(text_field->form, REQ_VALIDATION);
1039
1040         buf = string_trim(section, field_buffer(text_field->field[0], 0));
1041         if (buf == NULL) {
1042                 return false;
1043         }
1044         *out = strtoull(buf, &endp, 0);
1045         rv = true;
1046         if (endp == buf || endp == NULL || endp[0] != '\0') {
1047                 rv = false;
1048         }
1049
1050         return rv;
1051 }
1052
1053 /* hex editor field */
1054 struct dialog_section_hexedit {
1055         struct dialog_section section;
1056         struct hexedit *buf;
1057 };
1058
1059 #define HEXEDIT_MIN_SIZE 16
1060 static WERROR hexedit_create(struct dialog *dia,
1061                                 struct dialog_section *section)
1062 {
1063         struct dialog_section_hexedit *hexedit =
1064                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1065
1066         hexedit->buf = hexedit_new(dia, section->window, NULL,
1067                                    HEXEDIT_MIN_SIZE);
1068         if (hexedit->buf == NULL) {
1069                 return WERR_NOMEM;
1070         }
1071
1072         hexedit_refresh(hexedit->buf);
1073
1074         return WERR_OK;
1075 }
1076
1077 static void hexedit_destroy(struct dialog_section *section)
1078 {
1079         struct dialog_section_hexedit *hexedit =
1080                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1081
1082         if (hexedit->buf) {
1083                 TALLOC_FREE(hexedit->buf);
1084         }
1085 }
1086
1087 static void hexedit_on_input(struct dialog *dia,
1088                                 struct dialog_section *section,
1089                                 int c)
1090 {
1091         struct dialog_section_hexedit *hexedit =
1092                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1093
1094         switch (c) {
1095         case KEY_BACKSPACE:
1096                 // FIXME hexedit_driver(hexedit->buf, c);
1097                 break;
1098         default:
1099                 hexedit_driver(hexedit->buf, c);
1100                 break;
1101         }
1102 }
1103
1104 static bool hexedit_on_up(struct dialog *dia,
1105                              struct dialog_section *section)
1106 {
1107         struct dialog_section_hexedit *hexedit =
1108                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1109
1110         hexedit_driver(hexedit->buf, HE_CURSOR_UP);
1111
1112         return true;
1113 }
1114
1115 static bool hexedit_on_down(struct dialog *dia,
1116                                struct dialog_section *section)
1117 {
1118         struct dialog_section_hexedit *hexedit =
1119                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1120
1121         hexedit_driver(hexedit->buf, HE_CURSOR_DOWN);
1122
1123         return true;
1124 }
1125
1126 static bool hexedit_on_left(struct dialog *dia,
1127                                struct dialog_section *section)
1128 {
1129         struct dialog_section_hexedit *hexedit =
1130                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1131
1132         hexedit_driver(hexedit->buf, HE_CURSOR_LEFT);
1133
1134         return true;
1135 }
1136
1137 static bool hexedit_on_right(struct dialog *dia,
1138                                 struct dialog_section *section)
1139 {
1140         struct dialog_section_hexedit *hexedit =
1141                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1142
1143         hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT);
1144
1145         return true;
1146 }
1147
1148 static enum dialog_action hexedit_on_enter(struct dialog *dia,
1149                                               struct dialog_section *section)
1150 {
1151         return DIALOG_IGNORE;
1152 }
1153
1154 static bool hexedit_on_focus(struct dialog *dia,
1155                                 struct dialog_section *section, bool forward)
1156 {
1157         struct dialog_section_hexedit *hexedit =
1158                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1159
1160         hexedit_set_cursor(hexedit->buf);
1161
1162         return true;
1163 }
1164
1165 struct dialog_section_ops hexedit_ops = {
1166         .create = hexedit_create,
1167         .destroy = hexedit_destroy,
1168         .on_input = hexedit_on_input,
1169         .on_up = hexedit_on_up,
1170         .on_down = hexedit_on_down,
1171         .on_left = hexedit_on_left,
1172         .on_right = hexedit_on_right,
1173         .on_enter = hexedit_on_enter,
1174         .on_focus = hexedit_on_focus
1175 };
1176
1177 static int hexedit_free(struct dialog_section_hexedit *hexedit)
1178 {
1179         dialog_section_destroy(&hexedit->section);
1180         return 0;
1181 }
1182
1183 struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height)
1184 {
1185         struct dialog_section_hexedit *hexedit;
1186
1187         hexedit = talloc_zero(ctx, struct dialog_section_hexedit);
1188         if (hexedit == NULL) {
1189                 return NULL;
1190         }
1191         talloc_set_destructor(hexedit, hexedit_free);
1192         dialog_section_init(&hexedit->section, &hexedit_ops,
1193                             height, LINE_WIDTH);
1194
1195         return &hexedit->section;
1196 }
1197
1198 WERROR dialog_section_hexedit_set_buf(struct dialog_section *section,
1199                                       const void *data, size_t size)
1200 {
1201         WERROR rv;
1202         struct dialog_section_hexedit *hexedit =
1203                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1204
1205         SMB_ASSERT(hexedit->buf != NULL);
1206
1207         rv = hexedit_set_buf(hexedit->buf, data, size);
1208         if (W_ERROR_IS_OK(rv)) {
1209                 hexedit_refresh(hexedit->buf);
1210                 hexedit_set_cursor(hexedit->buf);
1211         }
1212
1213         return rv;
1214 }
1215
1216 void dialog_section_hexedit_get_buf(struct dialog_section *section,
1217                                     const void **data, size_t *size)
1218 {
1219         struct dialog_section_hexedit *hexedit =
1220                 talloc_get_type_abort(section, struct dialog_section_hexedit);
1221
1222         SMB_ASSERT(hexedit->buf != NULL);
1223         *data = hexedit_get_buf(hexedit->buf);
1224         *size = hexedit_get_buf_len(hexedit->buf);
1225 }
1226
1227 /* button box */
1228 struct dialog_section_buttons {
1229         struct dialog_section section;
1230         struct button_spec *spec;
1231         int current_button;
1232 };
1233
1234 static void buttons_unhighlight(struct dialog_section_buttons *buttons)
1235 {
1236         short pair;
1237         attr_t attr;
1238
1239         /*
1240          *  Some GCC versions will complain if the macro version of
1241          *  wattr_get is used. So we should enforce the use of the
1242          *  function instead. See:
1243          *  http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
1244          */
1245         (wattr_get)(buttons->section.window, &attr, &pair, NULL);
1246         mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
1247         wnoutrefresh(buttons->section.window);
1248 }
1249
1250 static void buttons_highlight(struct dialog_section_buttons *buttons)
1251 {
1252         struct button_spec *spec = &buttons->spec[buttons->current_button];
1253         short pair;
1254         attr_t attr;
1255
1256         /*
1257          *  Some GCC versions will complain if the macro version of
1258          *  wattr_get is used. So we should enforce the use of the
1259          *  function instead. See:
1260          *  http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
1261          */
1262         (wattr_get)(buttons->section.window, &attr, &pair, NULL);
1263         mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
1264         mvwchgat(buttons->section.window, 0, spec->col,
1265                  strlen(spec->label), A_REVERSE, pair, NULL);
1266         wmove(buttons->section.window, 0, spec->col + 2);
1267         wcursyncup(buttons->section.window);
1268         wnoutrefresh(buttons->section.window);
1269 }
1270
1271 static bool buttons_highlight_next(struct dialog_section_buttons *buttons)
1272 {
1273         if (buttons->current_button < talloc_array_length(buttons->spec) - 1) {
1274                 buttons->current_button++;
1275                 buttons_highlight(buttons);
1276                 return true;
1277         }
1278         return false;
1279 }
1280
1281 static bool buttons_highlight_previous(struct dialog_section_buttons *buttons)
1282 {
1283         if (buttons->current_button > 0) {
1284                 buttons->current_button--;
1285                 buttons_highlight(buttons);
1286                 return true;
1287         }
1288         return false;
1289 }
1290
1291 static WERROR buttons_create(struct dialog *dia,
1292                                 struct dialog_section *section)
1293 {
1294         size_t i, nbuttons;
1295         struct dialog_section_buttons *buttons =
1296                 talloc_get_type_abort(section, struct dialog_section_buttons);
1297
1298         nbuttons = talloc_array_length(buttons->spec);
1299         for (i = 0; i < nbuttons; ++i) {
1300                 struct button_spec *spec = &buttons->spec[i];
1301                 mvwaddstr(section->window, 0, spec->col, spec->label);
1302         }
1303
1304         buttons->current_button = 0;
1305
1306         return WERR_OK;
1307 }
1308
1309 static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section)
1310 {
1311         struct dialog_section_buttons *buttons =
1312                 talloc_get_type_abort(section, struct dialog_section_buttons);
1313
1314         return buttons_highlight_previous(buttons);
1315 }
1316
1317 static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section)
1318 {
1319         struct dialog_section_buttons *buttons =
1320                 talloc_get_type_abort(section, struct dialog_section_buttons);
1321
1322         return buttons_highlight_next(buttons);
1323 }
1324
1325 static enum dialog_action buttons_on_enter(struct dialog *dia,
1326                                            struct dialog_section *section)
1327 {
1328         struct dialog_section_buttons *buttons =
1329                 talloc_get_type_abort(section, struct dialog_section_buttons);
1330         struct button_spec *spec = &buttons->spec[buttons->current_button];
1331
1332         if (spec->on_enter) {
1333                 return spec->on_enter(dia, section);
1334         }
1335
1336         return spec->action;
1337 }
1338
1339 static bool buttons_on_focus(struct dialog *dia,
1340                                 struct dialog_section *section,
1341                                 bool forward)
1342 {
1343         struct dialog_section_buttons *buttons =
1344                 talloc_get_type_abort(section, struct dialog_section_buttons);
1345
1346         if (forward) {
1347                 buttons->current_button = 0;
1348         } else {
1349                 buttons->current_button = talloc_array_length(buttons->spec) - 1;
1350         }
1351         buttons_highlight(buttons);
1352
1353         return true;
1354 }
1355
1356 static void buttons_on_leave_focus(struct dialog *dia,
1357                                 struct dialog_section *section)
1358 {
1359         struct dialog_section_buttons *buttons =
1360                 talloc_get_type_abort(section, struct dialog_section_buttons);
1361         buttons_unhighlight(buttons);
1362 }
1363
1364 struct dialog_section_ops buttons_ops = {
1365         .create = buttons_create,
1366         .on_tab = buttons_on_tab,
1367         .on_btab = buttons_on_btab,
1368         .on_up = buttons_on_btab,
1369         .on_down = buttons_on_tab,
1370         .on_left = buttons_on_btab,
1371         .on_right = buttons_on_tab,
1372         .on_enter = buttons_on_enter,
1373         .on_focus = buttons_on_focus,
1374         .on_leave_focus = buttons_on_leave_focus
1375 };
1376
1377 static int buttons_free(struct dialog_section_buttons *buttons)
1378 {
1379         dialog_section_destroy(&buttons->section);
1380         return 0;
1381 }
1382
1383 struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx,
1384                                                   const struct button_spec *spec)
1385 {
1386         struct dialog_section_buttons *buttons;
1387         size_t i, nbuttons;
1388         int width;
1389
1390         buttons = talloc_zero(ctx, struct dialog_section_buttons);
1391         if (buttons == NULL) {
1392                 return NULL;
1393         }
1394         talloc_set_destructor(buttons, buttons_free);
1395
1396         for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) {
1397         }
1398         buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons);
1399         if (buttons->spec == NULL) {
1400                 goto fail;
1401         }
1402
1403         for (width = 0, i = 0; i < nbuttons; ++i) {
1404                 buttons->spec[i] = spec[i];
1405                 buttons->spec[i].label = talloc_asprintf(buttons->spec,
1406                                                          "[ %s ]",
1407                                                          spec[i].label);
1408                 if (!buttons->spec[i].label) {
1409                         goto fail;
1410                 }
1411
1412                 buttons->spec[i].col = width;
1413                 width += strlen(buttons->spec[i].label);
1414                 if (i != nbuttons - 1) {
1415                         ++width;
1416                 }
1417         }
1418
1419         dialog_section_init(&buttons->section, &buttons_ops, 1, width);
1420
1421         return &buttons->section;
1422
1423 fail:
1424         talloc_free(buttons);
1425         return NULL;
1426 }
1427
1428 /* options */
1429 struct dialog_section_options {
1430         struct dialog_section section;
1431         struct option_spec *spec;
1432         int current_option;
1433         bool single_select;
1434 };
1435
1436 static void options_unhighlight(struct dialog_section_options *options)
1437 {
1438         short pair;
1439         attr_t attr;
1440         size_t row;
1441
1442         /*
1443          *  Some GCC versions will complain if the macro version of
1444          *  wattr_get is used. So we should enforce the use of the
1445          *  function instead. See:
1446          *  http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
1447          */
1448         (wattr_get)(options->section.window, &attr, &pair, NULL);
1449         for (row = 0; row < options->section.nlines; ++row) {
1450                 mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
1451         }
1452         wnoutrefresh(options->section.window);
1453 }
1454
1455 static void options_highlight(struct dialog_section_options *options)
1456 {
1457         struct option_spec *spec = &options->spec[options->current_option];
1458         short pair;
1459         attr_t attr;
1460         size_t row;
1461
1462         /*
1463          *  Some GCC versions will complain if the macro version of
1464          *  wattr_get is used. So we should enforce the use of the
1465          *  function instead. See:
1466          *  http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
1467          */
1468         (wattr_get)(options->section.window, &attr, &pair, NULL);
1469         for (row = 0; row < options->section.nlines; ++row) {
1470                 mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
1471         }
1472         mvwchgat(options->section.window, spec->row, spec->col,
1473                  strlen(spec->label), A_REVERSE, pair, NULL);
1474         wmove(options->section.window, spec->row, spec->col + 4);
1475         wcursyncup(options->section.window);
1476         wnoutrefresh(options->section.window);
1477 }
1478
1479 static void options_render_state(struct dialog_section_options *options)
1480 {
1481         size_t i, noptions;
1482
1483         noptions = talloc_array_length(options->spec);
1484         for (i = 0; i < noptions; ++i) {
1485                 struct option_spec *spec = &options->spec[i];
1486                 char c = ' ';
1487                 if (*spec->state)
1488                         c = 'x';
1489                 mvwaddch(options->section.window,
1490                          spec->row, spec->col + 1, c);
1491                 wnoutrefresh(options->section.window);
1492         }
1493 }
1494
1495 static bool options_highlight_next(struct dialog_section_options *options)
1496 {
1497         if (options->current_option < talloc_array_length(options->spec) - 1) {
1498                 options->current_option++;
1499                 options_highlight(options);
1500                 return true;
1501         }
1502         return false;
1503 }
1504
1505 static bool options_highlight_previous(struct dialog_section_options *options)
1506 {
1507         if (options->current_option > 0) {
1508                 options->current_option--;
1509                 options_highlight(options);
1510                 return true;
1511         }
1512         return false;
1513 }
1514
1515 static WERROR options_create(struct dialog *dia,
1516                              struct dialog_section *section)
1517 {
1518         size_t i, noptions;
1519         struct dialog_section_options *options =
1520                 talloc_get_type_abort(section, struct dialog_section_options);
1521
1522         noptions = talloc_array_length(options->spec);
1523         for (i = 0; i < noptions; ++i) {
1524                 struct option_spec *spec = &options->spec[i];
1525                 mvwaddstr(section->window, spec->row, spec->col,
1526                           spec->label);
1527         }
1528
1529         options->current_option = 0;
1530         options_render_state(options);
1531
1532         return WERR_OK;
1533 }
1534
1535 static bool options_on_btab(struct dialog *dia, struct dialog_section *section)
1536 {
1537         struct dialog_section_options *options =
1538                 talloc_get_type_abort(section, struct dialog_section_options);
1539
1540         return options_highlight_previous(options);
1541 }
1542
1543 static bool options_on_tab(struct dialog *dia, struct dialog_section *section)
1544 {
1545         struct dialog_section_options *options =
1546                 talloc_get_type_abort(section, struct dialog_section_options);
1547
1548         return options_highlight_next(options);
1549 }
1550
1551 static void options_on_input(struct dialog *dia, struct dialog_section *section, int c)
1552 {
1553         struct dialog_section_options *options =
1554                 talloc_get_type_abort(section, struct dialog_section_options);
1555
1556         if (c == ' ') {
1557                 struct option_spec *spec = &options->spec[options->current_option];
1558                 if (options->single_select) {
1559                         size_t i, noptions;
1560                         noptions = talloc_array_length(options->spec);
1561                         for (i = 0; i < noptions; ++i) {
1562                                 *(options->spec[i].state) = false;
1563                         }
1564                 }
1565                 *spec->state = !*spec->state;
1566                 options_unhighlight(options);
1567                 options_render_state(options);
1568                 options_highlight(options);
1569         }
1570 }
1571
1572 static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section)
1573 {
1574         options_on_input(dia, section, ' ');
1575         return DIALOG_OK;
1576 }
1577
1578 static bool options_on_focus(struct dialog *dia,
1579                                 struct dialog_section *section,
1580                                 bool forward)
1581 {
1582         struct dialog_section_options *options =
1583                 talloc_get_type_abort(section, struct dialog_section_options);
1584
1585         if (forward) {
1586                 options->current_option = 0;
1587         } else {
1588                 options->current_option = talloc_array_length(options->spec) - 1;
1589         }
1590         options_highlight(options);
1591
1592         return true;
1593 }
1594
1595 static void options_on_leave_focus(struct dialog *dia,
1596                                 struct dialog_section *section)
1597 {
1598         struct dialog_section_options *options =
1599                 talloc_get_type_abort(section, struct dialog_section_options);
1600         options_unhighlight(options);
1601 }
1602
1603 struct dialog_section_ops options_ops = {
1604         .create = options_create,
1605         .on_tab = options_on_tab,
1606         .on_btab = options_on_btab,
1607         .on_up = options_on_btab,
1608         .on_down = options_on_tab,
1609         .on_left = options_on_btab,
1610         .on_right = options_on_tab,
1611         .on_input = options_on_input,
1612         .on_enter = options_on_enter,
1613         .on_focus = options_on_focus,
1614         .on_leave_focus = options_on_leave_focus
1615 };
1616
1617 static int options_free(struct dialog_section_options *options)
1618 {
1619         dialog_section_destroy(&options->section);
1620         return 0;
1621 }
1622
1623 struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx,
1624                                                   const struct option_spec *spec,
1625                                                   int maxcol, bool single_select)
1626 {
1627         struct dialog_section_options *options;
1628         size_t i, noptions;
1629         int width, maxwidth, maxrows;
1630
1631         options = talloc_zero(ctx, struct dialog_section_options);
1632         if (options == NULL) {
1633                 return NULL;
1634         }
1635         talloc_set_destructor(options, options_free);
1636
1637         for (noptions = 0; spec[noptions].label; ++noptions) {
1638         }
1639         options->spec = talloc_zero_array(options, struct option_spec, noptions);
1640         if (options->spec == NULL) {
1641                 goto fail;
1642         }
1643
1644         maxrows = noptions / maxcol;
1645         if (noptions % maxcol) {
1646                 ++maxrows;
1647         }
1648
1649         for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) {
1650                 options->spec[i] = spec[i];
1651                 options->spec[i].label = talloc_asprintf(options->spec,
1652                                                          "[ ] %s",
1653                                                          spec[i].label);
1654                 if (!options->spec[i].label) {
1655                         goto fail;
1656                 }
1657
1658                 options->spec[i].col = maxwidth;
1659                 options->spec[i].row = i % maxrows;
1660                 width = MAX(strlen(options->spec[i].label), width);
1661                 if (options->spec[i].row == maxrows - 1 || i == noptions - 1) {
1662                         maxwidth += width + 1;
1663                         width = 0;
1664                 }
1665         }
1666
1667         dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1);
1668         options->single_select = single_select;
1669
1670         return &options->section;
1671
1672 fail:
1673         talloc_free(options);
1674         return NULL;
1675 }
1676
1677
1678 int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title,
1679                  const char *msg, ...)
1680 {
1681         va_list ap;
1682         WERROR err;
1683         enum dialog_action action;
1684         struct dialog *dia;
1685         struct dialog_section *section;
1686         struct button_spec spec[] = {
1687                 {.label = "OK", .action = DIALOG_OK},
1688                 {.label = "Cancel", .action = DIALOG_CANCEL},
1689                 { 0 }
1690         };
1691
1692         dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
1693         va_start(ap, msg);
1694         section = dialog_section_label_new_va(dia, msg, ap);
1695         va_end(ap);
1696         dialog_append_section(dia, section);
1697         section = dialog_section_hsep_new(dia, ' ');
1698         dialog_append_section(dia, section);
1699         section = dialog_section_text_field_new(dia, 1, -1);
1700         dialog_section_set_name(section, "input");
1701         dialog_append_section(dia, section);
1702         section = dialog_section_hsep_new(dia, 0);
1703         dialog_append_section(dia, section);
1704         section = dialog_section_buttons_new(dia, spec);
1705         dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
1706         dialog_append_section(dia, section);
1707
1708         dialog_create(dia);
1709         dialog_show(dia);
1710         dialog_modal_loop(dia, &err, &action);
1711
1712         *output = NULL;
1713         if (action == DIALOG_OK) {
1714                 section = dialog_find_section(dia, "input");
1715                 *output = dialog_section_text_field_get(ctx, section);
1716         }
1717
1718         talloc_free(dia);
1719
1720         return action;
1721 }
1722
1723 int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
1724                   const char *title, const char *msg, ...)
1725 {
1726         va_list ap;
1727         WERROR err;
1728         enum dialog_action action;
1729         struct dialog *dia;
1730         struct dialog_section *section;
1731         struct button_spec spec[3];
1732
1733         memset(&spec, '\0', sizeof(spec));
1734         spec[0].label = "OK";
1735         spec[0].action = DIALOG_OK;
1736         if (type == DIA_CONFIRM) {
1737                 spec[1].label = "Cancel";
1738                 spec[1].action = DIALOG_CANCEL;
1739         }
1740
1741         dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
1742         va_start(ap, msg);
1743         section = dialog_section_label_new_va(dia, msg, ap);
1744         va_end(ap);
1745         dialog_append_section(dia, section);
1746         section = dialog_section_hsep_new(dia, 0);
1747         dialog_append_section(dia, section);
1748         section = dialog_section_buttons_new(dia, spec);
1749         dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
1750         dialog_append_section(dia, section);
1751
1752         dialog_create(dia);
1753         dialog_show(dia);
1754         dialog_modal_loop(dia, &err, &action);
1755         talloc_free(dia);
1756
1757         return action;
1758 }
1759
1760
1761 struct edit_req {
1762         uint32_t type;
1763         uint32_t mode;
1764         struct registry_key *key;
1765         const struct value_item *vitem;
1766 };
1767
1768 static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit)
1769 {
1770         char *tmp;
1771         struct dialog_section *data;
1772
1773         if (edit->vitem == NULL) {
1774                 return WERR_OK;
1775         }
1776
1777         data = dialog_find_section(dia, "data");
1778         SMB_ASSERT(data != NULL);
1779
1780         switch (edit->mode) {
1781         case REG_DWORD: {
1782                 uint32_t v = 0;
1783                 if (edit->vitem->data.length >= 4) {
1784                         v = IVAL(edit->vitem->data.data, 0);
1785                 }
1786                 tmp = talloc_asprintf(dia, "%u", (unsigned)v);
1787                 if (tmp == NULL) {
1788                         return WERR_NOMEM;
1789                 }
1790                 dialog_section_text_field_set(data, tmp);
1791                 talloc_free(tmp);
1792                 break;
1793         }
1794         case REG_SZ:
1795         case REG_EXPAND_SZ: {
1796                 const char *s;
1797
1798                 if (!pull_reg_sz(dia, &edit->vitem->data, &s)) {
1799                         return WERR_NOMEM;
1800                 }
1801                 dialog_section_text_field_set(data, s);
1802                 break;
1803         }
1804         case REG_MULTI_SZ: {
1805                 const char **array;
1806
1807                 if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) {
1808                         return WERR_NOMEM;
1809                 }
1810                 return dialog_section_text_field_set_lines(dia, data, array);
1811         }
1812         case REG_BINARY:
1813         default:
1814                 return dialog_section_hexedit_set_buf(data,
1815                                                       edit->vitem->data.data,
1816                                                       edit->vitem->data.length);
1817         }
1818
1819         return WERR_OK;
1820 }
1821
1822 static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
1823                          const char *name)
1824 {
1825         uint32_t type;
1826         DATA_BLOB blob;
1827         WERROR rv;
1828
1829         rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
1830
1831         return W_ERROR_IS_OK(rv);
1832 }
1833
1834 static bool edit_on_submit(struct dialog *dia, struct dialog_section *section,
1835                            void *arg)
1836 {
1837         struct edit_req *edit = arg;
1838         WERROR rv;
1839         DATA_BLOB blob;
1840         const char *name;
1841         struct dialog_section *name_section, *data;
1842
1843         name_section = dialog_find_section(dia, "name");
1844         if (name_section) {
1845                 name = dialog_section_text_field_get(dia, name_section);
1846                 if (*name == '\0') {
1847                         dialog_notice(dia, DIA_ALERT, "Error",
1848                                       "Value name must not be blank.");
1849                         return false;
1850                 }
1851                 if (value_exists(dia, edit->key, name)) {
1852                         dialog_notice(dia, DIA_ALERT, "Error",
1853                                       "Value named \"%s\" already exists.",
1854                                       name);
1855                         return false;
1856                 }
1857         } else {
1858                 SMB_ASSERT(edit->vitem);
1859                 name = edit->vitem->value_name;
1860         }
1861         SMB_ASSERT(name);
1862
1863         data = dialog_find_section(dia, "data");
1864         SMB_ASSERT(data != NULL);
1865
1866         rv = WERR_OK;
1867         switch (edit->mode) {
1868         case REG_DWORD: {
1869                 unsigned long long v;
1870                 uint32_t val;
1871
1872                 if (!dialog_section_text_field_get_uint(data, &v)) {
1873                         dialog_notice(dia, DIA_ALERT, "Error",
1874                                       "REG_DWORD value must be an integer.");
1875                         return false;
1876                 }
1877                 if (v > UINT32_MAX) {
1878                         dialog_notice(dia, DIA_ALERT, "Error",
1879                                       "REG_DWORD value must less than %lu.",
1880                                       (unsigned long)UINT32_MAX);
1881                         return false;
1882                 }
1883                 val = (uint32_t)v;
1884                 blob = data_blob_talloc(dia, NULL, sizeof(val));
1885                 SIVAL(blob.data, 0, val);
1886                 break;
1887         }
1888         case REG_SZ:
1889         case REG_EXPAND_SZ: {
1890                 const char *buf;
1891
1892                 buf = dialog_section_text_field_get(dia, data);
1893                 if (!buf || !push_reg_sz(dia, &blob, buf)) {
1894                         rv = WERR_NOMEM;
1895                 }
1896                 break;
1897         }
1898         case REG_MULTI_SZ: {
1899                 const char **lines;
1900
1901                 lines = dialog_section_text_field_get_lines(dia, data);
1902                 if (!lines || !push_reg_multi_sz(dia, &blob, lines)) {
1903                         rv = WERR_NOMEM;
1904                 }
1905                 break;
1906         }
1907         case REG_BINARY: {
1908                 const void *buf;
1909                 size_t len;
1910
1911                 dialog_section_hexedit_get_buf(data, &buf, &len);
1912                 blob = data_blob_talloc(dia, buf, len);
1913                 break;
1914         }
1915         }
1916
1917         if (W_ERROR_IS_OK(rv)) {
1918                 rv = reg_val_set(edit->key, name, edit->type, blob);
1919         }
1920
1921         if (!W_ERROR_IS_OK(rv)) {
1922                 const char *msg = get_friendly_werror_msg(rv);
1923                 dialog_notice(dia, DIA_ALERT, "Error",
1924                               "Error saving value:\n%s", msg);
1925
1926                 return false;
1927         }
1928
1929         return true;
1930
1931 }
1932
1933 int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key,
1934                       uint32_t type, const struct value_item *vitem,
1935                       bool force_binary, WERROR *err,
1936                       const char **name)
1937 {
1938         enum dialog_action action;
1939         struct dialog *dia;
1940         struct dialog_section *section;
1941         struct edit_req edit;
1942         struct button_spec spec[] = {
1943                 {.label = "OK", .action = DIALOG_OK},
1944                 {.label = "Cancel", .action = DIALOG_CANCEL},
1945                 { 0 }
1946         };
1947
1948         edit.key = key;
1949         edit.vitem = vitem;
1950         edit.type = type;
1951         edit.mode = type;
1952         if (force_binary || (vitem && vitem->unprintable)) {
1953                 edit.mode = REG_BINARY;
1954         }
1955
1956         dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1);
1957         dialog_set_submit_cb(dia, edit_on_submit, &edit);
1958
1959         section = dialog_section_label_new(dia, "Type");
1960         dialog_append_section(dia, section);
1961         section = dialog_section_label_new(dia, "%s",
1962                                            str_regtype(type));
1963         dialog_append_section(dia, section);
1964         section = dialog_section_hsep_new(dia, ' ');
1965         dialog_append_section(dia, section);
1966
1967         section = dialog_section_label_new(dia, "Name");
1968         dialog_append_section(dia, section);
1969         if (vitem) {
1970                 section = dialog_section_label_new(dia, "%s",
1971                                                    vitem->value_name);
1972         } else {
1973                 section = dialog_section_text_field_new(dia, 1, 50);
1974                 dialog_section_set_name(section, "name");
1975         }
1976         dialog_append_section(dia, section);
1977         section = dialog_section_hsep_new(dia, ' ');
1978         dialog_append_section(dia, section);
1979
1980         section = dialog_section_label_new(dia, "Data");
1981         dialog_append_section(dia, section);
1982
1983         switch (edit.mode) {
1984         case REG_DWORD:
1985         case REG_SZ:
1986         case REG_EXPAND_SZ:
1987                 section = dialog_section_text_field_new(dia, 1, 50);
1988                 break;
1989         case REG_MULTI_SZ:
1990                 section = dialog_section_text_field_new(dia, 10, 50);
1991                 break;
1992         case REG_BINARY:
1993         default:
1994                 section = dialog_section_hexedit_new(dia, 10);
1995                 break;
1996         }
1997
1998         dialog_section_set_name(section, "data");
1999         dialog_append_section(dia, section);
2000
2001         section = dialog_section_hsep_new(dia, 0);
2002         dialog_append_section(dia, section);
2003         section = dialog_section_buttons_new(dia, spec);
2004         dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
2005         dialog_append_section(dia, section);
2006
2007         dialog_create(dia);
2008
2009         *err = fill_value_buffer(dia, &edit);
2010         if (!W_ERROR_IS_OK(*err)) {
2011                 return DIALOG_CANCEL;
2012         }
2013
2014         dialog_show(dia);
2015         dialog_modal_loop(dia, err, &action);
2016
2017         if (action == DIALOG_OK && name) {
2018                 if (vitem) {
2019                         *name = talloc_strdup(ctx, vitem->value_name);
2020                 } else if ((section = dialog_find_section(dia, "name"))) {
2021                         *name = dialog_section_text_field_get(ctx, section);
2022                 }
2023         }
2024
2025         talloc_free(dia);
2026
2027         return action;
2028 }
2029
2030 int dialog_select_type(TALLOC_CTX *ctx, int *type)
2031 {
2032         WERROR err;
2033         enum dialog_action action;
2034         struct dialog *dia;
2035         struct dialog_section *section;
2036         const char *reg_types[] = {
2037                 "REG_BINARY",
2038                 "REG_DWORD",
2039                 "REG_EXPAND_SZ",
2040                 "REG_MULTI_SZ",
2041                 "REG_SZ"
2042         };
2043         #define NTYPES ARRAY_SIZE(reg_types)
2044         struct button_spec spec[] = {
2045                 {.label = "OK", .action = DIALOG_OK},
2046                 {.label = "Cancel", .action = DIALOG_CANCEL},
2047                 { 0 }
2048         };
2049         bool flags[NTYPES] = { true };
2050         struct option_spec opsec[NTYPES + 1];
2051         unsigned i;
2052
2053         memset(&opsec, '\0', sizeof(opsec));
2054         for (i = 0; i < NTYPES; ++i) {
2055                 opsec[i].label = reg_types[i];
2056                 opsec[i].state = &flags[i];
2057         }
2058
2059         dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1);
2060
2061         section = dialog_section_label_new(dia, "Select type for new value:");
2062         dialog_append_section(dia, section);
2063         section = dialog_section_hsep_new(dia, ' ');
2064         dialog_append_section(dia, section);
2065         section = dialog_section_options_new(dia, opsec, 2, true);
2066         dialog_append_section(dia, section);
2067         section = dialog_section_hsep_new(dia, 0);
2068         dialog_append_section(dia, section);
2069         section = dialog_section_buttons_new(dia, spec);
2070         dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
2071         dialog_append_section(dia, section);
2072
2073         dialog_create(dia);
2074         dialog_show(dia);
2075
2076         dialog_modal_loop(dia, &err, &action);
2077         if (action == DIALOG_OK) {
2078                 for (i = 0; i < NTYPES; ++i) {
2079                         if (flags[i]) {
2080                                 *type = regtype_by_string(reg_types[i]);
2081                                 break;
2082                         }
2083                 }
2084         }
2085
2086         talloc_free(dia);
2087
2088         return action;
2089 }
2090
2091 struct search_req {
2092         TALLOC_CTX *ctx;
2093         struct regedit_search_opts *opts;
2094 };
2095
2096 static bool search_on_submit(struct dialog *dia, struct dialog_section *section,
2097                              void *arg)
2098 {
2099         struct search_req *search = arg;
2100         struct dialog_section *query;
2101
2102         query = dialog_find_section(dia, "query");
2103         SMB_ASSERT(query != NULL);
2104
2105         if (!search->opts->search_key && !search->opts->search_value) {
2106                 dialog_notice(dia, DIA_ALERT, "Error",
2107                               "Must search a key and/or a value");
2108                 return false;
2109         }
2110
2111         talloc_free(discard_const(search->opts->query));
2112         search->opts->query = dialog_section_text_field_get(search->ctx, query);
2113         SMB_ASSERT(search->opts->query != NULL);
2114         if (search->opts->query[0] == '\0') {
2115                 dialog_notice(dia, DIA_ALERT, "Error",
2116                               "Query must not be blank.");
2117                 return false;
2118         }
2119
2120         return true;
2121 }
2122
2123 int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts)
2124 {
2125         WERROR err;
2126         enum dialog_action action;
2127         struct dialog *dia;
2128         struct dialog_section *section, *query;
2129         struct search_req search;
2130         struct button_spec spec[] = {
2131                 {.label = "Search", .action = DIALOG_OK},
2132                 {.label = "Cancel", .action = DIALOG_CANCEL},
2133                 { 0 }
2134         };
2135         struct option_spec search_opts[] = {
2136                 {.label = "Search Keys", .state = &opts->search_key},
2137                 {.label = "Search Values", .state = &opts->search_value},
2138                 {.label = "Recursive", .state = &opts->search_recursive},
2139                 {.label = "Case Sensitive", .state = &opts->search_case},
2140                 { 0 }
2141         };
2142
2143         if (!opts->search_key && !opts->search_value) {
2144                 opts->search_key = true;
2145         }
2146
2147         search.ctx = ctx;
2148         search.opts = opts;
2149         dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1);
2150         dialog_set_submit_cb(dia, search_on_submit, &search);
2151         section = dialog_section_label_new(dia, "Query");
2152         dialog_append_section(dia, section);
2153         query = dialog_section_text_field_new(dia, 1, -1);
2154         dialog_section_set_name(query, "query");
2155         dialog_append_section(dia, query);
2156         section = dialog_section_hsep_new(dia, 0);
2157         dialog_append_section(dia, section);
2158         section = dialog_section_options_new(dia, search_opts, 2, false);
2159         dialog_append_section(dia, section);
2160         section = dialog_section_hsep_new(dia, 0);
2161         dialog_append_section(dia, section);
2162         section = dialog_section_buttons_new(dia, spec);
2163         dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
2164         dialog_append_section(dia, section);
2165
2166         dialog_create(dia);
2167         if (opts->query) {
2168                 dialog_section_text_field_set(query, opts->query);
2169         }
2170
2171         dialog_modal_loop(dia, &err, &action);
2172         talloc_free(dia);
2173
2174         return action;
2175 }