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 <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
            {
!               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
!                        * <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 (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 <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
!     {
!       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
!                * <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 (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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20230202163442.C389C1C03D0%40moolenaar.net.

Raspunde prin e-mail lui