vi: reject change command when motion fails

If the motion command used to define the range of a change, yank or
delete fails the whole command should be rejected.  BusyBox vi already
handled failed searches in these circumstances.  Add some more cases:

- non-existent mark: d'x

- movement beyond end of file: c99999+ or 99999<<

This is implemented using a global variable which is set when a command
error is detected.  Unlike the case of motion within a line it's
insufficient to check that the motion command doesn't move the cursor:
this fails to process 'LyL' correctly, for example, as the second 'L'
doesn't move the cursor.

function                                             old     new   delta
indicate_error                                        75      82      +7
find_range                                           686     692      +6
do_cmd                                              4851    4852      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 14/0)               Total: 14 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2021-06-16 14:47:00 +01:00 committed by Denys Vlasenko
parent ac04eb3657
commit 038d400709

View File

@ -378,6 +378,7 @@ struct globals {
#if ENABLE_FEATURE_VI_SETOPTS #if ENABLE_FEATURE_VI_SETOPTS
int indentcol; // column of recently autoindent, 0 or -1 int indentcol; // column of recently autoindent, 0 or -1
#endif #endif
smallint cmd_error;
// former statics // former statics
#if ENABLE_FEATURE_VI_YANKMARK #if ENABLE_FEATURE_VI_YANKMARK
@ -502,6 +503,7 @@ struct globals {
#define dotcnt (G.dotcnt ) #define dotcnt (G.dotcnt )
#define last_search_pattern (G.last_search_pattern) #define last_search_pattern (G.last_search_pattern)
#define indentcol (G.indentcol ) #define indentcol (G.indentcol )
#define cmd_error (G.cmd_error )
#define edit_file__cur_line (G.edit_file__cur_line) #define edit_file__cur_line (G.edit_file__cur_line)
#define refresh__old_offset (G.refresh__old_offset) #define refresh__old_offset (G.refresh__old_offset)
@ -1099,6 +1101,7 @@ static void indicate_error(void)
if (crashme > 0) if (crashme > 0)
return; return;
#endif #endif
cmd_error = TRUE;
if (!err_method) { if (!err_method) {
write1(ESC_BELL); write1(ESC_BELL);
} else { } else {
@ -3399,8 +3402,11 @@ static int find_range(char **start, char **stop, int cmd)
#endif #endif
// these cmds operate on whole lines // these cmds operate on whole lines
buftype = WHOLE; buftype = WHOLE;
if (--cmdcnt > 0) if (--cmdcnt > 0) {
do_cmd('j'); do_cmd('j');
if (cmd_error)
buftype = -1;
}
} else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) { } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
// Most operate on char positions within a line. Of those that // Most operate on char positions within a line. Of those that
// don't '%' needs no special treatment, search commands are // don't '%' needs no special treatment, search commands are
@ -3430,6 +3436,8 @@ static int find_range(char **start, char **stop, int cmd)
// these operate on whole lines // these operate on whole lines
buftype = WHOLE; buftype = WHOLE;
do_cmd(c); // execute movement cmd do_cmd(c); // execute movement cmd
if (cmd_error)
buftype = -1;
} else if (c == ' ' || c == 'l') { } else if (c == ' ' || c == 'l') {
// forward motion by character // forward motion by character
int tmpcnt = (cmdcnt ?: 1); int tmpcnt = (cmdcnt ?: 1);
@ -3515,6 +3523,7 @@ static void do_cmd(int c)
// p = q = save_dot = buf; // quiet the compiler // p = q = save_dot = buf; // quiet the compiler
memset(buf, '\0', sizeof(buf)); memset(buf, '\0', sizeof(buf));
keep_index = FALSE; keep_index = FALSE;
cmd_error = FALSE;
show_status_line(); show_status_line();
@ -3699,6 +3708,8 @@ static void do_cmd(int c)
dot = q; dot = q;
dot_begin(); // go to B-o-l dot_begin(); // go to B-o-l
dot_skip_over_ws(); dot_skip_over_ws();
} else {
indicate_error();
} }
} else if (c1 == '\'') { // goto previous context } else if (c1 == '\'') { // goto previous context
dot = swap_context(dot); // swap current and previous context dot = swap_context(dot); // swap current and previous context