aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2024-04-17 15:45:36 +0100
committerMichael Brown <mcb30@ipxe.org>2024-04-17 15:55:28 +0100
commitb01781a2b8fb29669d9a610bfb323fb8a6960612 (patch)
tree4c621dd4ab80ec6e6c7a2e408efef1684ab93875
parentcb95b5b378b1a61d10770965d82dba535b6be242 (diff)
downloadipxe-b01781a2b8fb29669d9a610bfb323fb8a6960612.zip
ipxe-b01781a2b8fb29669d9a610bfb323fb8a6960612.tar.gz
ipxe-b01781a2b8fb29669d9a610bfb323fb8a6960612.tar.bz2
[hci] Fix semantics of replace_string() to match code comments
The comments for replace_string() state that a successful return status guarantees that the dynamically allocated string pointer is no longer NULL (even if it was initially NULL and the replacement string is NULL or empty). This is relied upon by readline() to guarantee that it will always return a non-NULL string if successful. The code behaviour does not currently match this comment: an empty replacement string may result in a successful return status even if the (single-byte) allocation fails. Fix up the code behaviour to match the comments, and to additionally ensure that the edit history is filled in even in the event of an allocation failure. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/hci/editstring.c35
1 files changed, 18 insertions, 17 deletions
diff --git a/src/hci/editstring.c b/src/hci/editstring.c
index b83916c..be9ca06 100644
--- a/src/hci/editstring.c
+++ b/src/hci/editstring.c
@@ -59,10 +59,14 @@ static __nonnull void kill_eol ( struct edit_string *string );
*/
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;
+ size_t old_len, max_delete_len, move_len, insert_len, new_len;
char *buf;
char *tmp;
+ /* Prepare edit history */
+ string->mod_start = string->cursor;
+ string->mod_end = string->cursor;
+
/* Calculate lengths */
buf = *(string->buf);
old_len = ( buf ? strlen ( buf ) : 0 );
@@ -70,39 +74,36 @@ static int insert_delete ( struct edit_string *string, size_t delete_len,
max_delete_len = ( old_len - string->cursor );
if ( delete_len > max_delete_len )
delete_len = max_delete_len;
+ move_len = ( max_delete_len - delete_len );
insert_len = ( insert_text ? strlen ( insert_text ) : 0 );
new_len = ( old_len - delete_len + insert_len );
+ /* Delete existing text */
+ memmove ( ( buf + string->cursor ),
+ ( buf + string->cursor + delete_len ), move_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 ) {
+ } else if ( ( new_len > old_len ) || ( ! buf ) ) {
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 */
+ /* Create space for inserted text */
memmove ( ( buf + string->cursor + insert_len ),
- ( buf + string->cursor + delete_len ),
- ( max_delete_len - delete_len ) );
+ ( buf + string->cursor ), move_len );
/* Copy inserted text to cursor position */
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 );
- }
+ /* Terminate string */
+ buf[new_len] = '\0';
+
+ /* Update edit history */
+ string->mod_end = ( ( new_len > old_len ) ? new_len : old_len );
return 0;
}