]> git.ozlabs.org Git - petitboot/blobdiff - ui/ncurses/nc-widgets.c
discover/grub2: Allow to separate the --id argument using a space char
[petitboot] / ui / ncurses / nc-widgets.c
index 8f8816ecaa7c472b33fe9c52d0e0305556b6c621..afd56d71cf90b79d83ee8a07fd4bdfc466e1b508 100644 (file)
 #  error "Curses form.h not found."
 #endif
 
-#include <string.h>
+#include <arpa/inet.h>
 #include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <talloc/talloc.h>
 #include <types/types.h>
@@ -53,6 +55,7 @@
 #include <util/util.h>
 #include <i18n/i18n.h>
 #include <fold/fold.h>
+#include <url/url.h>
 
 #include "nc-cui.h"
 #include "nc-widgets.h"
@@ -82,7 +85,9 @@ struct nc_widgetset {
        FIELD   *cur_field;
 
        /* custom validators */
-       FIELDTYPE *ipv4_multi_type;
+       FIELDTYPE *ip_multi_type;
+       FIELDTYPE *ip_type;
+       FIELDTYPE *url_type;
 };
 
 struct nc_widget {
@@ -337,6 +342,14 @@ static bool textbox_process_key(
        case KEY_DC:
                form_driver(form, REQ_DEL_CHAR);
                break;
+       case '\t':
+       case KEY_BTAB:
+       case KEY_UP:
+       case KEY_DOWN:
+       case KEY_PPAGE:
+       case KEY_NPAGE:
+               /* Don't catch navigational keys */
+               return false;
        default:
                form_driver(form, key);
                break;
@@ -352,8 +365,8 @@ static int textbox_destructor(void *ptr)
        return 0;
 }
 
-struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
-               int y, int x, int len, char *str)
+struct nc_widget_textbox *widget_new_textbox_hidden(struct nc_widgetset *set,
+               int y, int x, int len, char *str, bool hide_input)
 {
        struct nc_widget_textbox *textbox;
        FIELD *f;
@@ -370,6 +383,8 @@ struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
        textbox->widget.unfocussed_attr = A_UNDERLINE;
 
        field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
+       if (hide_input)
+               field_opts_off(f, O_PUBLIC);
        set_field_buffer(f, 0, str);
        set_field_back(f, textbox->widget.unfocussed_attr);
        set_field_userptr(f, &textbox->widget);
@@ -380,6 +395,12 @@ struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
        return textbox;
 }
 
+struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
+               int y, int x, int len, char *str)
+{
+       return widget_new_textbox_hidden(set, y, x, len, str, false);
+}
+
 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
 {
        field_opts_on(textbox->widget.field, O_STATIC);
@@ -391,54 +412,102 @@ void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
        set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
 }
 
-void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
+static bool check_url_field(FIELD *field,
+               const void *arg __attribute__((unused)))
+{
+       return is_url(field_buffer(field, 0));
+}
+
+void widget_textbox_set_validator_url(struct nc_widget_textbox *textbox)
 {
-       set_field_type(textbox->widget.field, TYPE_IPV4);
+       if (!textbox->set->url_type)
+               textbox->set->url_type = new_fieldtype(check_url_field, NULL);
+
+       set_field_type(textbox->widget.field, textbox->set->url_type);
 }
 
