mirror of https://github.com/ipxe/ipxe.git
[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
parent
27ecc36c0b
commit
40b5112440
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) ));
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 )
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
Loading…
Reference in New Issue