To: vim_dev@googlegroups.com Subject: Patch 9.0.1275 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1275 Problem: The code for setting options is too complicated. Solution: Refactor the do_set() function. (Yegappan Lakshmanan, Lewis Russell, closes #11932) Files: src/option.c, src/optionstr.c *** ../vim-9.0.1274/src/option.c 2023-01-25 15:04:17.939549253 +0000 --- src/option.c 2023-02-02 16:32:42.893667705 +0000 *************** *** 10,25 **** /* * 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). * - 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 variable to the window or buffer struct in structs.h. * - For a window option, add some code to copy_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. * - 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 --- 10,29 ---- /* * 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 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 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 ! * 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,2165 **** } /* ! * 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 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; 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]; ! if (*arg == NUL) { ! showoptions(0, opt_flags); ! did_show = TRUE; ! goto theend; } ! while (*arg != NUL) // loop to process all options { ! errmsg = NULL; ! startarg = arg; // remember for error message ! 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 { ! 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; } ! ! // find end of name ! key = 0; ! if (*arg == '<') ! { ! opt_idx = -1; ! // look out for ;> ! 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 { ! 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); } ! // 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] == '='))) ! { ! errmsg = e_no_white_space_allowed_between_option_and; ! arg = p; ! startarg = p; ! goto skip; ! } } ! 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] == '+') ! { ! 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 ! { ! key_name[0] = KS_KEY; ! key_name[1] = (key & 0xff); ! } } ! ! // 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 { ! 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))) ! { ! /* ! * 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) ! { ! // 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]); ! } ! #endif } else { ! char_u *p; ! ! p = find_termcode(key_name); ! if (p == NULL) ! { ! errmsg = e_key_code_not_set; ! goto skip; ! } ! 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 (flags & P_BOOL) // boolean ! { ! if (nextchar == '=' || nextchar == ':') ! { ! errmsg = e_invalid_argument; ! 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; ! } ! 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_invalid_argument; ! goto skip; ! } ! if (flags & P_NUM) // numeric ! { ! /* ! * Different ways to set a number option: ! * & set to default value ! * < set to global value ! * 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 (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; ! 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); ! } ! } ! if (opt_idx >= 0) ! did_set_option( ! opt_idx, opt_flags, op == OP_NONE, value_checked); } - skip: /* * Advance to next argument. * - skip until a blank found, taking care of backslashes --- 1637,2199 ---- } /* ! * 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. */ ! 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 *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; int len; ! set_op_T op = 0; long_u flags; // flags for current option char_u *varp = NULL; // pointer to variable for current option char_u key_name[2]; + int cp_val = 0; + varnumber_T value; + int i; ! 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; } ! // find end of name ! key = 0; ! if (*arg == '<') { ! opt_idx = -1; ! // look out for ;> ! 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 ! { ! 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); ! } ! ! // 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] == '='))) ! { ! errmsg = e_no_white_space_allowed_between_option_and; ! arg = p; ! *startarg = p; ! goto skip; ! } ! } ! 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] == '+') { ! 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 ! { ! key_name[0] = KS_KEY; ! key_name[1] = (key & 0xff); ! } ! } ! ! // 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 { ! 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))) ! { ! /* ! * 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) { ! // 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]); } ! #endif ! } ! else ! { ! char_u *p; ! p = find_termcode(key_name); ! if (p == NULL) { ! errmsg = e_key_code_not_set; ! goto skip; } ! 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 (flags & P_BOOL) // boolean ! { ! if (nextchar == '=' || nextchar == ':') { ! errmsg = e_invalid_argument; 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; } ! 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_invalid_argument; goto skip; } ! if (flags & P_NUM) // numeric { ! /* ! * Different ways to set a number option: ! * & set to default value ! * < set to global value ! * 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 (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 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; ! *stopopteval = TRUE; ! goto skip; } ! } ! else // key code option ! { ! char_u *p; ! ! 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); } ! } ! if (opt_idx >= 0) ! did_set_option( ! opt_idx, opt_flags, op == OP_NONE, value_checked); ! } ! skip: ! *argp = arg; ! return errmsg; ! } ! /* ! * 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 (*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; /* * Advance to next argument. * - skip until a blank found, taking care of backslashes *************** *** 2175,2201 **** if (*arg != '=') break; } - } ! if (errmsg != NULL) ! { ! 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; ! return FAIL; } arg = skipwhite(arg); --- 2209,2235 ---- if (*arg != '=') break; } ! if (errmsg != NULL) ! { ! 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; ! ! return FAIL; ! } } arg = skipwhite(arg); *** ../vim-9.0.1274/src/optionstr.c 2023-01-31 13:25:55.062216292 +0000 --- src/optionstr.c 2023-02-02 16:24:59.358259406 +0000 *************** *** 680,686 **** // 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); } return errmsg; --- 680,687 ---- // Both 'term' and 'ttytype' point to T_NAME, only set the // P_ALLOCED flag on 'term'. *opt_idx = findoption((char_u *)"term"); ! if (*opt_idx >= 0) ! *free_oldval = (get_option_flags(*opt_idx) & P_ALLOCED); } return errmsg; *** ../vim-9.0.1274/src/version.c 2023-02-02 13:30:09.711079343 +0000 --- src/version.c 2023-02-02 16:26:29.198230063 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1275, /**/ -- From "know your smileys": O:-) Saint /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///