Patch 9.0.1283
Problem:    The code for setting options is too complicated.
Solution:   Refactor the do_set() function. (Yegappan Lakshmanan, Lewis
            Russell, closes #11945)
Files:      src/option.c


*** ../vim-9.0.1282/src/option.c        2023-02-02 16:34:07.741513245 +0000
--- src/option.c        2023-02-05 16:00:56.316836155 +0000
***************
*** 1211,1216 ****
--- 1211,1219 ----
      }
  }
  
+ /*
+  * :set operator types
+  */
  typedef enum {
      OP_NONE = 0,
      OP_ADDING,                // "opt+=arg"
***************
*** 1218,1223 ****
--- 1221,1401 ----
      OP_REMOVING,      // "opt-=arg"
  } set_op_T;
  
+ typedef enum {
+     PREFIX_NO = 0,    // "no" prefix
+     PREFIX_NONE,      // no prefix
+     PREFIX_INV,               // "inv" prefix
+ } set_prefix_T;
+ 
+ /*
+  * Return the prefix type for the option name in *argp.
+  */
+     static set_prefix_T
+ get_option_prefix(char_u **argp)
+ {
+     int               prefix = PREFIX_NONE;
+     char_u    *arg = *argp;
+ 
+     if (STRNCMP(arg, "no", 2) == 0 && STRNCMP(arg, "novice", 6) != 0)
+     {
+       prefix = PREFIX_NO;
+       arg += 2;
+     }
+     else if (STRNCMP(arg, "inv", 3) == 0)
+     {
+       prefix = PREFIX_INV;
+       arg += 3;
+     }
+ 
+     *argp = arg;
+     return prefix;
+ }
+ 
+ /*
+  * Parse the option name in "arg" and return the option index in "*opt_idxp",
+  * and the option name length in "*lenp".  For a <t_xx> option, return the key
+  * number in "*keyp".
+  *
+  * Returns FAIL if an option starting with "<" doesn't end with a ">",
+  * otherwise returns OK.
+  */
+     static int
+ parse_option_name(char_u *arg, int *opt_idxp, int *lenp, int *keyp)
+ {
+     int       key = 0;
+     int       len;
+     int       opt_idx;
+ 
+     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] != '>')
+           return FAIL;
+ 
+       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
+     {
+       int     nextchar;   // next non-white char after option name
+ 
+       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);
+     }
+ 
+     *keyp = key;
+     *lenp = len;
+     *opt_idxp = opt_idx;
+ 
+     return OK;
+ }
+ 
+ /*
+  * Get the option operator (+=, ^=, -=).
+  */
+     static set_op_T
+ get_opt_op(char_u *arg)
+ {
+     set_op_T op = OP_NONE;
+ 
+     if (*arg != NUL && *(arg + 1) == '=')
+     {
+       if (*arg == '+')
+           op = OP_ADDING;             // "+="
+       else if (*arg == '^')
+           op = OP_PREPENDING;         // "^="
+       else if (*arg == '-')
+           op = OP_REMOVING;           // "-="
+     }
+ 
+     return op;
+ }
+ 
+ /*
+  * Validate whether the value of the option in "opt_idx" can be changed.
+  * Returns FAIL if the option can be skipped or cannot be changed. Returns OK
+  * if it can be changed.
+  */
+     static int
+ validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg)
+ {
+     // 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))
+       return FAIL;
+ 
+     // Skip all options that are window-local (used for :vimgrep).
+     if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
+           && options[opt_idx].var == VAR_WIN)
+       return FAIL;
+ 
+     // Disallow changing some options from modelines.
+     if (opt_flags & OPT_MODELINE)
+     {
+       if (flags & (P_SECURE | P_NO_ML))
+       {
+           *errmsg = e_not_allowed_in_modeline;
+           return FAIL;
+       }
+       if ((flags & P_MLE) && !p_mle)
+       {
+           *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
+           return FAIL;
+       }
+ #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))
+           return FAIL;
+ #endif
+     }
+ 
+ #ifdef HAVE_SANDBOX
+     // Disallow changing some options in the sandbox
+     if (sandbox != 0 && (flags & P_SECURE))
+     {
+       *errmsg = e_not_allowed_in_sandbox;
+       return FAIL;
+     }
+ #endif
+ 
+     return OK;
+ }
+ 
  /*
   * Part of do_set() for string options.
   * Returns FAIL on failure, do not process further options.
***************
*** 1637,1730 ****
  }
  
  /*
!  * 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];
  
--- 1815,2114 ----
  }
  
  /*
!  * Set a boolean option
   */
      static char *
