diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2023-02-02 16:34:11 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2023-02-02 16:34:11 +0000 |
commit | 78012f55faf7444e554c0a97a589d99fa215bea9 (patch) | |
tree | 1be0c2873a9fe80dbd806eba1167501c3eb351fa /src | |
parent | be4e01637e71c8d5095c33b9861fd70b41476732 (diff) |
patch 9.0.1275: the code for setting options is too complicatedv9.0.1275
Problem: The code for setting options is too complicated.
Solution: Refactor the do_set() function. (Yegappan Lakshmanan, Lewis
Russell, closes #11932)
Diffstat (limited to 'src')
-rw-r--r-- | src/option.c | 980 | ||||
-rw-r--r-- | src/optionstr.c | 3 | ||||
-rw-r--r-- | src/version.c | 2 |
3 files changed, 511 insertions, 474 deletions
diff --git a/src/option.c b/src/option.c index 528be65aac..1aed4e1a0d 100644 --- a/src/option.c +++ b/src/option.c @@ -10,16 +10,20 @@ /* * Code to handle user-settable options. This is all pretty much table- * driven. Checklist for adding a new option: - * - Put it in the options array below (copy an existing entry). + * - Put it in the options array in optiondefs.h (copy an existing entry). * - For a global option: Add a variable for it in option.h. * - For a buffer or window local option: - * - Add a PV_XX entry to the enum below. + * - Add a PV_XX macro definition to the optiondefs.h file. * - Add a variable to the window or buffer struct in structs.h. * - For a window option, add some code to copy_winopt(). + * - For a window string option, add code to check_win_options() and + * clear_winopt(). * - For a buffer option, add some code to buf_copy_options(). * - For a buffer string option, add code to check_buf_options(). - * - If it's a numeric option, add any necessary bounds checks to do_set(). - * - If it's a list of flags, add some code in do_set(), search for WW_ALL. + * - If it's a numeric option, add any necessary bounds checks to + * set_num_option(). + * - If it's a list of flags, add some code in did_set_string_option(), search + * for WW_ALL. * - When adding an option with expansion (P_EXPAND), but with a different * default for Vi and Vim (no P_VI_DEF), add some code at VIMEXP. * - Add documentation! One line in doc/quickref.txt, full description in @@ -1633,533 +1637,563 @@ do_set_string( } /* - * Parse 'arg' for option settings. - * - * 'arg' may be IObuff, but only when no errors can be present and option - * does not need to be expanded with option_expand(). - * "opt_flags": - * 0 for ":set" - * OPT_GLOBAL for ":setglobal" - * OPT_LOCAL for ":setlocal" and a modeline - * OPT_MODELINE for a modeline - * OPT_WINONLY to only set window-local options - * OPT_NOWIN to skip setting window-local options - * OPT_ONECOLUMN do not use multiple columns - * - * returns FAIL if an error is detected, OK otherwise + * Set an option to a new value. + * Return NULL if OK, return an untranslated error message when something is + * wrong. "errbuf[errbuflen]" can be used to create the error message. */ - int -do_set( - char_u *arg_start, // option string (may be written to!) - int opt_flags) + static char * +do_set_option( + int opt_flags, + char_u **argp, + char_u *arg_start, + char_u **startarg, + int *did_show, + int *stopopteval, + char *errbuf, + size_t errbuflen) { - char_u *arg = arg_start; + char *errmsg = NULL; + int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name + int nextchar; // next non-white char after option name + int afterchar; // character just after option name + char_u *arg = *argp; + int key; int opt_idx; - char *errmsg; - char errbuf[80]; - char_u *startarg; - int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name - int nextchar; // next non-white char after option name - int afterchar; // character just after option name int len; - int i; - varnumber_T value; - int key; + set_op_T op = 0; long_u flags; // flags for current option char_u *varp = NULL; // pointer to variable for current option - int did_show = FALSE; // already showed one value - set_op_T op = 0; - int cp_val = 0; char_u key_name[2]; + int cp_val = 0; + varnumber_T value; + int i; - if (*arg == NUL) + prefix = 1; + if (STRNCMP(arg, "no", 2) == 0 && STRNCMP(arg, "novice", 6) != 0) { - showoptions(0, opt_flags); - did_show = TRUE; - goto theend; + prefix = 0; + arg += 2; + } + else if (STRNCMP(arg, "inv", 3) == 0) + { + prefix = 2; + arg += 3; } - while (*arg != NUL) // loop to process all options + // find end of name + key = 0; + if (*arg == '<') + { + opt_idx = -1; + // look out for <t_>;> + if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) + len = 5; + else + { + len = 1; + while (arg[len] != NUL && arg[len] != '>') + ++len; + } + if (arg[len] != '>') + { + errmsg = e_invalid_argument; + goto skip; + } + arg[len] = NUL; // put NUL after name + if (arg[1] == 't' && arg[2] == '_') // could be term code + opt_idx = findoption(arg + 1); + arg[len++] = '>'; // restore '>' + if (opt_idx == -1) + key = find_key_option(arg + 1, TRUE); + } + else { - errmsg = NULL; - startarg = arg; // remember for error message + len = 0; + /* + * The two characters after "t_" may not be alphanumeric. + */ + if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) + len = 4; + else + while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') + ++len; + nextchar = arg[len]; + arg[len] = NUL; // put NUL after name + opt_idx = findoption(arg); + arg[len] = nextchar; // restore nextchar + if (opt_idx == -1) + key = find_key_option(arg, FALSE); + } - if (STRNCMP(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3]) - && !(opt_flags & OPT_MODELINE)) + // remember character after option name + afterchar = arg[len]; + + if (in_vim9script()) + { + char_u *p = skipwhite(arg + len); + + // disallow white space before =val, +=val, -=val, ^=val + if (p > arg + len && (p[0] == '=' + || (vim_strchr((char_u *)"+-^", p[0]) != NULL + && p[1] == '='))) { - /* - * ":set all" show all options. - * ":set all&" set all options to their default value. - */ - arg += 3; - if (*arg == '&') - { - ++arg; - // Only for :set command set global value of local options. - set_options_default(OPT_FREE | opt_flags); - didset_options(); - didset_options2(); - redraw_all_later(UPD_CLEAR); - } - else - { - showoptions(1, opt_flags); - did_show = TRUE; - } + errmsg = e_no_white_space_allowed_between_option_and; + arg = p; + *startarg = p; + goto skip; } - else if (STRNCMP(arg, "termcap", 7) == 0 && !(opt_flags & OPT_MODELINE)) + } + else + // skip white space, allow ":set ai ?", ":set hlsearch !" + while (VIM_ISWHITE(arg[len])) + ++len; + + op = OP_NONE; + if (arg[len] != NUL && arg[len + 1] == '=') + { + if (arg[len] == '+') { - showoptions(2, opt_flags); - show_termcodes(opt_flags); - did_show = TRUE; - arg += 7; + op = OP_ADDING; // "+=" + ++len; + } + else if (arg[len] == '^') + { + op = OP_PREPENDING; // "^=" + ++len; + } + else if (arg[len] == '-') + { + op = OP_REMOVING; // "-=" + ++len; + } + } + nextchar = arg[len]; + + if (opt_idx == -1 && key == 0) // found a mismatch: skip + { + if (in_vim9script() && arg > arg_start + && vim_strchr((char_u *)"!&<", *arg) != NULL) + errmsg = e_no_white_space_allowed_between_option_and; + else + errmsg = e_unknown_option; + goto skip; + } + + if (opt_idx >= 0) + { + if (options[opt_idx].var == NULL) // hidden option: skip + { + // Only give an error message when requesting the value of + // a hidden option, ignore setting it. + if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL + && (!(options[opt_idx].flags & P_BOOL) + || nextchar == '?')) + errmsg = e_option_not_supported; + goto skip; + } + + flags = options[opt_idx].flags; + varp = get_varp_scope(&(options[opt_idx]), opt_flags); + } + else + { + flags = P_STRING; + if (key < 0) + { + key_name[0] = KEY2TERMCAP0(key); + key_name[1] = KEY2TERMCAP1(key); } else { - prefix = 1; - if (STRNCMP(arg, "no", 2) == 0 && STRNCMP(arg, "novice", 6) != 0) - { - prefix = 0; - arg += 2; - } - else if (STRNCMP(arg, "inv", 3) == 0) - { - prefix = 2; - arg += 3; - } + key_name[0] = KS_KEY; + key_name[1] = (key & 0xff); + } + } - // find end of name - key = 0; - if (*arg == '<') + // Skip all options that are not window-local (used when showing + // an already loaded buffer in a window). + if ((opt_flags & OPT_WINONLY) + && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) + goto skip; + + // Skip all options that are window-local (used for :vimgrep). + if ((opt_flags & OPT_NOWIN) && opt_idx >= 0 + && options[opt_idx].var == VAR_WIN) + goto skip; + + // Disallow changing some options from modelines. + if (opt_flags & OPT_MODELINE) + { + if (flags & (P_SECURE | P_NO_ML)) + { + errmsg = e_not_allowed_in_modeline; + goto skip; + } + if ((flags & P_MLE) && !p_mle) + { + errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; + goto skip; + } +#ifdef FEAT_DIFF + // In diff mode some options are overruled. This avoids that + // 'foldmethod' becomes "marker" instead of "diff" and that + // "wrap" gets set. + if (curwin->w_p_diff + && opt_idx >= 0 // shut up coverity warning + && ( +#ifdef FEAT_FOLDING + options[opt_idx].indir == PV_FDM || +#endif + options[opt_idx].indir == PV_WRAP)) + goto skip; +#endif + } + +#ifdef HAVE_SANDBOX + // Disallow changing some options in the sandbox + if (sandbox != 0 && (flags & P_SECURE)) + { + errmsg = e_not_allowed_in_sandbox; + goto skip; + } +#endif + + if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) + { + arg += len; + cp_val = p_cp; + if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') + { + if (arg[3] == 'm') // "opt&vim": set to Vim default { - opt_idx = -1; - // look out for <t_>;> - if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) - len = 5; - else - { - len = 1; - while (arg[len] != NUL && arg[len] != '>') - ++len; - } - if (arg[len] != '>') - { - errmsg = e_invalid_argument; - goto skip; - } - arg[len] = NUL; // put NUL after name - if (arg[1] == 't' && arg[2] == '_') // could be term code - opt_idx = findoption(arg + 1); - arg[len++] = '>'; // restore '>' - if (opt_idx == -1) - key = find_key_option(arg + 1, TRUE); + cp_val = FALSE; + arg += 3; } - else + else // "opt&vi": set to Vi default { - len = 0; - /* - * The two characters after "t_" may not be alphanumeric. - */ - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) - len = 4; - else - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') - ++len; - nextchar = arg[len]; - arg[len] = NUL; // put NUL after name - opt_idx = findoption(arg); - arg[len] = nextchar; // restore nextchar - if (opt_idx == -1) - key = find_key_option(arg, FALSE); + cp_val = TRUE; + arg += 2; } + } + if (vim_strchr((char_u *)"?!&<", nextchar) != NULL + && arg[1] != NUL && !VIM_ISWHITE(arg[1])) + { + errmsg = e_trailing_characters; + goto skip; + } + } - // remember character after option name - afterchar = arg[len]; - - if (in_vim9script()) + /* + * allow '=' and ':' for historical reasons (MSDOS command.com + * allows only one '=' character per "set" command line. grrr. (jw) + */ + if (nextchar == '?' + || (prefix == 1 + && vim_strchr((char_u *)"=:&<", nextchar) == NULL + && !(flags & P_BOOL))) + { + /* + * print value + */ + if (*did_show) + msg_putchar('\n'); // cursor below last one + else + { + gotocmdline(TRUE); // cursor at status line + *did_show = TRUE; // remember that we did a line + } + if (opt_idx >= 0) + { + showoneopt(&options[opt_idx], opt_flags); +#ifdef FEAT_EVAL + if (p_verbose > 0) { - char_u *p = skipwhite(arg + len); - - // disallow white space before =val, +=val, -=val, ^=val - if (p > arg + len && (p[0] == '=' - || (vim_strchr((char_u *)"+-^", p[0]) != NULL - && p[1] == '='))) - { - errmsg = e_no_white_space_allowed_between_option_and; - arg = p; - startarg = p; - goto skip; - } + // Mention where the option was last set. + if (varp == options[opt_idx].var) + last_set_msg(options[opt_idx].script_ctx); + else if ((int)options[opt_idx].indir & PV_WIN) + last_set_msg(curwin->w_p_script_ctx[ + (int)options[opt_idx].indir & PV_MASK]); + else if ((int)options[opt_idx].indir & PV_BUF) + last_set_msg(curbuf->b_p_script_ctx[ + (int)options[opt_idx].indir & PV_MASK]); } - else - // skip white space, allow ":set ai ?", ":set hlsearch !" - while (VIM_ISWHITE(arg[len])) - ++len; +#endif + } + else + { + char_u *p; - op = OP_NONE; - if (arg[len] != NUL && arg[len + 1] == '=') + p = find_termcode(key_name); + if (p == NULL) { - if (arg[len] == '+') - { - op = OP_ADDING; // "+=" - ++len; - } - else if (arg[len] == '^') - { - op = OP_PREPENDING; // "^=" - ++len; - } - else if (arg[len] == '-') - { - op = OP_REMOVING; // "-=" - ++len; - } + errmsg = e_key_code_not_set; + goto skip; } - nextchar = arg[len]; + else + (void)show_one_termcode(key_name, p, TRUE); + } + if (nextchar != '?' + && nextchar != NUL && !VIM_ISWHITE(afterchar)) + errmsg = e_trailing_characters; + } + else + { + int value_checked = FALSE; - if (opt_idx == -1 && key == 0) // found a mismatch: skip + if (flags & P_BOOL) // boolean + { + if (nextchar == '=' || nextchar == ':') { - if (in_vim9script() && arg > arg_start - && vim_strchr((char_u *)"!&<", *arg) != NULL) - errmsg = e_no_white_space_allowed_between_option_and; - else - errmsg = e_unknown_option; + errmsg = e_invalid_argument; goto skip; } - if (opt_idx >= 0) - { - if (options[opt_idx].var == NULL) // hidden option: skip - { - // Only give an error message when requesting the value of - // a hidden option, ignore setting it. - if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL - && (!(options[opt_idx].flags & P_BOOL) - || nextchar == '?')) - errmsg = e_option_not_supported; - goto skip; - } - - flags = options[opt_idx].flags; - varp = get_varp_scope(&(options[opt_idx]), opt_flags); - } - else + /* + * ":set opt!": invert + * ":set opt&": reset to default value + * ":set opt<": reset to global value + */ + if (nextchar == '!') + value = *(int *)(varp) ^ 1; + else if (nextchar == '&') + value = (int)(long)(long_i)options[opt_idx].def_val[ + ((flags & P_VI_DEF) || cp_val) + ? VI_DEFAULT : VIM_DEFAULT]; + else if (nextchar == '<') { - flags = P_STRING; - if (key < 0) - { - key_name[0] = KEY2TERMCAP0(key); - key_name[1] = KEY2TERMCAP1(key); - } + // For 'autoread' -1 means to use global value. + if ((int *)varp == &curbuf->b_p_ar + && opt_flags == OPT_LOCAL) + value = -1; else - { - key_name[0] = KS_KEY; - key_name[1] = (key & 0xff); - } + value = *(int *)get_varp_scope(&(options[opt_idx]), + OPT_GLOBAL); } - - // Skip all options that are not window-local (used when showing - // an already loaded buffer in a window). - if ((opt_flags & OPT_WINONLY) - && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) - goto skip; - - // Skip all options that are window-local (used for :vimgrep). - if ((opt_flags & OPT_NOWIN) && opt_idx >= 0 - && options[opt_idx].var == VAR_WIN) - goto skip; - - // Disallow changing some options from modelines. - if (opt_flags & OPT_MODELINE) + else { - if (flags & (P_SECURE | P_NO_ML)) - { - errmsg = e_not_allowed_in_modeline; - goto skip; - } - if ((flags & P_MLE) && !p_mle) + /* + * ":set invopt": invert + * ":set opt" or ":set noopt": set or reset + */ + if (nextchar != NUL && !VIM_ISWHITE(afterchar)) { - errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; + errmsg = e_trailing_characters; goto skip; } -#ifdef FEAT_DIFF - // In diff mode some options are overruled. This avoids that - // 'foldmethod' becomes "marker" instead of "diff" and that - // "wrap" gets set. - if (curwin->w_p_diff - && opt_idx >= 0 // shut up coverity warning - && ( -#ifdef FEAT_FOLDING - options[opt_idx].indir == PV_FDM || -#endif - options[opt_idx].indir == PV_WRAP)) - goto skip; -#endif + if (prefix == 2) // inv + value = *(int *)(varp) ^ 1; + else + value = prefix; } -#ifdef HAVE_SANDBOX - // Disallow changing some options in the sandbox - if (sandbox != 0 && (flags & P_SECURE)) + errmsg = set_bool_option(opt_idx, varp, (int)value, + opt_flags); + } + else // numeric or string + { + if (vim_strchr((char_u *)"=:&<", nextchar) == NULL + || prefix != 1) { - errmsg = e_not_allowed_in_sandbox; + errmsg = e_invalid_argument; goto skip; } -#endif - if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) - { - arg += len; - cp_val = p_cp; - if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') - { - if (arg[3] == 'm') // "opt&vim": set to Vim default - { - cp_val = FALSE; - arg += 3; - } - else // "opt&vi": set to Vi default - { - cp_val = TRUE; - arg += 2; - } - } - if (vim_strchr((char_u *)"?!&<", nextchar) != NULL - && arg[1] != NUL && !VIM_ISWHITE(arg[1])) - { - errmsg = e_trailing_characters; - goto skip; - } - } - - /* - * allow '=' and ':' for historical reasons (MSDOS command.com - * allows only one '=' character per "set" command line. grrr. (jw) - */ - if (nextchar == '?' - || (prefix == 1 - && vim_strchr((char_u *)"=:&<", nextchar) == NULL - && !(flags & P_BOOL))) + if (flags & P_NUM) // numeric { /* - * print value + * Different ways to set a number option: + * & set to default value + * < set to global value + * <xx> accept special key codes for 'wildchar' + * c accept any non-digit for 'wildchar' + * [-]0-9 set number + * other error */ - if (did_show) - msg_putchar('\n'); // cursor below last one - else + ++arg; + if (nextchar == '&') + value = (long)(long_i)options[opt_idx].def_val[ + ((flags & P_VI_DEF) || cp_val) + ? VI_DEFAULT : VIM_DEFAULT]; + else if (nextchar == '<') { - gotocmdline(TRUE); // cursor at status line - did_show = TRUE; // remember that we did a line + // For 'undolevels' NO_LOCAL_UNDOLEVEL means to + // use the global value. + if ((long *)varp == &curbuf->b_p_ul + && opt_flags == OPT_LOCAL) + value = NO_LOCAL_UNDOLEVEL; + else + value = *(long *)get_varp_scope( + &(options[opt_idx]), OPT_GLOBAL); } - if (opt_idx >= 0) + else if (((long *)varp == &p_wc + || (long *)varp == &p_wcm) + && (*arg == '<' + || *arg == '^' + || (*arg != NUL + && (!arg[1] || VIM_ISWHITE(arg[1])) + && !VIM_ISDIGIT(*arg)))) { - showoneopt(&options[opt_idx], opt_flags); -#ifdef FEAT_EVAL - if (p_verbose > 0) + value = string_to_key(arg, FALSE); + if (value == 0 && (long *)varp != &p_wcm) { - // Mention where the option was last set. - if (varp == options[opt_idx].var) - last_set_msg(options[opt_idx].script_ctx); - else if ((int)options[opt_idx].indir & PV_WIN) - last_set_msg(curwin->w_p_script_ctx[ - (int)options[opt_idx].indir & PV_MASK]); - else if ((int)options[opt_idx].indir & PV_BUF) - last_set_msg(curbuf->b_p_script_ctx[ - (int)options[opt_idx].indir & PV_MASK]); + errmsg = e_invalid_argument; + goto skip; } -#endif } - else + else if (*arg == '-' || VIM_ISDIGIT(*arg)) { - char_u *p; - - p = find_termcode(key_name); - if (p == NULL) + // Allow negative (for 'undolevels'), octal and + // hex numbers. + vim_str2nr(arg, NULL, &i, STR2NR_ALL, + &value, NULL, 0, TRUE); + if (i == 0 || (arg[i] != NUL + && !VIM_ISWHITE(arg[i]))) { - errmsg = e_key_code_not_set; + errmsg = e_number_required_after_equal; goto skip; } - else - (void)show_one_termcode(key_name, p, TRUE); } - if (nextchar != '?' - && nextchar != NUL && !VIM_ISWHITE(afterchar)) - errmsg = e_trailing_characters; + else + { + errmsg = e_number_required_after_equal; + goto skip; + } + + if (op == OP_ADDING) + value = *(long *)varp + value; + else if (op == OP_PREPENDING) + value = *(long *)varp * value; + else if (op == OP_REMOVING) + value = *(long *)varp - value; + errmsg = set_num_option(opt_idx, varp, value, + errbuf, errbuflen, opt_flags); } - else + else if (opt_idx >= 0) // string { - int value_checked = FALSE; - - if (flags & P_BOOL) // boolean + if (do_set_string(opt_idx, opt_flags, &arg, nextchar, + op, flags, cp_val, varp, errbuf, + &value_checked, &errmsg) == FAIL) { - if (nextchar == '=' || nextchar == ':') - { - errmsg = e_invalid_argument; + if (errmsg != NULL) goto skip; - } - - /* - * ":set opt!": invert - * ":set opt&": reset to default value - * ":set opt<": reset to global value - */ - if (nextchar == '!') - value = *(int *)(varp) ^ 1; - else if (nextchar == '&') - value = (int)(long)(long_i)options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) - ? VI_DEFAULT : VIM_DEFAULT]; - else if (nextchar == '<') - { - // For 'autoread' -1 means to use global value. - if ((int *)varp == &curbuf->b_p_ar - && opt_flags == OPT_LOCAL) - value = -1; - else - value = *(int *)get_varp_scope(&(options[opt_idx]), - OPT_GLOBAL); - } - else - { - /* - * ":set invopt": invert - * ":set opt" or ":set noopt": set or reset - */ - if (nextchar != NUL && !VIM_ISWHITE(afterchar)) - { - errmsg = e_trailing_characters; - goto skip; - } - if (prefix == 2) // inv - value = *(int *)(varp) ^ 1; - else - value = prefix; - } + *stopopteval = TRUE; + goto skip; + } + } + else // key code option + { + char_u *p; - errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); + if (nextchar == '&') + { + if (add_termcap_entry(key_name, TRUE) == FAIL) + errmsg = e_not_found_in_termcap; } - else // numeric or string + else { - if (vim_strchr((char_u *)"=:&<", nextchar) == NULL - || prefix != 1) - { - errmsg = e_invalid_argument; - goto skip; - } + ++arg; // jump to after the '=' or ':' + for (p = arg; *p && !VIM_ISWHITE(*p); ++p) + if (*p == '\\' && p[1] != NUL) + ++p; + nextchar = *p; + *p = NUL; + add_termcode(key_name, arg, FALSE); + *p = nextchar; + } + if (full_screen) + ttest(FALSE); + redraw_all_later(UPD_CLEAR); + } + } - if (flags & P_NUM) // numeric - { - /* - * Different ways to set a number option: - * & set to default value - * < set to global value - * <xx> accept special key codes for 'wildchar' - * c accept any non-digit for 'wildchar' - * [-]0-9 set number - * other error - */ - ++arg; - if (nextchar == '&') - value = (long)(long_i)options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) - ? VI_DEFAULT : VIM_DEFAULT]; - else if (nextchar == '<') - { - // For 'undolevels' NO_LOCAL_UNDOLEVEL means to - // use the global value. - if ((long *)varp == &curbuf->b_p_ul - && opt_flags == OPT_LOCAL) - value = NO_LOCAL_UNDOLEVEL; - else - value = *(long *)get_varp_scope( - &(options[opt_idx]), OPT_GLOBAL); - } - else if (((long *)varp == &p_wc - || (long *)varp == &p_wcm) - && (*arg == '<' - || *arg == '^' - || (*arg != NUL - && (!arg[1] || VIM_ISWHITE(arg[1])) - && !VIM_ISDIGIT(*arg)))) - { - value = string_to_key(arg, FALSE); - if (value == 0 && (long *)varp != &p_wcm) - { - errmsg = e_invalid_argument; - goto skip; - } - } - else if (*arg == '-' || VIM_ISDIGIT(*arg)) - { - // Allow negative (for 'undolevels'), octal and - // hex numbers. - vim_str2nr(arg, NULL, &i, STR2NR_ALL, - &value, NULL, 0, TRUE); - if (i == 0 || (arg[i] != NUL - && !VIM_ISWHITE(arg[i]))) - { - errmsg = e_number_required_after_equal; - goto skip; - } - } - else - { - errmsg = e_number_required_after_equal; - goto skip; - } + if (opt_idx >= 0) + did_set_option( + opt_idx, opt_flags, op == OP_NONE, value_checked); + } - if (op == OP_ADDING) - value = *(long *)varp + value; - else if (op == OP_PREPENDING) - value = *(long *)varp * value; - else if (op == OP_REMOVING) - value = *(long *)varp - value; - errmsg = set_num_option(opt_idx, varp, value, - errbuf, sizeof(errbuf), opt_flags); - } - else if (opt_idx >= 0) // string - { - if (do_set_string(opt_idx, opt_flags, &arg, nextchar, - op, flags, cp_val, varp, errbuf, - &value_checked, &errmsg) == FAIL) - { - if (errmsg != NULL) - goto skip; - break; - } - } - else // key code option - { - char_u *p; +skip: + *argp = arg; + return errmsg; +} - if (nextchar == '&') - { - if (add_termcap_entry(key_name, TRUE) == FAIL) - errmsg = e_not_found_in_termcap; - } - else - { - ++arg; // jump to after the '=' or ':' - for (p = arg; *p && !VIM_ISWHITE(*p); ++p) - if (*p == '\\' && p[1] != NUL) - ++p; - nextchar = *p; - *p = NUL; - add_termcode(key_name, arg, FALSE); - *p = nextchar; - } - if (full_screen) - ttest(FALSE); - redraw_all_later(UPD_CLEAR); - } - } +/* + * Parse 'arg' for option settings. + * + * 'arg' may be IObuff, but only when no errors can be present and option + * does not need to be expanded with option_expand(). + * "opt_flags": + * 0 for ":set" + * OPT_GLOBAL for ":setglobal" + * OPT_LOCAL for ":setlocal" and a modeline + * OPT_MODELINE for a modeline + * OPT_WINONLY to only set window-local options + * OPT_NOWIN to skip setting window-local options + * OPT_ONECOLUMN do not use multiple columns + * + * Returns FAIL if an error is detected, OK otherwise. + */ + int +do_set( + char_u *arg_start, // option string (may be written to!) + int opt_flags) +{ + char_u *arg = arg_start; + int i; + int did_show = FALSE; // already showed one value - if (opt_idx >= 0) - did_set_option( - opt_idx, opt_flags, op == OP_NONE, value_checked); + if (*arg == NUL) + { + showoptions(0, opt_flags); + did_show = TRUE; + goto theend; + } + + while (*arg != NUL) // loop to process all options + { + if (STRNCMP(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3]) + && !(opt_flags & OPT_MODELINE)) + { + /* + * ":set all" show all options. + * ":set all&" set all options to their default value. + */ + arg += 3; + if (*arg == '&') + { + ++arg; + // Only for :set command set global value of local options. + set_options_default(OPT_FREE | opt_flags); + didset_options(); + didset_options2(); + redraw_all_later(UPD_CLEAR); } + else + { + showoptions(1, opt_flags); + did_show = TRUE; + } + } + else if (STRNCMP(arg, "termcap", 7) == 0 && !(opt_flags & OPT_MODELINE)) + { + showoptions(2, opt_flags); + show_termcodes(opt_flags); + did_show = TRUE; + arg += 7; + } + else + { + int stopopteval = FALSE; + char *errmsg = NULL; + char errbuf[80]; + char_u *startarg = arg; + + errmsg = do_set_option(opt_flags, &arg, arg_start, &startarg, + &did_show, &stopopteval, errbuf, + sizeof(errbuf)); + if (stopopteval) + break; -skip: /* * Advance to next argument. * - skip until a blank found, taking care of backslashes @@ -2175,27 +2209,27 @@ skip: if (*arg != '=') break; } - } - if (errmsg != NULL) - { - vim_strncpy(IObuff, (char_u *)_(errmsg), IOSIZE - 1); - i = (int)STRLEN(IObuff) + 2; - if (i + (arg - startarg) < IOSIZE) + if (errmsg != NULL) { - // append the argument with the error - STRCAT(IObuff, ": "); - mch_memmove(IObuff + i, startarg, (arg - startarg)); - IObuff[i + (arg - startarg)] = NUL; - } - // make sure all characters are printable - trans_characters(IObuff, IOSIZE); + vim_strncpy(IObuff, (char_u *)_(errmsg), IOSIZE - 1); + i = (int)STRLEN(IObuff) + 2; + if (i + (arg - startarg) < IOSIZE) + { + // append the argument with the error + STRCAT(IObuff, ": "); + mch_memmove(IObuff + i, startarg, (arg - startarg)); + IObuff[i + (arg - startarg)] = NUL; + } + // make sure all characters are printable + trans_characters(IObuff, IOSIZE); - ++no_wait_return; // wait_return() done later - emsg((char *)IObuff); // show error highlighted - --no_wait_return; + ++no_wait_return; // wait_return() done later + emsg((char *)IObuff); // show error highlighted + --no_wait_return; - return FAIL; + return FAIL; + } } arg = skipwhite(arg); diff --git a/src/optionstr.c b/src/optionstr.c index 46e9ac00b2..d218d5662b 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -680,7 +680,8 @@ did_set_term(int *opt_idx, long_u *free_oldval) // Both 'term' and 'ttytype' point to T_NAME, only set the // P_ALLOCED flag on 'term'. *opt_idx = findoption((char_u *)"term"); - *free_oldval = (get_option_flags(*opt_idx) & P_ALLOCED); + if (*opt_idx >= 0) + *free_oldval = (get_option_flags(*opt_idx) & P_ALLOCED); } return errmsg; diff --git a/src/version.c b/src/version.c index 8facdc4e7f..926cd7f650 100644 --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1275, +/**/ 1274, /**/ 1273, |