]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[hci] Use dynamically allocated buffers for editable strings
authorMichael Brown <mcb30@ipxe.org>
Mon, 15 Apr 2024 14:59:49 +0000 (15:59 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 15 Apr 2024 14:59:49 +0000 (15:59 +0100)
Editable strings currently require a fixed-size buffer, which is
inelegant and limits the potential for creating interactive forms with
a variable number of edit box widgets.

Remove this limitation by switching to using a dynamically allocated
buffer for editable strings and edit box widgets.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/hci/editstring.c
src/hci/mucurses/widgets/editbox.c
src/hci/readline.c
src/hci/tui/login_ui.c
src/hci/tui/settings_ui.c
src/include/ipxe/editbox.h
src/include/ipxe/editstring.h
src/include/ipxe/errfile.h

index 8cbce076723695ad60d134df2712b0298c9dd7de..b83916c6ec357a170133b55dfab9efb05db62ca3 100644 (file)
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <assert.h>
+#include <errno.h>
 #include <string.h>
 #include <ctype.h>
+#include <stdlib.h>
 #include <ipxe/keys.h>
 #include <ipxe/editstring.h>
 
@@ -35,17 +37,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
-static void insert_delete ( struct edit_string *string, size_t delete_len,
-                            const char *insert_text ) 
-                           __attribute__ (( nonnull (1) ));
-static void insert_character ( struct edit_string *string,
-                               unsigned int character ) __nonnull;
-static void delete_character ( struct edit_string *string ) __nonnull;
-static void backspace ( struct edit_string *string ) __nonnull;
-static void previous_word ( struct edit_string *string ) __nonnull;
-static void kill_word ( struct edit_string *string ) __nonnull;
-static void kill_sol ( struct edit_string *string ) __nonnull;
-static void kill_eol ( struct edit_string *string ) __nonnull;
+static __attribute__ (( nonnull ( 1 ) )) int
+insert_delete ( struct edit_string *string, size_t delete_len,
+               const char *insert_text );
+static __nonnull int insert_character ( struct edit_string *string,
+                                       unsigned int character );
+static __nonnull void delete_character ( struct edit_string *string );
+static __nonnull void backspace ( struct edit_string *string );
+static __nonnull void previous_word ( struct edit_string *string );
+static __nonnull void kill_word ( struct edit_string *string );
+static __nonnull void kill_sol ( struct edit_string *string );
+static __nonnull void kill_eol ( struct edit_string *string );
 
 /**
  * Insert and/or delete text within an editable string
@@ -53,35 +55,56 @@ static void kill_eol ( struct edit_string *string ) __nonnull;
  * @v string           Editable string
  * @v delete_len       Length of text to delete from current cursor position
  * @v insert_text      Text to insert at current cursor position, or NULL
+ * @ret rc             Return status code
  */
-static void insert_delete ( struct edit_string *string, size_t delete_len,
-                           const char *insert_text ) {
-       size_t old_len, max_delete_len, insert_len, max_insert_len, new_len;
+static int insert_delete ( struct edit_string *string, size_t delete_len,
+                          const char *insert_text ) {
+       size_t old_len, max_delete_len, insert_len, new_len;
+       char *buf;
+       char *tmp;
 
        /* Calculate lengths */
-       old_len = strlen ( string->buf );
+       buf = *(string->buf);
+       old_len = ( buf ? strlen ( buf ) : 0 );
        assert ( string->cursor <= old_len );
        max_delete_len = ( old_len - string->cursor );
        if ( delete_len > max_delete_len )
                delete_len = max_delete_len;
        insert_len = ( insert_text ? strlen ( insert_text ) : 0 );
-       max_insert_len = ( ( string->len - 1 ) - ( old_len - delete_len ) );
-       if ( insert_len > max_insert_len )
-               insert_len = max_insert_len;
        new_len = ( old_len - delete_len + insert_len );
 
+       /* Reallocate string, ignoring failures if shrinking */
+       tmp = realloc ( buf, ( new_len + 1 /* NUL */ ) );
+       if ( tmp ) {
+               buf = tmp;
+               *(string->buf) = buf;
+       } else if ( new_len > old_len ) {
+               return -ENOMEM;
+       }
+
        /* Fill in edit history */
        string->mod_start = string->cursor;
        string->mod_end = ( ( new_len > old_len ) ? new_len : old_len );
 
        /* Move data following the cursor */
-       memmove ( ( string->buf + string->cursor + insert_len ),
-                 ( string->buf + string->cursor + delete_len ),
-                 ( max_delete_len + 1 - delete_len ) );
+       memmove ( ( buf + string->cursor + insert_len ),
+                 ( buf + string->cursor + delete_len ),
+                 ( max_delete_len - delete_len ) );
 
        /* Copy inserted text to cursor position */
-       memcpy ( ( string->buf + string->cursor ), insert_text, insert_len );
+       memcpy ( ( buf + string->cursor ), insert_text, insert_len );
        string->cursor += insert_len;
+
+       /* Terminate string (if not NULL) */
+       if ( buf ) {
+               buf[new_len] = '\0';
+       } else {
+               assert ( old_len == 0 );
+               assert ( insert_len == 0 );
+               assert ( new_len == 0 );
+       }
+
+       return 0;
 }
 
 /**
@@ -89,11 +112,13 @@ static void insert_delete ( struct edit_string *string, size_t delete_len,
  *
  * @v string           Editable string
  * @v character                Character to insert
+ * @ret rc             Return status code
  */
-static void insert_character ( struct edit_string *string,
+static int insert_character ( struct edit_string *string,
                              unsigned int character ) {
        char insert_text[2] = { character, '\0' };
-       insert_delete ( string, 0, insert_text );
+
+       return insert_delete ( string, 0, insert_text );
 }
 
 /**
@@ -102,7 +127,10 @@ static void insert_character ( struct edit_string *string,
  * @v string           Editable string
  */
 static void delete_character ( struct edit_string *string ) {
-       insert_delete ( string, 1, NULL );
+       int rc;
+
+       rc = insert_delete ( string, 1, NULL );
+       assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) );
 }
 
 /**
@@ -111,6 +139,7 @@ static void delete_character ( struct edit_string *string ) {
  * @v string           Editable string
  */
 static void backspace ( struct edit_string *string ) {
+
        if ( string->cursor > 0 ) {
                string->cursor--;
                delete_character ( string );
@@ -123,14 +152,16 @@ static void backspace ( struct edit_string *string ) {
  * @v string           Editable string
  */
 static void previous_word ( struct edit_string *string ) {
-       while ( string->cursor &&
-               isspace ( string->buf[ string->cursor - 1 ] ) ) {
-               string->cursor--;
+       const char *buf = *(string->buf);
+       size_t cursor = string->cursor;
+
+       while ( cursor && isspace ( buf[ cursor - 1 ] ) ) {
+               cursor--;
        }
-       while ( string->cursor &&
-               ( ! isspace ( string->buf[ string->cursor - 1 ] ) ) ) {
-               string->cursor--;
+       while ( cursor && ( ! isspace ( buf[ cursor - 1 ] ) ) ) {
+               cursor--;
        }
+       string->cursor = cursor;
 }
 
 /**
@@ -140,8 +171,11 @@ static void previous_word ( struct edit_string *string ) {
  */
 static void kill_word ( struct edit_string *string ) {
        size_t old_cursor = string->cursor;
+       int rc;
+
        previous_word ( string );
-       insert_delete ( string, ( old_cursor - string->cursor ), NULL );
+       rc = insert_delete ( string, ( old_cursor - string->cursor ), NULL );
+       assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) );
 }
 
 /**
@@ -151,8 +185,11 @@ static void kill_word ( struct edit_string *string ) {
  */
 static void kill_sol ( struct edit_string *string ) {
        size_t old_cursor = string->cursor;
+       int rc;
+
        string->cursor = 0;
-       insert_delete ( string, old_cursor, NULL );
+       rc = insert_delete ( string, old_cursor, NULL );
+       assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) );
 }
 
 /**
@@ -161,18 +198,36 @@ static void kill_sol ( struct edit_string *string ) {
  * @v string           Editable string
  */
 static void kill_eol ( struct edit_string *string ) {
-       insert_delete ( string, ~( ( size_t ) 0 ), NULL );
+       int rc;
+
+       rc = insert_delete ( string, ~( ( size_t ) 0 ), NULL );
+       assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) );
 }
 
 /**
  * Replace editable string
  *
  * @v string           Editable string
- * @v replacement      Replacement string
+ * @v replacement      Replacement string, or NULL to empty the string
+ * @ret rc             Return status code
+ *
+ * Replace the entire content of the editable string and update the
+ * edit history to allow the caller to bring the display into sync
+ * with the string content.
+ *
+ * This function does not itself update the display in any way.
+ *
+ * Upon success, the string buffer is guaranteed to be non-NULL (even
+ * if the replacement string is NULL or empty).
+ *
+ * Errors may safely be ignored if it is deemed that subsequently
+ * failing to update the display will provide sufficient feedback to
+ * the user.
  */
