aboutsummaryrefslogtreecommitdiff
path: root/src/hci/editstring.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hci/editstring.c')
-rw-r--r--src/hci/editstring.c144
1 files changed, 102 insertions, 42 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 */