-static bool check_ipv4_multi_char(int c,
+static bool check_ip_field(FIELD *field,
                const void *arg __attribute__((unused)))
+{
+       char *str;
+       int rc;
+
+       str = strip_string(field_buffer(field, 0));
+
+       rc = addr_scheme(str);
+
+       return (rc == AF_INET || rc == AF_INET6);
+}
+
+
+static bool check_ipv6_multi_char(int c)
+{
+       return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
+              c == ':' || c == ' ';
+}
+
+static bool check_ipv4_multi_char(int c)
 {
        return isdigit(c) || c == '.' || c == ' ';
 }
 
-static bool check_ipv4_multi_field(FIELD *field,
+static bool check_ip_multi_field(FIELD *field,
                const void *arg __attribute__((unused)))
 {
-       char *buf = field_buffer(field, 0);
-       unsigned int ip[4];
-       int n, len;
-
-       while (*buf != '\0') {
-               n = sscanf(buf, "%u.%u.%u.%u%n",
-                               &ip[0], &ip[1], &ip[2], &ip[3], &len);
-               if (n != 4)
-                       return false;
-
-               if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
-                       return false;
-
-               for (buf += len; *buf != '\0'; buf++) {
-                       if (isspace(*buf))
-                               continue;
-                       else if (isdigit(*buf))
-                               break;
-                       else
-                               return false;
-               }
+       char *buf, *tok, *saveptr;
+       bool result;
+       int type;
+
+       /* Use strdup directly since we can't easily reach a talloc parent */
+       buf = strdup(strip_string(field_buffer(field, 0)));
+       if (!buf)
+               /* We tried */
+               return true;
+
+       result = false;
+       tok = strtok_r(buf, " ", &saveptr);
+       if (!tok || *tok == '\0')
+               goto err;
+
+       while (tok) {
+               type = addr_scheme(tok);
+               if (!(type == AF_INET || type == AF_INET6))
+                       goto err;
+
+               tok = strtok_r(NULL, " ", &saveptr);
        }
+       result = true;
 
-       return true;
+err:
+       free(buf);
+       return result;
 }
 
-void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
+static bool check_ip_multi_char(int c, const void *arg __attribute__((unused)))
 {
-       if (!textbox->set->ipv4_multi_type) {
-               textbox->set->ipv4_multi_type = new_fieldtype(
-                               check_ipv4_multi_field,
-                               check_ipv4_multi_char);
+       return (check_ipv4_multi_char(c) || check_ipv6_multi_char(c));
+}
+
+void widget_textbox_set_validator_ip(struct nc_widget_textbox *textbox)
+{
+       if (!textbox->set->ip_type) {
+               textbox->set->ip_type = new_fieldtype(check_ip_field, NULL);
        }
-       set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
+       set_field_type(textbox->widget.field, textbox->set->ip_type);
+}
+
+/*
+ * In a perfect world we would use link_fieldtype() but segfaults do not
+ * enhance the user experience.
+ */
+void widget_textbox_set_validator_ip_multi(struct nc_widget_textbox *textbox)
+{
+       if (!textbox->set->ip_multi_type) {
+               textbox->set->ip_multi_type = new_fieldtype(
+                                               check_ip_multi_field,
+                                               check_ip_multi_char);
+       }
+       set_field_type(textbox->widget.field, textbox->set->ip_multi_type);
 }
 
 static void subset_update_order(struct nc_widget_subset *subset)
@@ -1110,6 +1179,12 @@ bool widgetset_process_key(struct nc_widgetset *set, int key)
        field = current_field(set->form);
        assert(field);
 
+       widget = field_userptr(field);
+
+       if (widget->process_key)
+               if (widget->process_key(widget, set->form, key))
+                       return true;
+
        tab = false;
 
        /* handle field change events */
@@ -1118,14 +1193,12 @@ bool widgetset_process_key(struct nc_widgetset *set, int key)
                tab = true;
                /* fall through */
        case KEY_UP:
-       case KEY_LEFT:
                req = REQ_SPREV_FIELD;
                break;
        case '\t':
                tab = true;
                /* fall through */
        case KEY_DOWN:
-       case KEY_RIGHT:
                req = REQ_SNEXT_FIELD;
                break;
        case KEY_PPAGE:
@@ -1134,9 +1207,14 @@ bool widgetset_process_key(struct nc_widgetset *set, int key)
        case KEY_NPAGE:
                req = REQ_SLAST_FIELD;
                break;
+       case KEY_LEFT:
+               req = REQ_LEFT_FIELD;
+               break;
+       case KEY_RIGHT:
+               req = REQ_RIGHT_FIELD;
+               break;
        }
 
-       widget = field_userptr(field);
        if (req) {
                widget_focus_change(widget, field, false);
                form_driver(set->form, req);
@@ -1161,18 +1239,19 @@ bool widgetset_process_key(struct nc_widgetset *set, int key)
                return true;
        }
 
-       if (!widget->process_key)
-               return false;
-
-       return widget->process_key(widget, set->form, key);
+       return false;
 }
 
 static int widgetset_destructor(void *ptr)
 {
        struct nc_widgetset *set = ptr;
        free_form(set->form);
-       if (set->ipv4_multi_type)
-               free_fieldtype(set->ipv4_multi_type);
+       if (set->ip_type)
+               free_fieldtype(set->ip_type);
+       if (set->ip_multi_type)
+               free_fieldtype(set->ip_multi_type);
+       if (set->url_type)
+               free_fieldtype(set->url_type);
        return 0;
 }