summaryrefslogtreecommitdiffstats
path: root/source3/utils/regedit_dialog.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/utils/regedit_dialog.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/utils/regedit_dialog.c')
-rw-r--r--source3/utils/regedit_dialog.c2328
1 files changed, 2328 insertions, 0 deletions
diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c
new file mode 100644
index 0000000..d1cb45f
--- /dev/null
+++ b/source3/utils/regedit_dialog.c
@@ -0,0 +1,2328 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "regedit.h"
+#include "regedit_dialog.h"
+#include "regedit_valuelist.h"
+#include "regedit_hexedit.h"
+#include "util_reg.h"
+#include "lib/registry/registry.h"
+#include "lib/util/smb_strtox.h"
+#include <stdarg.h>
+#include <form.h>
+
+static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
+{
+ char *str;
+
+ str = talloc_strndup(ctx, buf, n);
+
+ if (str) {
+ trim_string(str, " ", " ");
+ }
+
+ return str;
+}
+
+static char *string_trim(TALLOC_CTX *ctx, const char *buf)
+{
+ char *str;
+
+ str = talloc_strdup(ctx, buf);
+
+ if (str) {
+ trim_string(str, " ", " ");
+ }
+
+ return str;
+}
+
+static int dialog_free(struct dialog *dia)
+{
+ dialog_destroy(dia);
+
+ return 0;
+}
+
+static bool default_validator(struct dialog *dia, struct dialog_section *sect,
+ void *arg)
+{
+ return true;
+}
+
+struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title,
+ int y, int x)
+{
+ struct dialog *dia;
+
+ dia = talloc_zero(ctx, struct dialog);
+ if (dia == NULL) {
+ return NULL;
+ }
+
+ talloc_set_destructor(dia, dialog_free);
+
+ dia->title = talloc_strdup(dia, title);
+ if (dia->title == NULL) {
+ goto fail;
+ }
+ dia->x = x;
+ dia->y = y;
+ dia->color = color;
+ dia->submit = default_validator;
+
+ return dia;
+
+fail:
+ talloc_free(dia);
+
+ return NULL;
+
+}
+
+void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg)
+{
+ dia->submit = cb;
+ dia->submit_arg = arg;
+}
+
+static void center_above_window(int *nlines, int *ncols, int *y, int *x)
+{
+ int centery, centerx;
+
+ centery = LINES / 2;
+ centerx = COLS / 2;
+ *y = 0;
+ *x = 0;
+
+ if (*nlines > LINES) {
+ *nlines = LINES;
+ }
+ if (*ncols > COLS) {
+ *ncols = COLS;
+ }
+
+ if (*nlines/2 < centery) {
+ *y = centery - *nlines / 2;
+ }
+ if (*ncols/2 < centerx) {
+ *x = centerx - *ncols / 2;
+ }
+}
+
+void dialog_section_destroy(struct dialog_section *section)
+{
+ if (section->ops->destroy) {
+ section->ops->destroy(section);
+ }
+ if (section->window) {
+ delwin(section->window);
+ section->window = NULL;
+ }
+}
+
+void dialog_section_init(struct dialog_section *section,
+ const struct dialog_section_ops *ops,
+ int nlines, int ncols)
+{
+ section->ops = ops;
+ section->nlines = nlines;
+ section->ncols = ncols;
+}
+
+const char *dialog_section_get_name(struct dialog_section *section)
+{
+ return section->name;
+}
+
+void dialog_section_set_name(struct dialog_section *section, const char *name)
+{
+ TALLOC_FREE(section->name);
+ section->name = talloc_strdup(section, name);
+}
+
+void dialog_section_set_justify(struct dialog_section *section,
+ enum section_justify justify)
+{
+ section->justify = justify;
+}
+
+/* append a section to the dialog's circular list */
+void dialog_append_section(struct dialog *dia,
+ struct dialog_section *section)
+{
+ SMB_ASSERT(section != NULL);
+
+ if (!dia->head_section) {
+ dia->head_section = section;
+ }
+ if (dia->tail_section) {
+ dia->tail_section->next = section;
+ }
+ section->prev = dia->tail_section;
+ section->next = dia->head_section;
+ dia->head_section->prev = section;
+ dia->tail_section = section;
+}
+
+struct dialog_section *dialog_find_section(struct dialog *dia, const char *name)
+{
+ struct dialog_section *section = dia->head_section;
+
+ do {
+ if (section->name && strequal(section->name, name)) {
+ return section;
+ }
+ section = section->next;
+ } while (section != dia->head_section);
+
+ return NULL;
+}
+
+static void section_on_input(struct dialog *dia, int c)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section->ops->on_input) {
+ return;
+ }
+ section->ops->on_input(dia, section, c);
+}
+
+static bool section_on_tab(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_tab) {
+ return false;
+ }
+ return section->ops->on_tab(dia, section);
+}
+
+static bool section_on_btab(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_btab) {
+ return false;
+ }
+ return section->ops->on_btab(dia, section);
+}
+
+static bool section_on_up(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_up) {
+ return false;
+ }
+ return section->ops->on_up(dia, section);
+}
+
+static bool section_on_down(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_down) {
+ return false;
+ }
+ return section->ops->on_down(dia, section);
+}
+
+static bool section_on_left(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_left) {
+ return false;
+ }
+ return section->ops->on_left(dia, section);
+}
+
+static bool section_on_right(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_right) {
+ return false;
+ }
+ return section->ops->on_right(dia, section);
+}
+
+static enum dialog_action section_on_enter(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_enter) {
+ return DIALOG_OK;
+ }
+ return section->ops->on_enter(dia, section);
+}
+
+static bool section_on_focus(struct dialog *dia, bool forward)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section->ops->on_focus) {
+ return false;
+ }
+ return section->ops->on_focus(dia, section, forward);
+}
+
+static void section_on_leave_focus(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (section->ops->on_leave_focus) {
+ section->ops->on_leave_focus(dia, section);
+ }
+}
+
+static void section_set_next_focus(struct dialog *dia)
+{
+ section_on_leave_focus(dia);
+
+ do {
+ dia->current_section = dia->current_section->next;
+ } while (!section_on_focus(dia, true));
+}
+
+static void section_set_previous_focus(struct dialog *dia)
+{
+ section_on_leave_focus(dia);
+
+ do {
+ dia->current_section = dia->current_section->prev;
+ } while (!section_on_focus(dia, false));
+}
+
+WERROR dialog_create(struct dialog *dia)
+{
+ WERROR rv = WERR_OK;
+ int row, col;
+ int nlines, ncols;
+ struct dialog_section *section;
+
+ nlines = 0;
+ ncols = 0;
+ SMB_ASSERT(dia->head_section != NULL);
+
+ /* calculate total size based on sections */
+ section = dia->head_section;
+ do {
+ nlines += section->nlines;
+ ncols = MAX(ncols, section->ncols);
+ section = section->next;
+ } while (section != dia->head_section);
+
+ /* fill in widths for sections that expand */
+ section = dia->head_section;
+ do {
+ if (section->ncols < 0) {
+ section->ncols = ncols;
+ }
+ section = section->next;
+ } while (section != dia->head_section);
+
+ /* create window for dialog */
+ nlines += 4;
+ ncols += 6;
+ dia->pad = newpad(nlines, ncols);
+ if (dia->pad == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ dia->centered = false;
+ if (dia->y < 0 || dia->x < 0) {
+ dia->centered = true;
+ center_above_window(&nlines, &ncols, &dia->y, &dia->x);
+ }
+ dia->window = newwin(nlines, ncols, dia->y, dia->x);
+ if (dia->window == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ dia->panel = new_panel(dia->window);
+ if (dia->panel == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+
+ /* setup color and border */
+ getmaxyx(dia->pad, nlines, ncols);
+ wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color));
+ wclear(dia->pad);
+ mvwhline(dia->pad, 1, 2, 0, ncols - 4);
+ mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4);
+ mvwvline(dia->pad, 2, 1, 0, nlines - 4);
+ mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4);
+ mvwaddch(dia->pad, 1, 1, ACS_ULCORNER);
+ mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER);
+ mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER);
+ mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER);
+ col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2;
+ mvwprintw(dia->pad, 1, col, " %s ", dia->title);
+
+ /* create subwindows for each section */
+ row = 2;
+ section = dia->head_section;
+ do {
+ col = 3;
+
+ switch (section->justify) {
+ case SECTION_JUSTIFY_LEFT:
+ break;
+ case SECTION_JUSTIFY_CENTER:
+ col += (ncols - 6)/ 2 - section->ncols / 2;
+ break;
+ case SECTION_JUSTIFY_RIGHT:
+ break;
+ }
+
+ section->window = subpad(dia->pad, section->nlines,
+ section->ncols, row, col);
+ if (section->window == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ SMB_ASSERT(section->ops->create != NULL);
+ rv = section->ops->create(dia, section);
+ row += section->nlines;
+ section = section->next;
+ } while (section != dia->head_section && W_ERROR_IS_OK(rv));
+
+ dia->current_section = dia->head_section;
+ section_set_next_focus(dia);
+
+fail:
+ return rv;
+}
+
+void dialog_show(struct dialog *dia)
+{
+ int nlines, ncols;
+ int pad_y, pad_x;
+ int y, x;
+ int rv;
+
+ touchwin(dia->pad);
+ getmaxyx(dia->window, nlines, ncols);
+ getmaxyx(dia->pad, pad_y, pad_x);
+ y = 0;
+ if (pad_y > nlines) {
+ y = (pad_y - nlines) / 2;
+ }
+ x = 0;
+ if (pad_x > ncols) {
+ x = (pad_x - ncols) / 2;
+ }
+ rv = copywin(dia->pad, dia->window, y, x, 0, 0,
+ nlines - 1, ncols - 1, false);
+ SMB_ASSERT(rv == OK);
+
+ getyx(dia->pad, pad_y, pad_x);
+ wmove(dia->window, pad_y - y, pad_x - x);
+ touchwin(dia->window);
+ wnoutrefresh(dia->window);
+}
+
+void dialog_destroy(struct dialog *dia)
+{
+ struct dialog_section *section;
+
+ section = dia->head_section;
+ do {
+ dialog_section_destroy(section);
+ section = section->next;
+ } while (section != dia->head_section);
+
+ if (dia->panel) {
+ del_panel(dia->panel);
+ dia->panel = NULL;
+ }
+ if (dia->window) {
+ delwin(dia->window);
+ dia->window = NULL;
+ }
+}
+
+static int dialog_getch(struct dialog *dia)
+{
+ int c;
+
+ c = regedit_getch();
+ if (c == KEY_RESIZE) {
+ int nlines, ncols, y, x;
+ int pad_nlines, pad_ncols;
+ int win_nlines, win_ncols;
+
+ getmaxyx(dia->window, win_nlines, win_ncols);
+ getmaxyx(dia->pad, pad_nlines, pad_ncols);
+ getbegyx(dia->window, y, x);
+
+ nlines = pad_nlines;
+ ncols = pad_ncols;
+
+ if (dia->centered) {
+ center_above_window(&nlines, &ncols, &y, &x);
+ } else {
+ if (nlines + y > LINES) {
+ if (nlines > LINES) {
+ y = 0;
+ } else {
+ y = LINES - nlines;
+ }
+ }
+ if (ncols + x > COLS) {
+ if (ncols > COLS) {
+ x = 0;
+ } else {
+ x = COLS - ncols;
+ }
+ }
+ }
+ if (nlines != win_nlines || ncols != win_ncols) {
+ wresize(dia->window, nlines, ncols);
+ replace_panel(dia->panel, dia->window);
+ }
+ move_panel(dia->panel, y, x);
+ }
+
+ return c;
+}
+
+bool dialog_handle_input(struct dialog *dia, WERROR *err,
+ enum dialog_action *action)
+{
+ int c;
+
+ *err = WERR_OK;
+
+ c = dialog_getch(dia);
+
+ switch (c) {
+ case '\t':
+ if (!section_on_tab(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case KEY_BTAB:
+ if (!section_on_btab(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_UP:
+ if (!section_on_up(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_DOWN:
+ if (!section_on_down(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case KEY_LEFT:
+ if (!section_on_left(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_RIGHT:
+ if (!section_on_right(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case '\n':
+ case KEY_ENTER:
+ *action = section_on_enter(dia);
+ switch (*action) {
+ case DIALOG_IGNORE:
+ break;
+ case DIALOG_CANCEL:
+ return false;
+ case DIALOG_OK:
+ return !dia->submit(dia, dia->current_section,
+ dia->submit_arg);
+ }
+ break;
+ case 27: /* ESC */
+ return false;
+ default:
+ section_on_input(dia, c);
+ break;
+ }
+
+ return true;
+}
+
+void dialog_modal_loop(struct dialog *dia, WERROR *err,
+ enum dialog_action *action)
+{
+ do {
+ dialog_show(dia);
+ update_panels();
+ doupdate();
+ } while (dialog_handle_input(dia, err, action));
+}
+
+/* text label */
+struct dialog_section_label {
+ struct dialog_section section;
+ char **text;
+};
+
+static WERROR label_create(struct dialog *dia, struct dialog_section *section)
+{
+ int row;
+ struct dialog_section_label *label =
+ talloc_get_type_abort(section, struct dialog_section_label);
+
+ for (row = 0; row < section->nlines; ++row) {
+ mvwaddstr(section->window, row, 0, label->text[row]);
+ }
+
+ return WERR_OK;
+}
+
+struct dialog_section_ops label_ops = {
+ .create = label_create,
+};
+
+static int label_free(struct dialog_section_label *label)
+{
+ dialog_section_destroy(&label->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx,
+ const char *msg, va_list ap)
+{
+ struct dialog_section_label *label;
+ char *tmp, *ptmp, *line, *saveptr;
+ int nlines, ncols;
+
+ label = talloc_zero(ctx, struct dialog_section_label);
+ if (label == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(label, label_free);
+ tmp = talloc_vasprintf(label, msg, ap);
+ if (tmp == NULL) {
+ goto fail;
+ }
+
+ for (nlines = 0, ncols = 0, ptmp = tmp;
+ (line = strtok_r(ptmp, "\n", &saveptr)) != NULL;
+ ++nlines) {
+ ptmp = NULL;
+ label->text = talloc_realloc(label, label->text,
+ char *, nlines + 1);
+ if (label->text == NULL) {
+ goto fail;
+ }
+ ncols = MAX(ncols, strlen(line));
+ label->text[nlines] = talloc_strdup(label->text, line);
+ if (label->text[nlines] == NULL) {
+ goto fail;
+ }
+ }
+ talloc_free(tmp);
+ dialog_section_init(&label->section, &label_ops, nlines, ncols);
+
+ return &label->section;
+
+fail:
+ talloc_free(label);
+ return NULL;
+}
+
+struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx,
+ const char *msg, ...)
+{
+ va_list ap;
+ struct dialog_section *rv;
+
+ va_start(ap, msg);
+ rv = dialog_section_label_new_va(ctx, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+/* horizontal separator */
+struct dialog_section_hsep {
+ struct dialog_section section;
+ int sep;
+};
+
+static WERROR hsep_create(struct dialog *dia, struct dialog_section *section)
+{
+ int y, x;
+ struct dialog_section_hsep *hsep =
+ talloc_get_type_abort(section, struct dialog_section_hsep);
+
+ whline(section->window, hsep->sep, section->ncols);
+
+ if (hsep->sep == 0 || hsep->sep == ACS_HLINE) {
+ /* change the border characters around this section to
+ tee chars */
+ getparyx(section->window, y, x);
+ mvwaddch(dia->pad, y, x - 1, ACS_HLINE);
+ mvwaddch(dia->pad, y, x - 2, ACS_LTEE);
+ mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE);
+ mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE);
+ }
+
+ return WERR_OK;
+}
+
+struct dialog_section_ops hsep_ops = {
+ .create = hsep_create
+};
+
+static int hsep_free(struct dialog_section_hsep *hsep)
+{
+ dialog_section_destroy(&hsep->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep)
+{
+ struct dialog_section_hsep *hsep;
+
+ hsep = talloc_zero(ctx, struct dialog_section_hsep);
+ if (hsep) {
+ talloc_set_destructor(hsep, hsep_free);
+ dialog_section_init(&hsep->section, &hsep_ops, 1, -1);
+ hsep->sep = sep;
+ }
+
+ return &hsep->section;
+}
+
+/* text input field */
+struct dialog_section_text_field {
+ struct dialog_section section;
+ unsigned opts;
+ FIELD *field[2];
+ FORM *form;
+ int length;
+};
+
+static int get_cursor_col(struct dialog_section_text_field *field)
+{
+ int col;
+
+ col = field->form->curcol + field->form->begincol;
+
+ return col;
+}
+
+static WERROR text_field_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ text_field->field[0] = new_field(section->nlines, section->ncols,
+ 0, 0, 0, 0);
+ if (text_field->field[0] == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ set_field_back(text_field->field[0], A_REVERSE);
+ set_field_opts(text_field->field[0], text_field->opts);
+
+ text_field->form = new_form(text_field->field);
+ if (text_field->form == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ set_form_win(text_field->form, dia->window);
+ set_form_sub(text_field->form, section->window);
+ set_current_field(text_field->form, text_field->field[0]);
+ post_form(text_field->form);
+
+ return WERR_OK;
+}
+
+static void text_field_destroy(struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (text_field->form) {
+ unpost_form(text_field->form);
+ free_form(text_field->form);
+ text_field->form = NULL;
+ }
+ if (text_field->field[0]) {
+ free_field(text_field->field[0]);
+ text_field->field[0] = NULL;
+ }
+}
+
+static void text_field_on_input(struct dialog *dia,
+ struct dialog_section *section,
+ int c)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ switch (c) {
+ case KEY_BACKSPACE:
+ if (text_field->length) {
+ text_field->length--;
+ }
+ form_driver(text_field->form, REQ_DEL_PREV);
+ break;
+ case '\x7f':
+ case KEY_DC:
+ if (text_field->length) {
+ text_field->length--;
+ }
+ form_driver(text_field->form, REQ_DEL_CHAR);
+ break;
+ default:
+ text_field->length++;
+ form_driver(text_field->form, c);
+ break;
+ }
+}
+
+static bool text_field_on_up(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ form_driver(text_field->form, REQ_UP_CHAR);
+ return true;
+ }
+ return false;
+}
+
+static bool text_field_on_down(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ form_driver(text_field->form, REQ_DOWN_CHAR);
+ return true;
+ }
+ return false;
+}
+
+static bool text_field_on_left(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_LEFT_CHAR);
+
+ return true;
+}
+
+static bool text_field_on_right(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1 ||
+ get_cursor_col(text_field) < text_field->length) {
+ form_driver(text_field->form, REQ_RIGHT_CHAR);
+ }
+
+ return true;
+}
+
+static enum dialog_action text_field_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ text_field->length += text_field->form->cols;
+ form_driver(text_field->form, REQ_NEW_LINE);
+ return DIALOG_IGNORE;
+ }
+
+ return DIALOG_OK;
+}
+
+static bool text_field_on_focus(struct dialog *dia,
+ struct dialog_section *section, bool forward)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ pos_form_cursor(text_field->form);
+
+ return true;
+}
+
+struct dialog_section_ops text_field_ops = {
+ .create = text_field_create,
+ .destroy = text_field_destroy,
+ .on_input = text_field_on_input,
+ .on_up = text_field_on_up,
+ .on_down = text_field_on_down,
+ .on_left = text_field_on_left,
+ .on_right = text_field_on_right,
+ .on_enter = text_field_on_enter,
+ .on_focus = text_field_on_focus
+};
+
+static int text_field_free(struct dialog_section_text_field *text_field)
+{
+ dialog_section_destroy(&text_field->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx,
+ int height, int width)
+{
+ struct dialog_section_text_field *text_field;
+
+ text_field = talloc_zero(ctx, struct dialog_section_text_field);
+ if (text_field == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(text_field, text_field_free);
+ dialog_section_init(&text_field->section, &text_field_ops,
+ height, width);
+ text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK;
+
+ return &text_field->section;
+}
+
+const char *dialog_section_text_field_get(TALLOC_CTX *ctx,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ return string_trim(ctx, field_buffer(text_field->field[0], 0));
+}
+
+void dialog_section_text_field_set(struct dialog_section *section,
+ const char *s)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ text_field->length = strlen(s);
+ set_field_buffer(text_field->field[0], 0, s);
+}
+
+const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section)
+{
+ int rows, cols, max;
+ const char **arr;
+ size_t i;
+ const char *buf;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+ buf = field_buffer(text_field->field[0], 0);
+
+ dynamic_field_info(text_field->field[0], &rows, &cols, &max);
+
+ arr = talloc_zero_array(ctx, const char *, rows + 1);
+ if (arr == NULL) {
+ return NULL;
+ }
+ for (i = 0; *buf; ++i, buf += cols) {
+ SMB_ASSERT(i < rows);
+ arr[i] = string_trim_n(arr, buf, cols);
+ }
+
+ return arr;
+}
+
+WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section,
+ const char **array)
+{
+ int rows, cols, max;
+ size_t padding, length, idx;
+ const char **arrayp;
+ char *buf = NULL;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ dynamic_field_info(text_field->field[0], &rows, &cols, &max);
+ /* try to fit each string on it's own line. each line
+ needs to be padded with whitespace manually, since
+ ncurses fields do not have newlines. */
+ for (idx = 0, arrayp = array; *arrayp != NULL; ++arrayp) {
+ length = MIN(strlen(*arrayp), cols);
+ padding = cols - length;
+ buf = talloc_realloc(ctx, buf, char,
+ talloc_array_length(buf) +
+ length + padding + 1);
+ if (buf == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ memcpy(&buf[idx], *arrayp, length);
+ idx += length;
+ memset(&buf[idx], ' ', padding);
+ idx += padding;
+ buf[idx] = '\0';
+ }
+
+ set_field_buffer(text_field->field[0], 0, buf);
+ talloc_free(buf);
+
+ return WERR_OK;
+}
+
+bool dialog_section_text_field_get_int(struct dialog_section *section,
+ long long *out)
+{
+ bool rv;
+ const char *buf;
+ char *endp;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ buf = string_trim(section, field_buffer(text_field->field[0], 0));
+ if (buf == NULL) {
+ return false;
+ }
+ *out = strtoll(buf, &endp, 0);
+ rv = true;
+ if (endp == buf || endp == NULL || endp[0] != '\0') {
+ rv = false;
+ }
+
+ return rv;
+}
+
+
+bool dialog_section_text_field_get_uint(struct dialog_section *section,
+ unsigned long long *out)
+{
+ const char *buf;
+ int error = 0;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ buf = string_trim(section, field_buffer(text_field->field[0], 0));
+ if (buf == NULL) {
+ return false;
+ }
+ *out = smb_strtoull(buf, NULL, 0, &error, SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/* hex editor field */
+struct dialog_section_hexedit {
+ struct dialog_section section;
+ struct hexedit *buf;
+};
+
+#define HEXEDIT_MIN_SIZE 1
+static WERROR hexedit_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit->buf = hexedit_new(dia, section->window, NULL,
+ HEXEDIT_MIN_SIZE);
+ if (hexedit->buf == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ hexedit_refresh(hexedit->buf);
+
+ return WERR_OK;
+}
+
+static void hexedit_destroy(struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ if (hexedit->buf) {
+ TALLOC_FREE(hexedit->buf);
+ }
+}
+
+static void hexedit_on_input(struct dialog *dia,
+ struct dialog_section *section,
+ int c)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ switch (c) {
+ case KEY_BACKSPACE:
+ hexedit_driver(hexedit->buf, HE_BACKSPACE);
+ break;
+ case '\x7f':
+ case KEY_DC:
+ hexedit_driver(hexedit->buf, HE_DELETE);
+ break;
+ default:
+ hexedit_driver(hexedit->buf, c);
+ break;
+ }
+}
+
+static bool hexedit_on_up(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_UP);
+
+ return true;
+}
+
+static bool hexedit_on_down(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_DOWN);
+
+ return true;
+}
+
+static bool hexedit_on_left(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_LEFT);
+
+ return true;
+}
+
+static bool hexedit_on_right(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT);
+
+ return true;
+}
+
+static enum dialog_action hexedit_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ return DIALOG_IGNORE;
+}
+
+static bool hexedit_on_focus(struct dialog *dia,
+ struct dialog_section *section, bool forward)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_set_cursor(hexedit->buf);
+
+ return true;
+}
+
+struct dialog_section_ops hexedit_ops = {
+ .create = hexedit_create,
+ .destroy = hexedit_destroy,
+ .on_input = hexedit_on_input,
+ .on_up = hexedit_on_up,
+ .on_down = hexedit_on_down,
+ .on_left = hexedit_on_left,
+ .on_right = hexedit_on_right,
+ .on_enter = hexedit_on_enter,
+ .on_focus = hexedit_on_focus
+};
+
+static int hexedit_free(struct dialog_section_hexedit *hexedit)
+{
+ dialog_section_destroy(&hexedit->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height)
+{
+ struct dialog_section_hexedit *hexedit;
+
+ hexedit = talloc_zero(ctx, struct dialog_section_hexedit);
+ if (hexedit == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(hexedit, hexedit_free);
+ dialog_section_init(&hexedit->section, &hexedit_ops,
+ height, LINE_WIDTH);
+
+ return &hexedit->section;
+}
+
+WERROR dialog_section_hexedit_set_buf(struct dialog_section *section,
+ const void *data, size_t size)
+{
+ WERROR rv;
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+
+ rv = hexedit_set_buf(hexedit->buf, data, size);
+ if (W_ERROR_IS_OK(rv)) {
+ hexedit_refresh(hexedit->buf);
+ hexedit_set_cursor(hexedit->buf);
+ }
+
+ return rv;
+}
+
+void dialog_section_hexedit_get_buf(struct dialog_section *section,
+ const void **data, size_t *size)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+ *data = hexedit_get_buf(hexedit->buf);
+ *size = hexedit_get_buf_len(hexedit->buf);
+}
+
+WERROR dialog_section_hexedit_resize(struct dialog_section *section,
+ size_t size)
+{
+ WERROR rv;
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+ rv = hexedit_resize_buffer(hexedit->buf, size);
+ if (W_ERROR_IS_OK(rv)) {
+ hexedit_refresh(hexedit->buf);
+ }
+
+ return rv;
+}
+
+
+/* button box */
+struct dialog_section_buttons {
+ struct dialog_section section;
+ struct button_spec *spec;
+ int current_button;
+};
+
+static void buttons_unhighlight(struct dialog_section_buttons *buttons)
+{
+ short pair;
+ attr_t attr;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(buttons->section.window, &attr, &pair, NULL);
+ mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
+ wnoutrefresh(buttons->section.window);
+}
+
+static void buttons_highlight(struct dialog_section_buttons *buttons)
+{
+ struct button_spec *spec = &buttons->spec[buttons->current_button];
+ short pair;
+ attr_t attr;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(buttons->section.window, &attr, &pair, NULL);
+ mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
+ mvwchgat(buttons->section.window, 0, spec->col,
+ strlen(spec->label), A_REVERSE, pair, NULL);
+ wmove(buttons->section.window, 0, spec->col + 2);
+ wcursyncup(buttons->section.window);
+ wnoutrefresh(buttons->section.window);
+}
+
+static bool buttons_highlight_next(struct dialog_section_buttons *buttons)
+{
+ if (buttons->current_button < talloc_array_length(buttons->spec) - 1) {
+ buttons->current_button++;
+ buttons_highlight(buttons);
+ return true;
+ }
+ return false;
+}
+
+static bool buttons_highlight_previous(struct dialog_section_buttons *buttons)
+{
+ if (buttons->current_button > 0) {
+ buttons->current_button--;
+ buttons_highlight(buttons);
+ return true;
+ }
+ return false;
+}
+
+static WERROR buttons_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ size_t i, nbuttons;
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ nbuttons = talloc_array_length(buttons->spec);
+ for (i = 0; i < nbuttons; ++i) {
+ struct button_spec *spec = &buttons->spec[i];
+ mvwaddstr(section->window, 0, spec->col, spec->label);
+ }
+
+ buttons->current_button = 0;
+
+ return WERR_OK;
+}
+
+static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ return buttons_highlight_previous(buttons);
+}
+
+static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ return buttons_highlight_next(buttons);
+}
+
+static enum dialog_action buttons_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+ struct button_spec *spec = &buttons->spec[buttons->current_button];
+
+ if (spec->on_enter) {
+ return spec->on_enter(dia, section);
+ }
+
+ return spec->action;
+}
+
+static bool buttons_on_focus(struct dialog *dia,
+ struct dialog_section *section,
+ bool forward)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ if (forward) {
+ buttons->current_button = 0;
+ } else {
+ buttons->current_button = talloc_array_length(buttons->spec) - 1;
+ }
+ buttons_highlight(buttons);
+
+ return true;
+}
+
+static void buttons_on_leave_focus(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+ buttons_unhighlight(buttons);
+}
+
+struct dialog_section_ops buttons_ops = {
+ .create = buttons_create,
+ .on_tab = buttons_on_tab,
+ .on_btab = buttons_on_btab,
+ .on_up = buttons_on_btab,
+ .on_down = buttons_on_tab,
+ .on_left = buttons_on_btab,
+ .on_right = buttons_on_tab,
+ .on_enter = buttons_on_enter,
+ .on_focus = buttons_on_focus,
+ .on_leave_focus = buttons_on_leave_focus
+};
+
+static int buttons_free(struct dialog_section_buttons *buttons)
+{
+ dialog_section_destroy(&buttons->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx,
+ const struct button_spec *spec)
+{
+ struct dialog_section_buttons *buttons;
+ size_t i, nbuttons;
+ int width;
+
+ buttons = talloc_zero(ctx, struct dialog_section_buttons);
+ if (buttons == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(buttons, buttons_free);
+
+ for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) {
+ }
+ buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons);
+ if (buttons->spec == NULL) {
+ goto fail;
+ }
+
+ for (width = 0, i = 0; i < nbuttons; ++i) {
+ buttons->spec[i] = spec[i];
+ buttons->spec[i].label = talloc_asprintf(buttons->spec,
+ "[ %s ]",
+ spec[i].label);
+ if (!buttons->spec[i].label) {
+ goto fail;
+ }
+
+ buttons->spec[i].col = width;
+ width += strlen(buttons->spec[i].label);
+ if (i != nbuttons - 1) {
+ ++width;
+ }
+ }
+
+ dialog_section_init(&buttons->section, &buttons_ops, 1, width);
+
+ return &buttons->section;
+
+fail:
+ talloc_free(buttons);
+ return NULL;
+}
+
+/* options */
+struct dialog_section_options {
+ struct dialog_section section;
+ struct option_spec *spec;
+ int current_option;
+ bool single_select;
+};
+
+static void options_unhighlight(struct dialog_section_options *options)
+{
+ short pair;
+ attr_t attr;
+ size_t row;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(options->section.window, &attr, &pair, NULL);
+ for (row = 0; row < options->section.nlines; ++row) {
+ mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
+ }
+ wnoutrefresh(options->section.window);
+}
+
+static void options_highlight(struct dialog_section_options *options)
+{
+ struct option_spec *spec = &options->spec[options->current_option];
+ short pair;
+ attr_t attr;
+ size_t row;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(options->section.window, &attr, &pair, NULL);
+ for (row = 0; row < options->section.nlines; ++row) {
+ mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
+ }
+ mvwchgat(options->section.window, spec->row, spec->col,
+ strlen(spec->label), A_REVERSE, pair, NULL);
+ wmove(options->section.window, spec->row, spec->col + 4);
+ wcursyncup(options->section.window);
+ wnoutrefresh(options->section.window);
+}
+
+static void options_render_state(struct dialog_section_options *options)
+{
+ size_t i, noptions;
+
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ struct option_spec *spec = &options->spec[i];
+ char c = ' ';
+ if (*spec->state)
+ c = 'x';
+ mvwaddch(options->section.window,
+ spec->row, spec->col + 1, c);
+ wnoutrefresh(options->section.window);
+ }
+}
+
+static bool options_highlight_next(struct dialog_section_options *options)
+{
+ if (options->current_option < talloc_array_length(options->spec) - 1) {
+ options->current_option++;
+ options_highlight(options);
+ return true;
+ }
+ return false;
+}
+
+static bool options_highlight_previous(struct dialog_section_options *options)
+{
+ if (options->current_option > 0) {
+ options->current_option--;
+ options_highlight(options);
+ return true;
+ }
+ return false;
+}
+
+static WERROR options_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ size_t i, noptions;
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ struct option_spec *spec = &options->spec[i];
+ mvwaddstr(section->window, spec->row, spec->col,
+ spec->label);
+ }
+
+ options->current_option = 0;
+ options_render_state(options);
+
+ return WERR_OK;
+}
+
+static bool options_on_btab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ return options_highlight_previous(options);
+}
+
+static bool options_on_tab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ return options_highlight_next(options);
+}
+
+static void options_on_input(struct dialog *dia, struct dialog_section *section, int c)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ if (c == ' ') {
+ struct option_spec *spec = &options->spec[options->current_option];
+ if (options->single_select) {
+ size_t i, noptions;
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ *(options->spec[i].state) = false;
+ }
+ }
+ *spec->state = !*spec->state;
+ options_unhighlight(options);
+ options_render_state(options);
+ options_highlight(options);
+ }
+}
+
+static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section)
+{
+ options_on_input(dia, section, ' ');
+ return DIALOG_OK;
+}
+
+static bool options_on_focus(struct dialog *dia,
+ struct dialog_section *section,
+ bool forward)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ if (forward) {
+ options->current_option = 0;
+ } else {
+ options->current_option = talloc_array_length(options->spec) - 1;
+ }
+ options_highlight(options);
+
+ return true;
+}
+
+static void options_on_leave_focus(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+ options_unhighlight(options);
+}
+
+struct dialog_section_ops options_ops = {
+ .create = options_create,
+ .on_tab = options_on_tab,
+ .on_btab = options_on_btab,
+ .on_up = options_on_btab,
+ .on_down = options_on_tab,
+ .on_left = options_on_btab,
+ .on_right = options_on_tab,
+ .on_input = options_on_input,
+ .on_enter = options_on_enter,
+ .on_focus = options_on_focus,
+ .on_leave_focus = options_on_leave_focus
+};
+
+static int options_free(struct dialog_section_options *options)
+{
+ dialog_section_destroy(&options->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx,
+ const struct option_spec *spec,
+ int maxcol, bool single_select)
+{
+ struct dialog_section_options *options;
+ size_t i, noptions;
+ int width, maxwidth, maxrows;
+
+ options = talloc_zero(ctx, struct dialog_section_options);
+ if (options == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(options, options_free);
+
+ for (noptions = 0; spec[noptions].label; ++noptions) {
+ }
+ options->spec = talloc_zero_array(options, struct option_spec, noptions);
+ if (options->spec == NULL) {
+ goto fail;
+ }
+
+ maxrows = noptions / maxcol;
+ if (noptions % maxcol) {
+ ++maxrows;
+ }
+
+ for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) {
+ options->spec[i] = spec[i];
+ options->spec[i].label = talloc_asprintf(options->spec,
+ "[ ] %s",
+ spec[i].label);
+ if (!options->spec[i].label) {
+ goto fail;
+ }
+
+ options->spec[i].col = maxwidth;
+ options->spec[i].row = i % maxrows;
+ width = MAX(strlen(options->spec[i].label), width);
+ if (options->spec[i].row == maxrows - 1 || i == noptions - 1) {
+ maxwidth += width + 1;
+ width = 0;
+ }
+ }
+
+ dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1);
+ options->single_select = single_select;
+
+ return &options->section;
+
+fail:
+ talloc_free(options);
+ return NULL;
+}
+
+
+enum input_type {
+ DLG_IN_LONG,
+ DLG_IN_ULONG,
+ DLG_IN_STR,
+};
+
+struct input_req {
+ TALLOC_CTX *ctx;
+ enum input_type type;
+ union {
+ void *out;
+ unsigned long *out_ulong;
+ long *out_long;
+ const char **out_str;
+ } out;
+};
+
+static bool input_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct input_req *req = arg;
+ struct dialog_section *data;
+ unsigned long long out_ulong;
+ long long out_long;
+
+ data = dialog_find_section(dia, "input");
+
+ switch (req->type) {
+ case DLG_IN_LONG:
+ if (!dialog_section_text_field_get_int(data, &out_long)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Input must be a number.");
+ return false;
+ }
+ if (out_long < LONG_MIN || out_long > LONG_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Number is out of range.");
+ return false;
+ }
+ *req->out.out_long = out_long;
+ break;
+ case DLG_IN_ULONG:
+ if (!dialog_section_text_field_get_uint(data, &out_ulong)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Input must be a number greater than zero.");
+ return false;
+ }
+ if (out_ulong > ULONG_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Number is out of range.");
+ return false;
+ }
+ *req->out.out_ulong = out_ulong;
+ break;
+ case DLG_IN_STR:
+ *req->out.out_str = dialog_section_text_field_get(req->ctx, data);
+ break;
+ }
+
+ return true;
+}
+
+static int dialog_input_internal(TALLOC_CTX *ctx, void *output,
+ enum input_type type,
+ const char *title,
+ const char *msg, va_list ap)
+ PRINTF_ATTRIBUTE(5,0);
+
+static int dialog_input_internal(TALLOC_CTX *ctx, void *output,
+ enum input_type type,
+ const char *title,
+ const char *msg, va_list ap)
+{
+ WERROR err;
+ struct input_req req;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct button_spec spec[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+
+ req.ctx = ctx;
+ req.type = type;
+ req.out.out = output;
+ *req.out.out_str = NULL;
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
+ dialog_set_submit_cb(dia, input_on_submit, &req);
+ section = dialog_section_label_new_va(dia, msg, ap);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+ section = dialog_section_text_field_new(dia, 1, -1);
+ dialog_section_set_name(section, "input");
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}
+
+int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title,
+ const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_STR, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_ULONG, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_input_long(TALLOC_CTX *ctx, long *output,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_LONG, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct button_spec spec[3];
+
+ memset(&spec, '\0', sizeof(spec));
+ spec[0].label = "OK";
+ spec[0].action = DIALOG_OK;
+ if (type == DIA_CONFIRM) {
+ spec[1].label = "Cancel";
+ spec[1].action = DIALOG_CANCEL;
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
+ va_start(ap, msg);
+ section = dialog_section_label_new_va(dia, msg, ap);
+ va_end(ap);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}
+
+
+struct edit_req {
+ uint32_t type;
+ uint32_t mode;
+ struct registry_key *key;
+ const struct value_item *vitem;
+};
+
+static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit)
+{
+ char *tmp;
+ struct dialog_section *data;
+
+ if (edit->vitem == NULL) {
+ return WERR_OK;
+ }
+
+ data = dialog_find_section(dia, "data");
+ SMB_ASSERT(data != NULL);
+
+ switch (edit->mode) {
+ case REG_DWORD: {
+ uint32_t v = 0;
+ if (edit->vitem->data.length >= 4) {
+ v = IVAL(edit->vitem->data.data, 0);
+ }
+ tmp = talloc_asprintf(dia, "%u", (unsigned)v);
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dialog_section_text_field_set(data, tmp);
+ talloc_free(tmp);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *s;
+
+ if (!pull_reg_sz(dia, &edit->vitem->data, &s)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dialog_section_text_field_set(data, s);
+ break;
+ }
+ case REG_MULTI_SZ: {
+ const char **array;
+
+ if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ return dialog_section_text_field_set_lines(dia, data, array);
+ }
+ case REG_BINARY:
+ default:
+ return dialog_section_hexedit_set_buf(data,
+ edit->vitem->data.data,
+ edit->vitem->data.length);
+ }
+
+ return WERR_OK;
+}
+
+static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
+ const char *name)
+{
+ uint32_t type;
+ DATA_BLOB blob;
+ WERROR rv;
+
+ rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
+
+ return W_ERROR_IS_OK(rv);
+}
+
+static bool edit_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct edit_req *edit = arg;
+ WERROR rv;
+ DATA_BLOB blob;
+ const char *name;
+ struct dialog_section *name_section, *data;
+
+ name_section = dialog_find_section(dia, "name");
+ if (name_section) {
+ name = dialog_section_text_field_get(dia, name_section);
+ if (*name == '\0') {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Value name must not be blank.");
+ return false;
+ }
+ if (value_exists(dia, edit->key, name)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Value named \"%s\" already exists.",
+ name);
+ return false;
+ }
+ } else {
+ SMB_ASSERT(edit->vitem);
+ name = edit->vitem->value_name;
+ }
+ SMB_ASSERT(name);
+
+ data = dialog_find_section(dia, "data");
+ SMB_ASSERT(data != NULL);
+
+ rv = WERR_OK;
+ switch (edit->mode) {
+ case REG_DWORD: {
+ unsigned long long v;
+ uint32_t val;
+
+ if (!dialog_section_text_field_get_uint(data, &v)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "REG_DWORD value must be an integer.");
+ return false;
+ }
+ if (v > UINT32_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "REG_DWORD value must less than %lu.",
+ (unsigned long)UINT32_MAX);
+ return false;
+ }
+ val = (uint32_t)v;
+ blob = data_blob_talloc(dia, NULL, sizeof(val));
+ SIVAL(blob.data, 0, val);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *buf;
+
+ buf = dialog_section_text_field_get(dia, data);
+ if (!buf || !push_reg_sz(dia, &blob, buf)) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ }
+ case REG_MULTI_SZ: {
+ const char **lines;
+
+ lines = dialog_section_text_field_get_lines(dia, data);
+ if (!lines || !push_reg_multi_sz(dia, &blob, lines)) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ }
+ case REG_BINARY: {
+ const void *buf;
+ size_t len;
+
+ dialog_section_hexedit_get_buf(data, &buf, &len);
+ blob = data_blob_talloc(dia, buf, len);
+ break;
+ }
+ }
+
+ if (W_ERROR_IS_OK(rv)) {
+ rv = reg_val_set(edit->key, name, edit->type, blob);
+ }
+
+ if (!W_ERROR_IS_OK(rv)) {
+ const char *msg = get_friendly_werror_msg(rv);
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Error saving value:\n%s", msg);
+
+ return false;
+ }
+
+ return true;
+
+}
+
+static enum dialog_action edit_on_resize(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section *data;
+ unsigned long size;
+ int rv;
+
+ data = dialog_find_section(dia, "data");
+ rv = dialog_input_ulong(dia, &size, "Resize", "Enter size of buffer");
+ if (rv == DIALOG_OK) {
+ dialog_section_hexedit_resize(data, size);
+ }
+
+ return DIALOG_IGNORE;
+}
+
+int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key,
+ uint32_t type, const struct value_item *vitem,
+ bool force_binary, WERROR *err,
+ const char **name)
+{
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct edit_req edit;
+ struct button_spec buttons[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ struct button_spec buttons_hexedit[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Resize Buffer", .on_enter = edit_on_resize},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+
+
+ edit.key = key;
+ edit.vitem = vitem;
+ edit.type = type;
+ edit.mode = type;
+ if (force_binary || (vitem && vitem->unprintable)) {
+ edit.mode = REG_BINARY;
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1);
+ dialog_set_submit_cb(dia, edit_on_submit, &edit);
+
+ section = dialog_section_label_new(dia, "Type");
+ dialog_append_section(dia, section);
+ section = dialog_section_label_new(dia, "%s",
+ str_regtype(type));
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+
+ section = dialog_section_label_new(dia, "Name");
+ dialog_append_section(dia, section);
+ if (vitem) {
+ section = dialog_section_label_new(dia, "%s",
+ vitem->value_name);
+ } else {
+ section = dialog_section_text_field_new(dia, 1, 50);
+ dialog_section_set_name(section, "name");
+ }
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+
+ section = dialog_section_label_new(dia, "Data");
+ dialog_append_section(dia, section);
+
+ switch (edit.mode) {
+ case REG_DWORD:
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ section = dialog_section_text_field_new(dia, 1, 50);
+ break;
+ case REG_MULTI_SZ:
+ section = dialog_section_text_field_new(dia, 10, 50);
+ break;
+ case REG_BINARY:
+ default:
+ section = dialog_section_hexedit_new(dia, 10);
+ break;
+ }
+
+ dialog_section_set_name(section, "data");
+ dialog_append_section(dia, section);
+
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ if (edit.mode == REG_BINARY) {
+ section = dialog_section_buttons_new(dia, buttons_hexedit);
+ } else {
+ section = dialog_section_buttons_new(dia, buttons);
+ }
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+
+ *err = fill_value_buffer(dia, &edit);
+ if (!W_ERROR_IS_OK(*err)) {
+ return DIALOG_CANCEL;
+ }
+
+ dialog_show(dia);
+ dialog_modal_loop(dia, err, &action);
+
+ if (action == DIALOG_OK && name) {
+ if (vitem) {
+ *name = talloc_strdup(ctx, vitem->value_name);
+ } else if ((section = dialog_find_section(dia, "name"))) {
+ *name = dialog_section_text_field_get(ctx, section);
+ }
+ }
+
+ talloc_free(dia);
+
+ return action;
+}
+
+int dialog_select_type(TALLOC_CTX *ctx, int *type)
+{
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ const char *reg_types[] = {
+ "REG_BINARY",
+ "REG_DWORD",
+ "REG_EXPAND_SZ",
+ "REG_MULTI_SZ",
+ "REG_SZ"
+ };
+ #define NTYPES ARRAY_SIZE(reg_types)
+ struct button_spec spec[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ bool flags[NTYPES] = { true };
+ struct option_spec opsec[NTYPES + 1];
+ unsigned i;
+
+ memset(&opsec, '\0', sizeof(opsec));
+ for (i = 0; i < NTYPES; ++i) {
+ opsec[i].label = reg_types[i];
+ opsec[i].state = &flags[i];
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1);
+
+ section = dialog_section_label_new(dia, "Select type for new value:");
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+ section = dialog_section_options_new(dia, opsec, 2, true);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+
+ dialog_modal_loop(dia, &err, &action);
+ if (action == DIALOG_OK) {
+ for (i = 0; i < NTYPES; ++i) {
+ if (flags[i]) {
+ *type = regtype_by_string(reg_types[i]);
+ break;
+ }
+ }
+ }
+
+ talloc_free(dia);
+
+ return action;
+}
+
+struct search_req {
+ TALLOC_CTX *ctx;
+ struct regedit_search_opts *opts;
+};
+
+static bool search_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct search_req *search = arg;
+ struct dialog_section *query;
+
+ query = dialog_find_section(dia, "query");
+ SMB_ASSERT(query != NULL);
+
+ if (!search->opts->search_key && !search->opts->search_value) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Must search a key and/or a value");
+ return false;
+ }
+
+ talloc_free(discard_const(search->opts->query));
+ search->opts->query = dialog_section_text_field_get(search->ctx, query);
+ SMB_ASSERT(search->opts->query != NULL);
+ if (search->opts->query[0] == '\0') {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Query must not be blank.");
+ return false;
+ }
+
+ return true;
+}
+
+int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts)
+{
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section, *query;
+ struct search_req search;
+ struct button_spec spec[] = {
+ {.label = "Search", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ struct option_spec search_opts[] = {
+ {.label = "Search Keys", .state = &opts->search_key},
+ {.label = "Search Values", .state = &opts->search_value},
+ {.label = "Recursive", .state = &opts->search_recursive},
+ {.label = "Case Sensitive", .state = &opts->search_case},
+ { 0 }
+ };
+
+ if (!opts->search_key && !opts->search_value) {
+ opts->search_key = true;
+ }
+
+ search.ctx = ctx;
+ search.opts = opts;
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1);
+ dialog_set_submit_cb(dia, search_on_submit, &search);
+ section = dialog_section_label_new(dia, "Query");
+ dialog_append_section(dia, section);
+ query = dialog_section_text_field_new(dia, 1, -1);
+ dialog_section_set_name(query, "query");
+ dialog_append_section(dia, query);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_options_new(dia, search_opts, 2, false);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ if (opts->query) {
+ dialog_section_text_field_set(query, opts->query);
+ }
+
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}