]> granicus.if.org Git - vim/commitdiff
patch 9.0.1275: the code for setting options is too complicated v9.0.1275
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 2 Feb 2023 16:34:11 +0000 (16:34 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 2 Feb 2023 16:34:11 +0000 (16:34 +0000)
Problem:    The code for setting options is too complicated.
Solution:   Refactor the do_set() function. (Yegappan Lakshmanan, Lewis
            Russell, closes #11932)

src/option.c
src/optionstr.c
src/version.c

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