aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2024-04-15 15:59:49 +0100
committerMichael Brown <mcb30@ipxe.org>2024-04-15 15:59:49 +0100
commit40b51124400df9cb0c57512ff93ac21827d5bac0 (patch)
treefefb7a187eda31347f14f7317b29ed6427a271e2
parent27ecc36c0bef804d12dbf8c29684c8e8159c8e47 (diff)
downloadipxe-40b51124400df9cb0c57512ff93ac21827d5bac0.zip
ipxe-40b51124400df9cb0c57512ff93ac21827d5bac0.tar.gz
ipxe-40b51124400df9cb0c57512ff93ac21827d5bac0.tar.bz2
[hci] Use dynamically allocated buffers for editable strings
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>
-rw-r--r--src/hci/editstring.c144
-rw-r--r--src/hci/mucurses/widgets/editbox.c14
-rw-r--r--src/hci/readline.c48
-rw-r--r--src/hci/tui/login_ui.c39
-rw-r--r--src/hci/tui/settings_ui.c60
-rw-r--r--src/include/ipxe/editbox.h2
-rw-r--r--src/include/ipxe/editstring.h33
-rw-r--r--src/include/ipxe/errfile.h1
8 files changed, 199 insertions, 142 deletions
diff --git a/src/hci/editstring.c b/src/hci/editstring.c
index 8cbce07..b83916c 100644
--- a/src/hci/editstring.c
+++ b/src/hci/editstring.c
@@ -24,8 +24,10 @@
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 */
diff --git a/src/hci/mucurses/widgets/editbox.c b/src/hci/mucurses/widgets/editbox.c
index 210de44..fddd274 100644
--- a/src/hci/mucurses/widgets/editbox.c
+++ b/src/hci/mucurses/widgets/editbox.c
@@ -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 */
diff --git a/src/hci/readline.c b/src/hci/readline.c
index ecc72d4..5b46413 100644
--- a/src/hci/readline.c
+++ b/src/hci/readline.c
@@ -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 );
diff --git a/src/hci/tui/login_ui.c b/src/hci/tui/login_ui.c
index 3c55325..bd24991 100644
--- a/src/hci/tui/login_ui.c
+++ b/src/hci/tui/login_ui.c
@@ -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;
}
diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c
index be421cc..95cbd77 100644
--- a/src/hci/tui/settings_ui.c
+++ b/src/hci/tui/settings_ui.c
@@ -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:
diff --git a/src/include/ipxe/editbox.h b/src/include/ipxe/editbox.h
index 2c70e0b..c14bbdc 100644
--- a/src/include/ipxe/editbox.h
+++ b/src/include/ipxe/editbox.h
@@ -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) ));
diff --git a/src/include/ipxe/editstring.h b/src/include/ipxe/editstring.h
index a00a8ad..7ad8fb3 100644
--- a/src/include/ipxe/editstring.h
+++ b/src/include/ipxe/editstring.h
@@ -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 */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 0ab3723..96981dd 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -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 )
/** @} */