4065bf04f191bd03bff00daf1fb570757e8d1be9
[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_dialog.h"
22 #include "regedit_valuelist.h"
23 #include "util_reg.h"
24 #include "lib/registry/registry.h"
25 #include <stdarg.h>
26 #include <form.h>
27
28 static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
29 {
30         char *str;
31
32         str = talloc_strndup(ctx, buf, n);
33
34         if (str) {
35                 trim_string(str, " ", " ");
36         }
37
38         return str;
39 }
40
41 static char *string_trim(TALLOC_CTX *ctx, const char *buf)
42 {
43         char *str;
44
45         str = talloc_strdup(ctx, buf);
46
47         if (str) {
48                 trim_string(str, " ", " ");
49         }
50
51         return str;
52 }
53
54 static int dialog_free(struct dialog *dia)
55 {
56         if (dia->window) {
57                 delwin(dia->window);
58         }
59         if (dia->sub_window) {
60                 delwin(dia->sub_window);
61         }
62         if (dia->panel) {
63                 del_panel(dia->panel);
64         }
65         if (dia->choices) {
66                 unpost_menu(dia->choices);
67                 free_menu(dia->choices);
68         }
69         if (dia->choice_items) {
70                 ITEM **it;
71                 for (it = dia->choice_items; *it != NULL; ++it) {
72                         free_item(*it);
73                 }
74         }
75
76         return 0;
77 }
78
79 struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines,
80                           int ncols, int y, int x)
81 {
82         struct dialog *dia;
83
84         dia = talloc_zero(ctx, struct dialog);
85         if (dia == NULL) {
86                 return NULL;
87         }
88
89         talloc_set_destructor(dia, dialog_free);
90
91         dia->window = newwin(nlines, ncols, y, x);
92         if (dia->window == NULL) {
93                 goto fail;
94         }
95
96         box(dia->window, 0, 0);
97         mvwaddstr(dia->window, 0, 1, title);
98
99         /* body of the dialog within the box outline */
100         dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1);
101         if (dia->sub_window == NULL) {
102                 goto fail;
103         }
104
105         dia->panel = new_panel(dia->window);
106         if (dia->panel == NULL) {
107                 goto fail;
108         }
109
110         return dia;
111
112 fail:
113         talloc_free(dia);
114
115         return NULL;
116
117 }
118
119 static void center_dialog_above_window(WINDOW *below, int *nlines, int *ncols,
120                                        int *y, int *x)
121 {
122         int maxy, maxx;
123         int centery, centerx;
124
125         getmaxyx(below, maxy, maxx);
126
127         centery = maxy / 2;
128         centerx = maxx / 2;
129         *y = 0;
130         *x = 0;
131
132         if (*nlines > maxy) {
133                 *nlines = maxy;
134         }
135         if (*ncols > maxx) {
136                 *ncols = maxx;
137         }
138
139         if (*nlines < centery) {
140                 *y = centery - *nlines;
141         }
142         if (*ncols < centerx) {
143                 *x = centerx - *ncols;
144         }
145 }
146
147 struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, int nlines,
148                                  int ncols, WINDOW *below)
149 {
150         int y, x;
151
152         center_dialog_above_window(below, &nlines, &ncols, &y, &x);
153
154         return dialog_new(ctx, title, nlines, ncols, y, x);
155 }
156
157 struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title,
158                                  const char **choices, int nlines,
159                                  int ncols, int y, int x)
160 {
161         size_t nchoices, i;
162         struct dialog *dia;
163
164         dia = dialog_new(ctx, title, nlines, ncols, y, x);
165         if (dia == NULL) {
166                 return NULL;
167         }
168
169         dia->menu_window = derwin(dia->sub_window, 1, ncols - 3,
170                                   nlines - 3, 0);
171         if (dia->menu_window == NULL) {
172                 goto fail;
173         }
174
175         for (nchoices = 0; choices[nchoices] != NULL; ++nchoices)
176                 ;
177         dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1);
178         if (dia->choice_items == NULL) {
179                 goto fail;
180         }
181         for (i = 0; i < nchoices; ++i) {
182                 char *desc = talloc_strdup(dia, choices[i]);
183                 if (desc == NULL) {
184                         goto fail;
185                 }
186                 dia->choice_items[i] = new_item(desc, desc);
187                 if (dia->choice_items[i] == NULL) {
188                         goto fail;
189                 }
190                 /* store choice index */
191                 set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i);
192         }
193
194         dia->choices = new_menu(dia->choice_items);
195         if (dia->choices == NULL) {
196                 goto fail;
197         }
198
199         set_menu_format(dia->choices, 1, ncols);
200         set_menu_win(dia->choices, dia->sub_window);
201         set_menu_sub(dia->choices, dia->menu_window);
202         menu_opts_off(dia->choices, O_SHOWDESC);
203         set_menu_mark(dia->choices, "* ");
204         post_menu(dia->choices);
205         wmove(dia->sub_window, 0, 0);
206
207         return dia;
208
209 fail:
210         talloc_free(dia);
211
212         return NULL;
213 }
214
215 struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title,
216                                         const char **choices, int nlines,
217                                         int ncols, WINDOW *below)
218 {
219         int y, x;
220
221         center_dialog_above_window(below, &nlines, &ncols, &y, &x);
222
223         return dialog_choice_new(ctx, title, choices, nlines, ncols, y, x);
224 }
225
226 static int handle_menu_input(MENU *menu, int c)
227 {
228         ITEM *item;
229
230         switch (c) {
231         case KEY_LEFT:
232                 menu_driver(menu, REQ_LEFT_ITEM);
233                 break;
234         case KEY_RIGHT:
235                 menu_driver(menu, REQ_RIGHT_ITEM);
236                 break;
237         case KEY_ENTER:
238         case '\n':
239                 item = current_item(menu);
240                 return (int)(uintptr_t)item_userptr(item);
241         }
242
243         return -1;
244 }
245
246 static void handle_form_input(FORM *frm, int c)
247 {
248         switch (c) {
249         case '\n':
250                 form_driver(frm, REQ_NEW_LINE);
251                 break;
252         case KEY_UP:
253                 form_driver(frm, REQ_UP_CHAR);
254                 break;
255         case KEY_DOWN:
256                 form_driver(frm, REQ_DOWN_CHAR);
257                 break;
258         case '\b':
259         case KEY_BACKSPACE:
260                 form_driver(frm, REQ_DEL_PREV);
261                 break;
262         case KEY_LEFT:
263                 form_driver(frm, REQ_LEFT_CHAR);
264                 break;
265         case KEY_RIGHT:
266                 form_driver(frm, REQ_RIGHT_CHAR);
267                 break;
268         default:
269                 form_driver(frm, c);
270                 break;
271         }
272 }
273
274 static int modal_loop(struct dialog *dia)
275 {
276         int c;
277         int selection = -1;
278
279         keypad(dia->window, true);
280         update_panels();
281         doupdate();
282
283         while (selection == -1) {
284                 c = wgetch(dia->window);
285                 selection = handle_menu_input(dia->choices, c);
286                 update_panels();
287                 doupdate();
288         }
289
290         talloc_free(dia);
291
292         return selection;
293 }
294
295 static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title,
296                                      const char **choices, WINDOW *below,
297                                      int nlines, const char *msg, va_list ap)
298 {
299         struct dialog *dia;
300         char *str;
301         int width;
302 #define MIN_WIDTH 20
303
304         str = talloc_vasprintf(ctx, msg, ap);
305         if (str == NULL) {
306                 return NULL;
307         }
308
309         width = strlen(str) + 2;
310         if (width < MIN_WIDTH) {
311                 width = MIN_WIDTH;
312         }
313         dia = dialog_choice_center_new(ctx, title, choices, nlines, width, below);
314         if (dia == NULL) {
315                 return NULL;
316         }
317
318         waddstr(dia->sub_window, str);
319         talloc_free(str);
320
321         return dia;
322 }
323
324 int dialog_input(TALLOC_CTX *ctx, char **output, const char *title,
325                  WINDOW *below, const char *msg, ...)
326 {
327         va_list ap;
328         struct dialog *dia;
329         const char *choices[] = {
330                 "Ok",
331                 "Cancel",
332                 NULL
333         };
334         FIELD *field[2] = {0};
335         FORM *input;
336         WINDOW *input_win;
337         int y, x;
338         int rv = -1;
339         bool input_section = true;
340
341         va_start(ap, msg);
342         dia = dialog_msg_new(ctx, title, choices, below, 7, msg, ap);
343         va_end(ap);
344         if (dia == NULL) {
345                 return -1;
346         }
347
348         getmaxyx(dia->sub_window, y, x);
349         input_win = derwin(dia->sub_window, 1, x - 2, 2, 1);
350         if (input_win == NULL) {
351                 goto finish;
352         }
353         field[0] = new_field(1, x - 2, 0, 0, 0, 0);
354         if (field[0] == NULL) {
355                 goto finish;
356         }
357
358         field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
359         set_field_back(field[0], A_REVERSE);
360
361         input = new_form(field);
362         form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD);
363         set_form_win(input, dia->sub_window);
364         set_form_sub(input, input_win);
365         set_current_field(input, field[0]);
366         post_form(input);
367         *output = NULL;
368
369         keypad(dia->window, true);
370         update_panels();
371         doupdate();
372
373         while (rv == -1) {
374                 int c = wgetch(dia->window);
375
376                 if (c == '\t' || c == KEY_BTAB) {
377                         if (input_section) {
378                                 if (form_driver(input,REQ_VALIDATION) == E_OK) {
379                                         input_section = false;
380                                         menu_driver(dia->choices, REQ_FIRST_ITEM);
381                                 }
382                         } else {
383                                 input_section = true;
384                                 set_current_field(input, field[0]);
385                         }
386                         continue;
387                 }
388
389                 if (input_section) {
390                         handle_form_input(input, c);
391                 } else {
392                         rv = handle_menu_input(dia->choices, c);
393                         if (rv == DIALOG_OK) {
394                                 const char *buf = field_buffer(field[0], 0);
395                                 *output = string_trim(ctx, buf);
396                         }
397                 }
398         }
399
400 finish:
401         if (input) {
402                 unpost_form(input);
403                 free_form(input);
404         }
405         if (field[0]) {
406                 free_field(field[0]);
407         }
408         if (input_win) {
409                 delwin(input_win);
410         }
411         talloc_free(dia);
412
413         return rv;
414 }
415
416 int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
417                   const char *title, WINDOW *below,
418                   const char *msg, ...)
419 {
420         va_list ap;
421         struct dialog *dia;
422         const char *choices[] = {
423                 "Ok",
424                 "Cancel",
425                 NULL
426         };
427
428         if (type == DIA_ALERT) {
429                 choices[1] = NULL;
430         }
431
432         va_start(ap, msg);
433         dia = dialog_msg_new(ctx, title, choices, below, 5, msg, ap);
434         va_end(ap);
435         if (dia == NULL) {
436                 return -1;
437         }
438
439         return modal_loop(dia);
440 }
441
442 #define MAX_FIELDS 8
443
444 enum input_section {
445         IN_NAME,
446         IN_DATA,
447         IN_MENU
448 };
449
450 struct edit_dialog {
451         struct dialog *dia;
452         WINDOW *input_win;
453         FORM *input;
454         FIELD *field[MAX_FIELDS];
455         enum input_section section;
456 };
457
458 static int edit_dialog_free(struct edit_dialog *edit)
459 {
460         if (edit->input) {
461                 unpost_form(edit->input);
462         }
463         if (edit->field[0]) {
464                 free_field(edit->field[0]);
465         }
466         if (edit->field[1]) {
467                 free_field(edit->field[1]);
468         }
469         delwin(edit->input_win);
470
471         return 0;
472 }
473
474 static WERROR fill_value_buffer(struct edit_dialog *edit,
475                                 const struct value_item *vitem)
476 {
477         char *tmp;
478
479         switch (vitem->type) {
480         case REG_DWORD: {
481                 uint32_t v = 0;
482                 if (vitem->data.length >= 4) {
483                         v = IVAL(vitem->data.data, 0);
484                 }
485                 tmp = talloc_asprintf(edit, "0x%x", v);
486                 if (tmp == NULL) {
487                         return WERR_NOMEM;
488                 }
489                 set_field_buffer(edit->field[1], 0, tmp);
490                 talloc_free(tmp);
491                 break;
492         }
493         case REG_SZ:
494         case REG_EXPAND_SZ: {
495                 const char *s;
496
497                 if (!pull_reg_sz(edit, &vitem->data, &s)) {
498                         return WERR_NOMEM;
499                 }
500                 set_field_buffer(edit->field[1], 0, s);
501                 break;
502         }
503         case REG_MULTI_SZ: {
504                 const char **p, **a;
505                 char *buf = NULL;
506
507                 if (!pull_reg_multi_sz(edit, &vitem->data, &a)) {
508                         return WERR_NOMEM;
509                 }
510                 for (p = a; *p != NULL; ++p) {
511                         if (buf == NULL) {
512                                 buf = talloc_asprintf(edit, "%s\n", *p);
513                         } else {
514                                 buf = talloc_asprintf_append(buf, "%s\n", *p);
515                         }
516                         if (buf == NULL) {
517                                 return WERR_NOMEM;
518                         }
519                 }
520                 set_field_buffer(edit->field[1], 0, buf);
521                 talloc_free(buf);
522         }
523
524         }
525
526         return WERR_OK;
527 }
528
529 static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
530                          const char *name)
531 {
532         uint32_t type;
533         DATA_BLOB blob;
534         WERROR rv;
535
536         rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
537
538         return W_ERROR_IS_OK(rv);
539 }
540
541 static WERROR set_value(struct edit_dialog *edit, struct registry_key *key,
542                         uint32_t type, bool new_value)
543 {
544         WERROR rv;
545         DATA_BLOB blob;
546         const char *buf = field_buffer(edit->field[1], 0);
547         char *name = string_trim(edit, field_buffer(edit->field[0], 0));
548
549         if (!buf) {
550                 return WERR_OK;
551         }
552         if (!new_value && !field_status(edit->field[1])) {
553                 return WERR_OK;
554         }
555         if (new_value && value_exists(edit, key, name)) {
556                 return WERR_FILE_EXISTS;
557         }
558
559         switch (type) {
560         case REG_DWORD: {
561                 uint32_t val;
562                 int base = 10;
563
564                 if (buf[0] == '0' && tolower(buf[1]) == 'x') {
565                         base = 16;
566                 }
567
568                 val = strtoul(buf, NULL, base);
569                 blob = data_blob_talloc(edit, NULL, sizeof(val));
570                 SIVAL(blob.data, 0, val);
571                 rv = WERR_OK;
572                 break;
573         }
574         case REG_SZ:
575         case REG_EXPAND_SZ: {
576                 char *str = string_trim(edit, buf);
577                 if (!str || !push_reg_sz(edit, &blob, str)) {
578                         rv = WERR_NOMEM;
579                 }
580                 break;
581         }
582         case REG_MULTI_SZ: {
583                 int rows, cols, max;
584                 const char **arr;
585                 size_t i;
586
587                 dynamic_field_info(edit->field[1], &rows, &cols, &max);
588
589                 arr = talloc_zero_array(edit, const char *, rows + 1);
590                 if (arr == NULL) {
591                         return WERR_NOMEM;
592                 }
593                 for (i = 0; *buf; ++i, buf += cols) {
594                         SMB_ASSERT(i < rows);
595                         arr[i] = string_trim_n(edit, buf, cols);
596                 }
597                 if (!push_reg_multi_sz(edit, &blob, arr)) {
598                         rv = WERR_NOMEM;
599                 }
600                 break;
601         }
602         }
603
604         rv = reg_val_set(key, name, type, blob);
605
606         return rv;
607 }
608
609 static void section_down(struct edit_dialog *edit)
610 {
611         switch (edit->section) {
612         case IN_NAME:
613                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
614                         edit->section = IN_DATA;
615                         set_current_field(edit->input, edit->field[1]);
616                 }
617                 break;
618         case IN_DATA:
619                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
620                         edit->section = IN_MENU;
621                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
622                 }
623                 break;
624         case IN_MENU:
625                 edit->section = IN_NAME;
626                 set_current_field(edit->input, edit->field[0]);
627                 break;
628         }
629 }
630
631 static void section_up(struct edit_dialog *edit)
632 {
633         switch (edit->section) {
634         case IN_NAME:
635                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
636                         edit->section = IN_MENU;
637                         menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
638                 }
639                 break;
640         case IN_DATA:
641                 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
642                         edit->section = IN_NAME;
643                         set_current_field(edit->input, edit->field[0]);
644                 }
645                 break;
646         case IN_MENU:
647                 edit->section = IN_DATA;
648                 set_current_field(edit->input, edit->field[1]);
649                 break;
650         }
651 }
652
653 WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, uint32_t type,
654                       const struct value_item *vitem, WINDOW *below)
655 {
656         struct edit_dialog *edit;
657         const char *choices[] = {
658                 "Ok",
659                 "Cancel",
660                 NULL
661         };
662         char *title;
663         int nlines, ncols, val_rows;
664         WERROR rv = WERR_NOMEM;
665         int selection;
666
667         edit = talloc_zero(ctx, struct edit_dialog);
668         if (edit == NULL) {
669                 return rv;
670         }
671         talloc_set_destructor(edit, edit_dialog_free);
672
673         title = talloc_asprintf(edit, "Edit %s value", str_regtype(type));
674         if (title == NULL) {
675                 goto finish;
676         }
677
678         nlines = 9;
679         if (type == REG_MULTI_SZ) {
680                 nlines += 4;
681         }
682         ncols = 50;
683         edit->dia = dialog_choice_center_new(edit, title, choices, nlines,
684                                              ncols, below);
685         talloc_free(title);
686         if (edit->dia == NULL) {
687                 goto finish;
688         }
689
690         /* name */
691         edit->field[0] = new_field(1, ncols - 4, 1, 1, 0, 0);
692         if (edit->field[0] == NULL) {
693                 goto finish;
694         }
695
696         /* data */
697         val_rows = 1;
698         if (type == REG_MULTI_SZ) {
699                 val_rows += 4;
700         }
701         edit->field[1] = new_field(val_rows, ncols - 4, 4, 1, 0, 0);
702         if (edit->field[1] == NULL) {
703                 goto finish;
704         }
705         set_field_back(edit->field[0], A_REVERSE);
706         set_field_back(edit->field[1], A_REVERSE);
707         field_opts_off(edit->field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
708         field_opts_off(edit->field[1], O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP);
709         if (type == REG_DWORD) {
710                 set_field_type(edit->field[1], TYPE_REGEXP,
711                                "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$");
712         }
713
714         if (vitem) {
715                 set_field_buffer(edit->field[0], 0, vitem->value_name);
716                 field_opts_off(edit->field[0], O_EDIT);
717                 fill_value_buffer(edit, vitem);
718         }
719
720         edit->input = new_form(edit->field);
721         if (edit->input == NULL) {
722                 goto finish;
723         }
724         form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD);
725
726         edit->input_win = derwin(edit->dia->sub_window, nlines - 3, ncols - 3,
727                                  0, 0);
728         if (edit->input_win == NULL) {
729                 goto finish;
730         }
731
732         set_form_win(edit->input, edit->dia->sub_window);
733         set_form_sub(edit->input, edit->input_win);
734         post_form(edit->input);
735         mvwprintw(edit->dia->sub_window, 0, 0, "Name");
736         mvwprintw(edit->dia->sub_window, 3, 0, "Data");
737
738         keypad(edit->dia->window, true);
739         update_panels();
740         doupdate();
741
742         edit->section = IN_NAME;
743
744         while (1) {
745                 int c = wgetch(edit->dia->window);
746                 if (c == '\t') {
747                         section_down(edit);
748                         continue;
749                 } else if (c == KEY_BTAB) {
750                         section_up(edit);
751                         continue;
752                 }
753
754                 if (edit->section == IN_NAME || edit->section == IN_DATA) {
755                         handle_form_input(edit->input, c);
756                 } else {
757                         selection = handle_menu_input(edit->dia->choices, c);
758                         if (selection == DIALOG_OK) {
759                                 rv = set_value(edit, key, type, vitem == NULL);
760                                 if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) {
761                                         dialog_notice(edit, DIA_ALERT,
762                                                       "Value exists", below,
763                                                       "Value name already exists.");
764                                         selection = -1;
765                                 } else {
766                                         goto finish;
767                                 }
768                         } else if (selection == DIALOG_CANCEL) {
769                                 rv = WERR_OK;
770                                 goto finish;
771                         }
772                 }
773
774                 update_panels();
775                 doupdate();
776         }
777
778 finish:
779         talloc_free(edit);
780
781         return rv;
782 }
783
784 int dialog_select_type(TALLOC_CTX *ctx, int *type, WINDOW *below)
785 {
786         struct dialog *dia;
787         const char *choices[] = {
788                 "OK",
789                 "Cancel",
790                 NULL
791         };
792         const char *reg_types[] = {
793                 "REG_DWORD",
794                 "REG_SZ",
795                 "REG_EXPAND_SZ",
796                 "REG_MULTI_SZ",
797         };
798 #define NTYPES (sizeof(reg_types) / sizeof(const char*))
799         ITEM **item;
800         MENU *list;
801         WINDOW *type_win;
802         int sel = -1;
803         size_t i;
804
805         dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20,
806                                        below);
807         if (dia == NULL) {
808                 return -1;
809         }
810
811         mvwprintw(dia->sub_window, 0, 0, "Choose type:");
812         type_win = derwin(dia->sub_window, 6, 18, 1, 0);
813         if (type_win == NULL) {
814                 goto finish;
815         }
816
817         item = talloc_zero_array(dia, ITEM *, NTYPES + 1);
818         if (item == NULL) {
819                 goto finish;
820         }
821
822         for (i = 0; i < NTYPES; ++i) {
823                 int t = regtype_by_string(reg_types[i]);
824
825                 item[i] = new_item(reg_types[i], reg_types[i]);
826                 if (item[i] == NULL) {
827                         goto finish;
828                 }
829                 set_item_userptr(item[i], (void*)(uintptr_t)t);
830         }
831
832         list = new_menu(item);
833         if (list == NULL) {
834                 goto finish;
835         }
836
837         set_menu_format(list, 7, 1);
838         set_menu_win(list, dia->sub_window);
839         set_menu_sub(list, type_win);
840         menu_opts_off(list, O_SHOWDESC);
841         set_menu_mark(list, "* ");
842         post_menu(list);
843
844         keypad(dia->window, true);
845         update_panels();
846         doupdate();
847
848         while (sel == -1) {
849                 ITEM *it;
850                 int c = wgetch(dia->window);
851
852                 switch (c) {
853                 case KEY_UP:
854                         menu_driver(list, REQ_UP_ITEM);
855                         break;
856                 case KEY_DOWN:
857                         menu_driver(list, REQ_DOWN_ITEM);
858                         break;
859                 case KEY_LEFT:
860                         menu_driver(dia->choices, REQ_LEFT_ITEM);
861                         break;
862                 case KEY_RIGHT:
863                         menu_driver(dia->choices, REQ_RIGHT_ITEM);
864                         break;
865                 case '\n':
866                 case KEY_ENTER:
867                         it = current_item(list);
868                         *type = (int)(uintptr_t)item_userptr(it);
869                         it = current_item(dia->choices);
870                         sel = (int)(uintptr_t)item_userptr(it);
871                         break;
872                 }
873         }
874
875 finish:
876         if (list) {
877                 unpost_menu(list);
878                 free_menu(list);
879         }
880         if (item) {
881                 ITEM **it;
882                 for (it = item; *it; ++it) {
883                         free_item(*it);
884                 }
885         }
886         if (type_win) {
887                 delwin(type_win);
888         }
889         talloc_free(dia);
890
891         return sel;
892 }