diff options
author | Michael Brown <mcb30@ipxe.org> | 2024-04-15 15:59:49 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2024-04-15 15:59:49 +0100 |
commit | 40b51124400df9cb0c57512ff93ac21827d5bac0 (patch) | |
tree | fefb7a187eda31347f14f7317b29ed6427a271e2 /src/hci/editstring.c | |
parent | 27ecc36c0bef804d12dbf8c29684c8e8159c8e47 (diff) | |
download | ipxe-40b51124400df9cb0c57512ff93ac21827d5bac0.tar.gz |
[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>
Diffstat (limited to 'src/hci/editstring.c')
-rw-r--r-- | src/hci/editstring.c | 144 |
1 files changed, 102 insertions, 42 deletions
diff --git a/src/hci/editstring.c b/src/hci/editstring.c index 8cbce0767..b83916c6e 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 */ |