! do_set_option_bool(
!     int               opt_idx,
      int               opt_flags,
!     set_prefix_T prefix,
!     long_u    flags,
!     char_u    *varp,
!     int               nextchar,
!     int               afterchar,
!     int               cp_val)
! 
  {
      varnumber_T       value;
  
!     if (nextchar == '=' || nextchar == ':')
!       return e_invalid_argument;
! 
!     /*
!      * ":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))
!           return e_trailing_characters;
!       if (prefix == PREFIX_INV)
!           value = *(int *)(varp) ^ 1;
!       else
!           value = prefix == PREFIX_NO ? 0 : 1;
      }
  
!     return set_bool_option(opt_idx, varp, (int)value, opt_flags);
! }
! 
! /*
!  * Set a numeric option
!  */
!     static char *
! do_set_option_numeric(
!     int               opt_idx,
!     int               opt_flags,
!     char_u    **argp,
!     int               nextchar,
!     set_op_T  op,
!     long_u    flags,
!     int               cp_val,
!     char_u    *varp,
!     char      *errbuf,
!     size_t    errbuflen)
! {
!     char_u            *arg = *argp;
!     varnumber_T               value;
!     int                       i;
!     char              *errmsg = NULL;
! 
!     /*
!      * 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);
! 
! skip:
!     *argp = arg;
!     return errmsg;
! }
! 
! /*
!  * Set a key code (t_xx) option
!  */
!     static char *
! do_set_option_keycode(char_u **argp, char_u *key_name, int nextchar)
! {
!     char_u    *arg = *argp;
!     char_u    *p;
! 
!     if (nextchar == '&')
!     {
!       if (add_termcap_entry(key_name, TRUE) == FAIL)
!           return 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);
! 
!     *argp = arg;
!     return NULL;
! }
! 
! /*
!  * Set an option to a new value.
!  */
!     static char *
! do_set_option_value(
!     int               opt_idx,
!     int               opt_flags,
!     char_u    **argp,
!     set_prefix_T prefix,
!     set_op_T  op,
!     long_u    flags,
!     char_u    *varp,
!     char_u    *key_name,
!     int               nextchar,
!     int               afterchar,
!     int               cp_val,
!     int               *stopopteval,
!     char      *errbuf,
!     size_t    errbuflen)
! {
!     int               value_checked = FALSE;
!     char      *errmsg = NULL;
!     char_u    *arg = *argp;
! 
!     if (flags & P_BOOL)
!     {
!       // boolean option
!       errmsg = do_set_option_bool(opt_idx, opt_flags, prefix, flags, varp,
!                                               nextchar, afterchar, cp_val);
!       if (errmsg != NULL)
!           goto skip;
!     }
!     else
!     {
!       // numeric or string option
!       if (vim_strchr((char_u *)"=:&<", nextchar) == NULL
!               || prefix != PREFIX_NONE)
!       {
!           errmsg = e_invalid_argument;
!           goto skip;
!       }
! 
!       if (flags & P_NUM)
!       {
!           // numeric option
!           errmsg = do_set_option_numeric(opt_idx, opt_flags, &arg, nextchar,
!                                               op, flags, cp_val, varp,
!                                               errbuf, errbuflen);
!           if (errmsg != NULL)
!               goto skip;
!       }
!       else if (opt_idx >= 0)
!       {
!           // string option
!           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
!           errmsg = do_set_option_keycode(&arg, key_name, nextchar);
!           if (errmsg != NULL)
!               goto skip;
!       }
      }
  
+     if (opt_idx >= 0)
+       did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked);
+ 
+ skip:
+     *argp = arg;
+     return errmsg;
+ }
+ 
+ /*
+  * 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)
+ {
+     int               opt_idx;
+     char_u    *arg;
+     set_prefix_T prefix;      // no prefix, "no" prefix or "inv" prefix
+     set_op_T  op;
+     long_u    flags;          // flags for current option
+     char_u    *varp;          // pointer to variable for current option
+     char_u    key_name[2];
+     int               nextchar;       // next non-white char after option name
+     int               afterchar;      // character just after option name
+     int               cp_val;
+     char      *errmsg = NULL;
+     int               key;
+     int               len;
+ 
+     prefix = get_option_prefix(argp);
+     arg = *argp;
+ 
+     // find end of name
+     key = 0;
+     if (parse_option_name(arg, &opt_idx, &len, &key) == FAIL)
+       return e_invalid_argument;
+ 
      // remember character after option name
      afterchar = arg[len];
  
***************
*** 1748,1772 ****
        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
--- 2132,2141 ----
        while (VIM_ISWHITE(arg[len]))
            ++len;
  
!     op = get_opt_op(arg + len);
!     if (op != OP_NONE)
!       len++;
! 
      nextchar = arg[len];
  
      if (opt_idx == -1 && key == 0)    // found a mismatch: skip
***************
*** 1810,1862 ****
        }
      }
  
!     // 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)
      {
--- 2179,2187 ----
        }
      }
  
!     // Make sure the option value can be changed.
!     if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg) == FAIL)
        goto skip;
  
      if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL)
      {
***************
*** 1888,1894 ****
       * allows only one '=' character per "set" command line. grrr. (jw)
       */
      if (nextchar == '?'
!           || (prefix == 1
                && vim_strchr((char_u *)"=:&<", nextchar) == NULL
                && !(flags & P_BOOL)))
      {
--- 2213,2219 ----
       * allows only one '=' character per "set" command line. grrr. (jw)
       */
      if (nextchar == '?'
!           || (prefix == PREFIX_NONE
                && vim_strchr((char_u *)"=:&<", nextchar) == NULL
                && !(flags & P_BOOL)))
      {
***************
*** 1939,2115 ****
      }
      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:
--- 2264,2273 ----
      }
      else
      {
!       errmsg = do_set_option_value(opt_idx, opt_flags, &arg, prefix, op,
!                                       flags, varp, key_name, nextchar,
!                                       afterchar, cp_val, stopopteval, errbuf,
!                                       errbuflen);
      }
  
  skip:
*** ../vim-9.0.1282/src/version.c       2023-02-05 14:47:41.271193236 +0000
--- src/version.c       2023-02-05 15:43:32.369677054 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1283,
  /**/

-- 
>From "know your smileys":
 :'-D   Laughing so much that they're crying

 /// 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/20230205160304.82AEC1C126A%40moolenaar.net.

Raspunde prin e-mail lui