[PATCH 11/11 v2] vi: changes to option handling
Ron Yorston
rmy at pobox.com
Tue Apr 6 21:11:21 UTC 2021
Since commit 70ee23399 (vi: code shrink) the ':set' command is
unable to process multiple options on a line. Fix this by
temporarily null-terminating each option.
Change the default setting for all options to off to match vim.
Actually, 'flash' isn't an option in vim, only traditional vi,
where it's on by default. In vim the corresponding option is
'visualbell' which defaults to off. POSIX doesn't have either
of these.
Allow the abbreviation 'ts' for the 'tabstop' option.
Issue an error message if:
- an option is not implemented
- an option that takes a value has no '=' or has a 'no' prefix
- a boolean option has a '='
function old new delta
colon 3292 3341 +49
.rodata 105147 105167 +20
vi_main 305 301 -4
setops 85 - -85
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 2/1 up/down: 69/-89) Total: -20 bytes
v2: Try harder to detect invalid options. Thanks to Peter D for pointing
this out.
Signed-off-by: Ron Yorston <rmy at pobox.com>
---
editors/vi.c | 94 +++++++++++++++++++++++++++++++++++-----------------
1 file changed, 64 insertions(+), 30 deletions(-)
diff --git a/editors/vi.c b/editors/vi.c
index 3e09900a8..143c76d11 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -287,16 +287,21 @@ struct globals {
// the rest
smallint vi_setops;
-#define VI_AUTOINDENT 1
-#define VI_SHOWMATCH 2
-#define VI_IGNORECASE 4
-#define VI_ERR_METHOD 8
+// the order of these values must match that in setops()
+#define VI_AUTOINDENT (1 << 0)
+#define VI_ERR_METHOD (1 << 1)
+#define VI_IGNORECASE (1 << 2)
+#define VI_SHOWMATCH (1 << 3)
+#define VI_TABSTOP (1 << 4)
#define autoindent (vi_setops & VI_AUTOINDENT)
#define showmatch (vi_setops & VI_SHOWMATCH )
#define ignorecase (vi_setops & VI_IGNORECASE)
// indicate error with beep or flash
#define err_method (vi_setops & VI_ERR_METHOD)
+// bitmask to indicate which options take a value
+#define VI_TAKE_VALUE VI_TABSTOP
+
#if ENABLE_FEATURE_VI_READONLY
smallint readonly_mode;
#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
@@ -2401,17 +2406,51 @@ static char *get_address(char *p, int *b, int *e) // get two colon addrs, if pre
}
# if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
-static void setops(const char *args, const char *nm_longname, int flg_no, int opt)
+// order must match option flags
+#define OPTS \
+ "ai\0""autoindent\0" \
+ "fl\0""flash\0" \
+ "ic\0""ignorecase\0" \
+ "sm\0""showmatch\0" \
+ "ts\0""tabstop\0"
+
+static void setops(const char *args, int flg_no)
{
- const char *a = args + flg_no;
+ char *eq;
+ int index, opt = 0, error = FALSE;
- if (strcmp(a, nm_longname) == 0
- || strcmp(a, nm_longname + 3) == 0
- ) {
- if (flg_no)
- vi_setops &= ~opt;
- else
- vi_setops |= opt;
+ eq = strchr(args, '=');
+ if (eq)
+ *eq = '\0';
+
+ index = index_in_strings(OPTS, args + flg_no);
+ if (index == -1) {
+ error = TRUE;
+ } else {
+ opt = 1 << (index/2);
+
+ // It's an error if:
+ // an option that takes a value has no "=" or has a prefix
+ // a boolean option has "="
+ if ((opt & VI_TAKE_VALUE && (!eq || flg_no)) ||
+ (opt & ~VI_TAKE_VALUE && eq))
+ error = TRUE;
+ }
+
+ if (error) {
+ status_line_bold("option error: %s", args + flg_no);
+ return;
+ }
+
+ if (opt == VI_TABSTOP) {
+ int t = bb_strtou(eq + 1, NULL, 10);
+ if (t > 0 && t <= MAX_TABSTOP)
+ tabstop = t;
+ }
+ else if (flg_no) {
+ vi_setops &= ~opt;
+ } else {
+ vi_setops |= opt;
}
}
# endif
@@ -2771,10 +2810,10 @@ static void colon(char *buf)
# if ENABLE_FEATURE_VI_SET
} else if (strncmp(cmd, "set", i) == 0) { // set or clear features
# if ENABLE_FEATURE_VI_SETOPTS
- char *argp;
+ char *argp, *argn, oldch;
# endif
// only blank is regarded as args delimiter. What about tab '\t'?
- if (!args[0] || strcasecmp(args, "all") == 0) {
+ if (!args[0] || strcmp(args, "all") == 0) {
// print out values of all options
# if ENABLE_FEATURE_VI_SETOPTS
status_line_bold(
@@ -2798,17 +2837,12 @@ static void colon(char *buf)
i = 0;
if (argp[0] == 'n' && argp[1] == 'o') // "noXXX"
i = 2;
- setops(argp, "ai""\0""autoindent", i, VI_AUTOINDENT);
- setops(argp, "fl""\0""flash" , i, VI_ERR_METHOD);
- setops(argp, "ic""\0""ignorecase", i, VI_IGNORECASE);
- setops(argp, "sm""\0""showmatch" , i, VI_SHOWMATCH );
- if (strncmp(argp, "tabstop=", 8) == 0) {
- int t = bb_strtou(argp + 8, NULL, 10);
- if (t > 0 && t <= MAX_TABSTOP)
- tabstop = t;
- }
- argp = skip_non_whitespace(argp);
- argp = skip_whitespace(argp);
+ argn = skip_non_whitespace(argp);
+ oldch = *argn;
+ *argn = '\0';
+ setops(argp, i);
+ *argn = oldch;
+ argp = skip_whitespace(argn);
}
# endif /* FEATURE_VI_SETOPTS */
# endif /* FEATURE_VI_SET */
@@ -4435,10 +4469,10 @@ int vi_main(int argc, char **argv)
}
#endif
- // autoindent is not default in vim 7.3
- vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE;
- // 1- process $HOME/.exrc file (not inplemented yet)
- // 2- process EXINIT variable from environment
+ // all of our options are disabled by default in vim
+ //vi_setops = 0;
+ // 1- process EXINIT variable from environment
+ // 2- if EXINIT is unset process $HOME/.exrc file (not inplemented yet)
// 3- process command line args
#if ENABLE_FEATURE_VI_COLON
{
--
2.30.2
More information about the busybox
mailing list