From ba0875373d2250e3a6d63d5269ff1a4a89a280ed Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Thu, 13 Apr 2023 12:59:31 +0300 Subject: cmdline.c: fixed rubbing out words (^W) and possible chrashes * This would sometimes rub out more than expected due to reading undefined memory. Actually even crashes were not impossible. * This is because SCI_GETWORDCHARS does not null-terminate the buffer it writes but this was assumed. In effect, we could easily read beyond the allocated memory in wchars if there doesn't happen to be a null-char following the buffer. * Consequently, null-chars in word chars were also not supported, although this would hardly trouble anybody. * Instead, we now store the word chars in a teco_string_t which supports non-null-terminated strings natively. Still we null-terminate the string to keep teco_string_t's promises about degrading to null-terminated char *. This is currently not necessary. * teco_is_wordchar() has been replaced by teco_string_contains(). --- TODO | 4 ---- src/cmdline.c | 27 ++++++++++----------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/TODO b/TODO index d55ed1e..b1a464a 100644 --- a/TODO +++ b/TODO @@ -14,10 +14,6 @@ Known Bugs: enable it after every keypress. * The "lexer.test..." macros do not work with the unnamed buffer, so there should be a special test in .teco_ini. - * Rubbing out via ^W will rub out more than expected. - This has also been observed after words e.g. rubbing out after "foo " - will sometimes rub out an entire line. - This is not reproducible. * After commands like ECcat /dev/zero$ result in OOM, we do not correctly recover, even though malloc_trim() is called. This could be because of Scintilla's undo token. diff --git a/src/cmdline.c b/src/cmdline.c index 1cdaa40..58d48b4 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -421,16 +421,6 @@ teco_state_caseinsensitive_process_edit_cmd(teco_machine_t *ctx, teco_machine_t return teco_state_process_edit_cmd(ctx, parent_ctx, key, error); } -/* - * NOTE: The wordchars are null-terminated, so the null byte - * is always considered to be a non-wordchar. - */ -static inline gboolean -teco_is_wordchar(const gchar *wordchars, gchar c) -{ - return c != '\0' && strchr(wordchars, c); -} - gboolean teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *ctx, teco_machine_t *parent_ctx, gchar key, GError **error) @@ -441,21 +431,24 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * case TECO_CTL_KEY('W'): { /* rubout/reinsert word */ teco_interface_popup_clear(); - g_autofree gchar *wchars = g_malloc(teco_interface_ssm(SCI_GETWORDCHARS, 0, 0)); - teco_interface_ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars); + g_auto(teco_string_t) wchars; + wchars.len = teco_interface_ssm(SCI_GETWORDCHARS, 0, 0); + wchars.data = g_malloc(wchars.len + 1); + teco_interface_ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars.data); + wchars.data[wchars.len] = '\0'; if (teco_cmdline.modifier_enabled) { /* reinsert word chars */ while (ctx->parent.current == current && teco_cmdline.effective_len < teco_cmdline.str.len && - teco_is_wordchar(wchars, teco_cmdline.str.data[teco_cmdline.effective_len])) + teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len])) if (!teco_cmdline_rubin(error)) return FALSE; /* reinsert non-word chars */ while (ctx->parent.current == current && teco_cmdline.effective_len < teco_cmdline.str.len && - !teco_is_wordchar(wchars, teco_cmdline.str.data[teco_cmdline.effective_len])) + !teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len])) if (!teco_cmdline_rubin(error)) return FALSE; @@ -475,7 +468,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * * rubout the entire command. */ if (ctx->result && ctx->result->len > 0) { - gboolean is_wordchar = teco_is_wordchar(wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1]); + gboolean is_wordchar = teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1]); teco_cmdline_rubout(); if (ctx->parent.current != current) { /* rub out string building command */ @@ -490,13 +483,13 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * */ if (!is_wordchar) { while (ctx->result->len > 0 && - !teco_is_wordchar(wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1])) + !teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1])) teco_cmdline_rubout(); } /* rubout word chars */ while (ctx->result->len > 0 && - teco_is_wordchar(wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1])) + teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1])) teco_cmdline_rubout(); return TRUE; -- cgit v1.2.3