vi: changes to option handling

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                                               2944    3003     +59
.rodata                                           103171  103189     +18
vi_main                                              274     270      -4
setops                                                73       -     -73
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 2/1 up/down: 77/-77)              Total: 0 bytes

v2: Try harder to detect invalid options.  Thanks to Peter D for pointing
    this out.

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-04-06 22:11:21 +01:00 committed by Denys Vlasenko
parent 24198f652f
commit 9f017d9db0

View File

@ -278,16 +278,23 @@ struct globals {
int text_size; // size of the allocated buffer int text_size; // size of the allocated buffer
// the rest // the rest
smallint vi_setops; smallint vi_setops; // set by setops()
#define VI_AUTOINDENT 1 #define VI_AUTOINDENT (1 << 0)
#define VI_SHOWMATCH 2 #define VI_ERR_METHOD (1 << 1)
#define VI_IGNORECASE 4 #define VI_IGNORECASE (1 << 2)
#define VI_ERR_METHOD 8 #define VI_SHOWMATCH (1 << 3)
#define VI_TABSTOP (1 << 4)
#define autoindent (vi_setops & VI_AUTOINDENT) #define autoindent (vi_setops & VI_AUTOINDENT)
#define showmatch (vi_setops & VI_SHOWMATCH ) #define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
#define ignorecase (vi_setops & VI_IGNORECASE) #define ignorecase (vi_setops & VI_IGNORECASE)
// indicate error with beep or flash #define showmatch (vi_setops & VI_SHOWMATCH )
#define err_method (vi_setops & VI_ERR_METHOD) // order of constants and strings must match
#define OPTS_STR \
"ai\0""autoindent\0" \
"fl\0""flash\0" \
"ic\0""ignorecase\0" \
"sm\0""showmatch\0" \
"ts\0""tabstop\0"
#if ENABLE_FEATURE_VI_READONLY #if ENABLE_FEATURE_VI_READONLY
smallint readonly_mode; smallint readonly_mode;
@ -2380,17 +2387,38 @@ 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 # if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
static void setops(const char *args, const char *nm_longname, int flg_no, int opt) static void setops(char *args, int flg_no)
{ {
const char *a = args + flg_no; char *eq;
int index;
if (strcmp(a, nm_longname) == 0 eq = strchr(args, '=');
|| strcmp(a, nm_longname + 3) == 0 if (eq) *eq = '\0';
) { index = index_in_strings(OPTS_STR, args + flg_no);
if (flg_no) if (eq) *eq = '=';
vi_setops &= ~opt; if (index < 0) {
else bad:
vi_setops |= opt; status_line_bold("bad option: %s", args);
return;
}
index = 1 << (index >> 1); // convert to VI_bit
if (index & VI_TABSTOP) {
int t;
if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
goto bad;
t = bb_strtou(eq + 1, NULL, 10);
if (t <= 0 || t > MAX_TABSTOP)
goto bad;
tabstop = t;
return;
}
if (eq) goto bad; // boolean option has "="?
if (flg_no) {
vi_setops &= ~index;
} else {
vi_setops |= index;
} }
} }
# endif # endif
@ -2750,10 +2778,10 @@ static void colon(char *buf)
# if ENABLE_FEATURE_VI_SET # if ENABLE_FEATURE_VI_SET
} else if (strncmp(cmd, "set", i) == 0) { // set or clear features } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
# if ENABLE_FEATURE_VI_SETOPTS # if ENABLE_FEATURE_VI_SETOPTS
char *argp; char *argp, *argn, oldch;
# endif # endif
// only blank is regarded as args delimiter. What about tab '\t'? // 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 // print out values of all options
# if ENABLE_FEATURE_VI_SETOPTS # if ENABLE_FEATURE_VI_SETOPTS
status_line_bold( status_line_bold(
@ -2777,17 +2805,12 @@ static void colon(char *buf)
i = 0; i = 0;
if (argp[0] == 'n' && argp[1] == 'o') // "noXXX" if (argp[0] == 'n' && argp[1] == 'o') // "noXXX"
i = 2; i = 2;
setops(argp, "ai""\0""autoindent", i, VI_AUTOINDENT); argn = skip_non_whitespace(argp);
setops(argp, "fl""\0""flash" , i, VI_ERR_METHOD); oldch = *argn;
setops(argp, "ic""\0""ignorecase", i, VI_IGNORECASE); *argn = '\0';
setops(argp, "sm""\0""showmatch" , i, VI_SHOWMATCH ); setops(argp, i);
if (strncmp(argp, "tabstop=", 8) == 0) { *argn = oldch;
int t = bb_strtou(argp + 8, NULL, 10); argp = skip_whitespace(argn);
if (t > 0 && t <= MAX_TABSTOP)
tabstop = t;
}
argp = skip_non_whitespace(argp);
argp = skip_whitespace(argp);
} }
# endif /* FEATURE_VI_SETOPTS */ # endif /* FEATURE_VI_SETOPTS */
# endif /* FEATURE_VI_SET */ # endif /* FEATURE_VI_SET */
@ -4383,10 +4406,10 @@ int vi_main(int argc, char **argv)
} }
#endif #endif
// autoindent is not default in vim 7.3 // 0: all of our options are disabled by default in vim
vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE; //vi_setops = 0;
// 1- process $HOME/.exrc file (not inplemented yet) // 1- process EXINIT variable from environment
// 2- process EXINIT variable from environment // 2- if EXINIT is unset process $HOME/.exrc file (not inplemented yet)
// 3- process command line args // 3- process command line args
#if ENABLE_FEATURE_VI_COLON #if ENABLE_FEATURE_VI_COLON
{ {