diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-09-18 12:32:16 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2024-09-18 12:32:16 +0200 |
commit | b7b98405089e69dfae0fc11e2a423860f50756e9 (patch) | |
tree | b789182370dc4493d040dafb0bc932f69fbbd3c4 | |
parent | dc417a890ad48e28e573770f2ae980af002e93cb (diff) | |
download | sciteco-b7b98405089e69dfae0fc11e2a423860f50756e9.tar.gz |
check that local register is not edited at the end of macro calls
* This was unsafe and could easily result in crashes, since teco_qreg_current
would afterwards point to an already freed Q-Register.
* Since automatically editing another register or buffer is not easy to do right,
we throw an error instead.
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | src/error.h | 9 | ||||
-rw-r--r-- | src/qreg-commands.c | 7 | ||||
-rw-r--r-- | src/qreg.c | 6 | ||||
-rw-r--r-- | tests/testsuite.at | 5 |
5 files changed, 27 insertions, 3 deletions
@@ -4,9 +4,6 @@ Tasks: "edit" hook. Known Bugs: - * A local Q-Register can be left as the edited document - even after invoking a macro via <M>. - This is probably not safe and we should throw an error instead. * E%$...$ broken. Similarily EQ$file$ won't do what you would expect it to. * <23(1;)> leaves values on the stack and the internal hidden diff --git a/src/error.h b/src/error.h index b12ec80..03af66c 100644 --- a/src/error.h +++ b/src/error.h @@ -47,6 +47,7 @@ typedef enum { TECO_ERROR_INVALIDQREG, TECO_ERROR_QREGOPUNSUPPORTED, TECO_ERROR_QREGCONTAINSNULL, + TECO_ERROR_EDITINGLOCALQREG, TECO_ERROR_MEMLIMIT, /** Interrupt current operation */ @@ -127,6 +128,14 @@ teco_error_qregcontainsnull_set(GError **error, const gchar *name, gsize len, gb } static inline void +teco_error_editinglocalqreg_set(GError **error, const gchar *name, gsize len) +{ + g_autofree gchar *name_printable = teco_string_echo(name, len); + g_set_error(error, TECO_ERROR, TECO_ERROR_EDITINGLOCALQREG, + "Editing local Q-Register \"%s\" at end of macro call", name_printable); +} + +static inline void teco_error_interrupted_set(GError **error) { g_set_error_literal(error, TECO_ERROR, TECO_ERROR_INTERRUPTED, "Interrupted"); diff --git a/src/qreg-commands.c b/src/qreg-commands.c index 8d28e7d..a96eb5f 100644 --- a/src/qreg-commands.c +++ b/src/qreg-commands.c @@ -652,8 +652,15 @@ teco_state_macro_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, } else { g_auto(teco_qreg_table_t) table; teco_qreg_table_init(&table, FALSE); + if (!teco_qreg_execute(qreg, &table, error)) return NULL; + if (teco_qreg_current && !teco_qreg_current->must_undo) { + /* currently editing local Q-Register */ + teco_error_editinglocalqreg_set(error, teco_qreg_current->head.name.data, + teco_qreg_current->head.name.len); + return NULL; + } } return &teco_state_start; @@ -1160,6 +1160,12 @@ teco_ed_hook(teco_ed_hook_t type, GError **error) if (!teco_qreg_execute(qreg, &locals, error)) goto error_add_frame; + if (teco_qreg_current && !teco_qreg_current->must_undo) { + /* currently editing local Q-Register */ + teco_error_editinglocalqreg_set(error, teco_qreg_current->head.name.data, + teco_qreg_current->head.name.len); + goto error_add_frame; + } return teco_expressions_discard_args(error) && teco_expressions_brace_close(error); diff --git a/tests/testsuite.at b/tests/testsuite.at index e33a2c4..648e76c 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -79,6 +79,11 @@ AT_CHECK([$SCITECO -e "[[a 23Ub ]]b Qb\"N(0/0)'"], 0, ignore, ignore) AT_CHECK([$SCITECO -e "[[\$ @FG'..' ]]\$ :Q\$-1Q\$-^^r\"=(0/0)'"], 0, ignore, ignore) AT_CLEANUP +AT_SETUP([Editing local registers in macro calls]) +AT_CHECK([$SCITECO -e '@^Ua{@EQ.x//} :Ma @^U.x/FOO/'], 0, ignore, ignore) +AT_CHECK([$SCITECO -e '@^Ua{@EQ.x//} Ma @^U.x/FOO/'], 1, ignore, ignore) +AT_CLEANUP + AT_SETUP([8-bit cleanliness]) AT_CHECK([$SCITECO -e "0@I//J 0A\"N(0/0)' :@S/^@/\"F(0/0)'"], 0, ignore, ignore) AT_CHECK([$SCITECO -e "@EQa//0EE 1U*0EE 0:@EUa/f^@^@/ :Qa-4\"N(0/0)' Ga Z-4\"N(0/0)'"], 0, ignore, ignore) |