From 40b51124400df9cb0c57512ff93ac21827d5bac0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Apr 2024 15:59:49 +0100 Subject: [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 --- src/hci/editstring.c | 144 ++++++++++++++++++++++++++----------- src/hci/mucurses/widgets/editbox.c | 14 ++-- src/hci/readline.c | 48 +++++-------- src/hci/tui/login_ui.c | 39 +++++----- src/hci/tui/settings_ui.c | 60 ++++++++-------- src/include/ipxe/editbox.h | 2 +- src/include/ipxe/editstring.h | 33 +++++---- src/include/ipxe/errfile.h | 1 + 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 +#include #include #include +#include #include #include @@ -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 +#include #include #include #include @@ -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 #include +#include #include #include #include @@ -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 = ""; - 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 ) /** @} */ -- cgit v1.1