[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>
pull/1194/head
Michael Brown 2024-04-15 15:59:49 +01:00
parent 27ecc36c0b
commit 40b5112440
8 changed files with 199 additions and 142 deletions

View File

@ -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 */

View 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 */

View 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 );

View 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;
}

View 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:

View 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) ));

View 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 */

View 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 )
/** @} */