-void replace_string ( struct edit_string *string, const char *replacement ) {
+int replace_string ( struct edit_string *string, const char *replacement ) {
+
        string->cursor = 0;
-       insert_delete ( string, ~( ( size_t ) 0 ), replacement );
+       return insert_delete ( string, ~( ( size_t ) 0 ), replacement );
 }
 
 /**
@@ -180,21 +235,26 @@ void replace_string ( struct edit_string *string, const char *replacement ) {
  *
  * @v string           Editable string
  * @v key              Key pressed by user
- * @ret key            Key returned to application, or zero
+ * @ret key            Key returned to application, zero, or negative error
  *
  * Handles keypresses and updates the content of the editable string.
  * Basic line editing facilities (delete/insert/cursor) are supported.
  * If edit_string() understands and uses the keypress it will return
  * zero, otherwise it will return the original key.
  *
- * This function does not update the display in any way.
- *
  * The string's edit history will be updated to allow the caller to
  * efficiently bring the display into sync with the string content.
+ *
+ * This function does not itself update the display in any way.
+ *
+ * Errors may safely be ignored if it is deemed that subsequently
+ * failing to update the display will provide sufficient feedback to
+ * the user.
  */
 int edit_string ( struct edit_string *string, int key ) {
+       const char *buf = *(string->buf);
+       size_t len = ( buf ? strlen ( buf ) : 0 );
        int retval = 0;
-       size_t len = strlen ( string->buf );
 
        /* Prepare edit history */
        string->last_cursor = string->cursor;
@@ -204,7 +264,7 @@ int edit_string ( struct edit_string *string, int key ) {
        /* Interpret key */
        if ( ( key >= 0x20 ) && ( key <= 0x7e ) ) {
                /* Printable character; insert at current position */
-               insert_character ( string, key );
+               retval = insert_character ( string, key );
        } else switch ( key ) {
        case KEY_BACKSPACE:
                /* Backspace */
index 210de448143b4a10ce9df3f0275afc5585b211e1..fddd2742e94307c95e2827f16997d2b755d7f58b 100644 (file)
@@ -39,20 +39,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  * Initialise text box widget
  *
  * @v box              Editable text box widget
- * @v buf              Text buffer
- * @v len              Size of text buffer
+ * @v buf              Dynamically allocated string buffer
  * @v win              Containing window
  * @v row              Row
  * @v col              Starting column
  * @v width            Width
  * @v flags            Flags
  */
-void init_editbox ( struct edit_box *box, char *buf, size_t len,
+void init_editbox ( struct edit_box *box, char **buf,
                    WINDOW *win, unsigned int row, unsigned int col,
                    unsigned int width, unsigned int flags ) {
        memset ( box, 0, sizeof ( *box ) );
-       init_editstring ( &box->string, buf, len );
-       box->string.cursor = strlen ( buf );
+       init_editstring ( &box->string, buf );
+       box->string.cursor = ( *buf ? strlen ( *buf ) : 0 );
        box->win = ( win ? win : stdscr );
        box->row = row;
        box->col = col;
@@ -67,6 +66,7 @@ void init_editbox ( struct edit_box *box, char *buf, size_t len,
  *
  */
 void draw_editbox ( struct edit_box *box ) {
+       const char *content = *(box->string.buf);
        size_t width = box->width;
        char buf[ width + 1 ];
        signed int cursor_offset, underflow, overflow, first;
@@ -90,13 +90,13 @@ void draw_editbox ( struct edit_box *box ) {
        /* Construct underscore-padded string portion */
        memset ( buf, '_', width );
        buf[width] = '\0';
-       len = ( strlen ( box->string.buf ) - first );
+       len = ( content ? ( strlen ( content ) - first ) : 0 );
        if ( len > width )
                len = width;
        if ( box->flags & EDITBOX_STARS ) {
                memset ( buf, '*', len );
        } else {
-               memcpy ( buf, ( box->string.buf + first ), len );
+               memcpy ( buf, ( content + first ), len );
        }
 
        /* Print box content and move cursor */
index ecc72d43f26c32f4770fafbaa689bf94c42b2750..5b46413e972494f46cb39dc00b302d1eebc6360e 100644 (file)
@@ -38,8 +38,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
-#define READLINE_MAX 1024
-
 /**
  * Synchronise console with edited string
  *
@@ -49,7 +47,8 @@ static void sync_console ( struct edit_string *string ) {
        unsigned int mod_start = string->mod_start;
        unsigned int mod_end = string->mod_end;
        unsigned int cursor = string->last_cursor;
-       size_t len = strlen ( string->buf );
+       const char *buf = *(string->buf);
+       size_t len = strlen ( buf );
 
        /* Expand region back to old cursor position if applicable */
        if ( mod_start > string->last_cursor )
@@ -67,7 +66,7 @@ static void sync_console ( struct edit_string *string ) {
 
        /* Print modified region */
        while ( cursor < mod_end ) {
-               putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] );
+               putchar ( ( cursor >= len ) ? ' ' : buf[cursor] );
                cursor++;
        }
 
@@ -259,15 +258,11 @@ int readline_history ( const char *prompt, const char *prefill,
                       struct readline_history *history, unsigned long timeout,
                       char **line ) {
        struct edit_string string;
-       char *buf;
        int key;
        int move_by;
        const char *new_string;
        int rc;
 
-       /* Avoid returning uninitialised data on error */
-       *line = NULL;
-
        /* Display prompt, if applicable */
        if ( prompt )
                printf ( "%s", prompt );
@@ -275,20 +270,15 @@ int readline_history ( const char *prompt, const char *prefill,
        /* Ensure cursor is visible */
        printf ( "\033[?25h" );
 
-       /* Allocate buffer and initialise editable string */
-       buf = zalloc ( READLINE_MAX );
-       if ( ! buf ) {
-               rc = -ENOMEM;
-               goto done;
-       }
+       /* Initialise editable string */
+       *line = NULL;
        memset ( &string, 0, sizeof ( string ) );
-       init_editstring ( &string, buf, READLINE_MAX );
+       init_editstring ( &string, line );
 
-       /* Prefill string, if applicable */
-       if ( prefill ) {
-               replace_string ( &string, prefill );
-               sync_console ( &string );
-       }
+       /* Prefill string */
+       if ( ( rc = replace_string ( &string, prefill ) ) != 0 )
+               goto error;
+       sync_console ( &string );
 
        while ( 1 ) {
 
@@ -296,7 +286,7 @@ int readline_history ( const char *prompt, const char *prefill,
                key = getkey ( timeout );
                if ( key < 0 ) {
                        rc = -ETIMEDOUT;
-                       goto done;
+                       goto error;
                }
                timeout = 0;
 
@@ -307,17 +297,11 @@ int readline_history ( const char *prompt, const char *prefill,
                switch ( key ) {
                case CR:
                case LF:
-                       /* Shrink string (ignoring failures) */
-                       *line = realloc ( buf,
-                                         ( strlen ( buf ) + 1 /* NUL */ ) );
-                       if ( ! *line )
-                               *line = buf;
-                       buf = NULL;
                        rc = 0;
                        goto done;
                case CTRL_C:
                        rc = -ECANCELED;
-                       goto done;
+                       goto error;
                case KEY_UP:
                        move_by = 1;
                        break;
@@ -325,13 +309,13 @@ int readline_history ( const char *prompt, const char *prefill,
                        move_by = -1;
                        break;
                default:
-                       /* Do nothing */
+                       /* Do nothing for unrecognised keys or edit errors */
                        break;
                }
 
                /* Handle history movement, if applicable */
                if ( move_by && history ) {
-                       new_string = history_move ( history, move_by, buf );
+                       new_string = history_move ( history, move_by, *line );
                        if ( new_string ) {
                                replace_string ( &string, new_string );
                                sync_console ( &string );
@@ -339,9 +323,11 @@ int readline_history ( const char *prompt, const char *prefill,
                }
        }
 
+ error:
+       free ( *line );
+       *line = NULL;
  done:
        putchar ( '\n' );
-       free ( buf );
        if ( history ) {
                if ( *line && (*line)[0] )
                        history_append ( history, *line );
index 3c55325d5a9024f92119e3a3623ad203846f0535..bd24991cd4ba3f836f06878ec7eb5f1f9105c5d3 100644 (file)
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  */
 
 #include <string.h>
+#include <stdlib.h>
 #include <errno.h>
 #include <curses.h>
 #include <ipxe/console.h>
@@ -49,8 +50,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define EDITBOX_WIDTH          20U
 
 int login_ui ( void ) {
-       char username[64];
-       char password[64];
+       char *username;
+       char *password;
        struct edit_box username_box;
        struct edit_box password_box;
        struct edit_box *current_box = &username_box;
@@ -58,19 +59,16 @@ int login_ui ( void ) {
        int rc = -EINPROGRESS;
 
        /* Fetch current setting values */
-       fetch_string_setting ( NULL, &username_setting, username,
-                              sizeof ( username ) );
-       fetch_string_setting ( NULL, &password_setting, password,
-                              sizeof ( password ) );
+       fetchf_setting_copy ( NULL, &username_setting, NULL, NULL, &username );
+       fetchf_setting_copy ( NULL, &password_setting, NULL, NULL, &password );
 
        /* Initialise UI */
        initscr();
        start_color();
-       init_editbox ( &username_box, username, sizeof ( username ), NULL,
-                      USERNAME_ROW, EDITBOX_COL, EDITBOX_WIDTH, 0 );
-       init_editbox ( &password_box, password, sizeof ( password ), NULL,
-                      PASSWORD_ROW, EDITBOX_COL, EDITBOX_WIDTH,
-                      EDITBOX_STARS );
+       init_editbox ( &username_box, &username, NULL, USERNAME_ROW,
+                      EDITBOX_COL, EDITBOX_WIDTH, 0 );
+       init_editbox ( &password_box, &password, NULL, PASSWORD_ROW,
+                      EDITBOX_COL, EDITBOX_WIDTH, EDITBOX_STARS );
 
        /* Draw initial UI */
        color_set ( CPAIR_NORMAL, NULL );
@@ -122,16 +120,15 @@ int login_ui ( void ) {
        erase();
        endwin();
 
-       if ( rc != 0 )
-               return rc;
+       /* Store settings on successful completion */
+       if ( rc == 0 )
+               rc = storef_setting ( NULL, &username_setting, username );
+       if ( rc == 0 )
+               rc = storef_setting ( NULL, &password_setting, password );
 
-       /* Store settings */
-       if ( ( rc = store_setting ( NULL, &username_setting, username,
-                                   strlen ( username ) ) ) != 0 )
-               return rc;
-       if ( ( rc = store_setting ( NULL, &password_setting, password,
-                                   strlen ( password ) ) ) != 0 )
-               return rc;
+       /* Free setting values */
+       free ( username );
+       free ( password );
 
-       return 0;
+       return rc;
 }
index be421cc0ac36dea576bc04120ca3083653285184..95cbd7758b494163975e3af160f7bb40ed39aa21 100644 (file)
@@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <curses.h>
@@ -58,12 +59,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
        char start[0];                                                  \
        char pad1[1];                                                   \
        union {                                                         \
-               char settings[ cols - 1 - 1 - 1 - 1 ];                  \
+               struct {                                                \
+                       char name[ cols - 1 - 1 - 1 - 1 - 1 ];          \
+                       char pad2[1];                                   \
+               } __attribute__ (( packed )) settings;                  \
                struct {                                                \
                        char name[15];                                  \
                        char pad2[1];                                   \
                        char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ];    \
-               } setting;                                              \
+               } __attribute__ (( packed )) setting;                   \
        } u;                                                            \
        char pad3[1];                                                   \
        char nul;                                                       \
@@ -92,8 +96,8 @@ struct settings_ui_row {
        struct edit_box editbox;
        /** Editing in progress flag */
        int editing;
-       /** Buffer for setting's value */
-       char value[256]; /* enough size for a DHCP string */
+       /** Dynamically allocated buffer for setting's value */
+       char *buf;
 };
 
 /** A settings user interface */
@@ -121,24 +125,22 @@ static unsigned int select_setting_row ( struct settings_ui *ui,
        struct setting *previous = NULL;
        unsigned int count = 0;
 
+       /* Free any previous setting value */
+       free ( ui->row.buf );
+       ui->row.buf = NULL;
+
        /* Initialise structure */
        memset ( &ui->row, 0, sizeof ( ui->row ) );
        ui->row.row = ( SETTINGS_LIST_ROW + index - ui->scroll.first );
 
        /* Include parent settings block, if applicable */
-       if ( ui->settings->parent && ( count++ == index ) ) {
+       if ( ui->settings->parent && ( count++ == index ) )
                ui->row.settings = ui->settings->parent;
-               snprintf ( ui->row.value, sizeof ( ui->row.value ),
-                          "../" );
-       }
 
        /* Include any child settings blocks, if applicable */
        list_for_each_entry ( settings, &ui->settings->children, siblings ) {
-               if ( count++ == index ) {
+               if ( count++ == index )
                        ui->row.settings = settings;
-                       snprintf ( ui->row.value, sizeof ( ui->row.value ),
-                                  "%s/", settings->name );
-               }
        }
 
        /* Include any applicable settings */
@@ -155,15 +157,14 @@ static unsigned int select_setting_row ( struct settings_ui *ui,
 
                /* Read current setting value and origin */
                if ( count++ == index ) {
-                       fetchf_setting ( ui->settings, setting, &ui->row.origin,
-                                        &ui->row.setting, ui->row.value,
-                                        sizeof ( ui->row.value ) );
+                       fetchf_setting_copy ( ui->settings, setting,
+                                             &ui->row.origin,
+                                             &ui->row.setting, &ui->row.buf );
                }
        }
 
        /* Initialise edit box */
-       init_editbox ( &ui->row.editbox, ui->row.value,
-                      sizeof ( ui->row.value ), NULL, ui->row.row,
+       init_editbox ( &ui->row.editbox, &ui->row.buf, NULL, ui->row.row,
                       ( SETTINGS_LIST_COL +
                         offsetof ( typeof ( *text ), u.setting.value ) ),
                       sizeof ( text->u.setting.value ), 0 );
@@ -197,7 +198,7 @@ static size_t string_copy ( char *dest, const char *src, size_t len ) {
 static void draw_setting_row ( struct settings_ui *ui ) {
        SETTING_ROW_TEXT ( COLS ) text;
        unsigned int curs_offset;
-       char *value;
+       const char *value;
 
        /* Fill row with spaces */
        memset ( &text, ' ', sizeof ( text ) );
@@ -207,10 +208,12 @@ static void draw_setting_row ( struct settings_ui *ui ) {
        if ( ui->row.settings ) {
 
                /* Construct space-padded name */
-               curs_offset = ( offsetof ( typeof ( text ), u.settings ) +
-                               string_copy ( text.u.settings,
-                                             ui->row.value,
-                                             sizeof ( text.u.settings ) ) );
+               value = ( ( ui->row.settings == ui->settings->parent ) ?
+                         ".." : ui->row.settings->name );
+               curs_offset = string_copy ( text.u.settings.name, value,
+                                           sizeof ( text.u.settings.name ) );
+               text.u.settings.name[curs_offset] = '/';
+               curs_offset += offsetof ( typeof ( text ), u.settings );
 
        } else {
 
@@ -221,12 +224,12 @@ static void draw_setting_row ( struct settings_ui *ui ) {
                              sizeof ( text.u.setting.name ) );
 
                /* Construct space-padded value */
-               value = ui->row.value;
-               if ( ! *value )
+               value = ui->row.buf;
+               if ( ! ( value && value[0] ) )
                        value = "<not specified>";
-               curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) +
-                               string_copy ( text.u.setting.value, value,
-                                             sizeof ( text.u.setting.value )));
+               curs_offset = string_copy ( text.u.setting.value, value,
+                                           sizeof ( text.u.setting.value ) );
+               curs_offset += offsetof ( typeof ( text ), u.setting.value );
        }
 
        /* Print row */
@@ -257,7 +260,7 @@ static int edit_setting ( struct settings_ui *ui, int key ) {
  */
 static int save_setting ( struct settings_ui *ui ) {
        assert ( ui->row.setting.name != NULL );
-       return storef_setting ( ui->settings, &ui->row.setting, ui->row.value );
+       return storef_setting ( ui->settings, &ui->row.setting, ui->row.buf );
 }
 
 /**
@@ -527,6 +530,7 @@ static int main_loop ( struct settings *settings ) {
                        redraw = 1;
                        break;
                case CTRL_X:
+                       select_setting_row ( &ui, -1U );
                        return 0;
                case CR:
                case LF:
index 2c70e0b6b0fbeb258b740ea8c4197e06884ecbdd..c14bbdc5b4413036b32a1721b7b32649a10e420f 100644 (file)
@@ -36,7 +36,7 @@ enum edit_box_flags {
        EDITBOX_STARS = 0x0001,
 };
 
-extern void init_editbox ( struct edit_box *box, char *buf, size_t len,
+extern void init_editbox ( struct edit_box *box, char **buf,
                           WINDOW *win, unsigned int row, unsigned int col,
                           unsigned int width, unsigned int flags )
                           __attribute__ (( nonnull (1, 2) ));
index a00a8adaaa74af6b21be4e6a9ba3bf45650e0fcf..7ad8fb304c481533ed32d98a70a6633e8bfda2b9 100644 (file)
@@ -11,10 +11,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 /** An editable string */
 struct edit_string {
-       /** Buffer for string */
-       char *buf;
-       /** Size of buffer (including terminating NUL) */
-       size_t len;
+       /** Dynamically allocated string buffer */
+       char **buf;
        /** Cursor position */
        unsigned int cursor;
 
@@ -32,17 +30,28 @@ struct edit_string {
  * Initialise editable string
  *
  * @v string           Editable string
- * @v buf              Buffer for string
- * @v len              Length of buffer
+ * @v buf              Dynamically allocated string buffer
+ *
+ * The @c buf parameter must be the address of a caller-provided
+ * pointer to a NUL-terminated string allocated using malloc() (or
+ * equivalent, such as strdup()).  Any edits made to the string will
+ * realloc() the string buffer as needed.
+ *
+ * The caller may choose leave the initial string buffer pointer as @c
+ * NULL, in which case it will be allocated upon the first attempt to
+ * insert a character into the buffer.  If the caller does this, then
+ * it must be prepared to find the pointer still @c NULL after
+ * editing, since the user may never attempt to insert any characters.
  */
-static inline void init_editstring ( struct edit_string *string, char *buf,
-                                    size_t len ) {
+static inline __nonnull void init_editstring ( struct edit_string *string,
+                                              char **buf ) {
+
        string->buf = buf;
-       string->len = len;
 }
 
-extern void replace_string ( struct edit_string *string,
-                            const char *replacement ) __nonnull;
-extern int edit_string ( struct edit_string *string, int key ) __nonnull;
+extern __attribute__ (( nonnull ( 1 ) )) int
+replace_string ( struct edit_string *string, const char *replacement );
+
+extern __nonnull int edit_string ( struct edit_string *string, int key );
 
 #endif /* _IPXE_EDITSTRING_H */
index 0ab37230a31032930ebf6772246fab81b2729c51..96981dd06971d23572d4a31c2c671d10890c55ca 100644 (file)
@@ -416,6 +416,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_efi_settings         ( ERRFILE_OTHER | 0x005e0000 )
 #define ERRFILE_x25519               ( ERRFILE_OTHER | 0x005f0000 )
 #define ERRFILE_des                  ( ERRFILE_OTHER | 0x00600000 )
+#define ERRFILE_editstring           ( ERRFILE_OTHER | 0x00610000 )
 
 /** @} */