}
}
+/*
+ * :set operator types
+ */
typedef enum {
OP_NONE = 0,
OP_ADDING, // "opt+=arg"
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.
return *errmsg == NULL ? OK : FAIL;
}
+/*
+ * 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.
- * 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(
+do_set_option_value(
+ int opt_idx,
int opt_flags,
char_u **argp,
- char_u *arg_start,
- char_u **startarg,
- int *did_show,
+ 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;
- 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)
+ if (flags & P_BOOL)
{
- prefix = 0;
- arg += 2;
+ // boolean option
+ errmsg = do_set_option_bool(opt_idx, opt_flags, prefix, flags, varp,
+ nextchar, afterchar, cp_val);
+ if (errmsg != NULL)
+ goto skip;
}
- else if (STRNCMP(arg, "inv", 3) == 0)
+ else
{
- prefix = 2;
- arg += 3;
- }
+ // numeric or string option
+ if (vim_strchr((char_u *)"=:&<", nextchar) == NULL
+ || prefix != PREFIX_NONE)
+ {
+ errmsg = e_invalid_argument;
+ goto skip;
+ }
- // 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
+ if (flags & P_NUM)
{
- len = 1;
- while (arg[len] != NUL && arg[len] != '>')
- ++len;
+ // 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;
}
- if (arg[len] != '>')
+ else if (opt_idx >= 0)
{
- errmsg = e_invalid_argument;
- goto skip;
+ // 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;
+ }
}
- 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);
+ {
+ // 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];
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;
- }
- }
+ op = get_opt_op(arg + len);
+ if (op != OP_NONE)
+ len++;
+
nextchar = arg[len];
if (opt_idx == -1 && key == 0) // found a mismatch: skip
}
}
- // 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)
+ // Make sure the option value can be changed.
+ if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg) == FAIL)
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;
* allows only one '=' character per "set" command line. grrr. (jw)
*/
if (nextchar == '?'
- || (prefix == 1
+ || (prefix == PREFIX_NONE
&& vim_strchr((char_u *)"=:&<", nextchar) == NULL
&& !(flags & P_BOOL)))
{
}
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);
+ errmsg = do_set_option_value(opt_idx, opt_flags, &arg, prefix, op,
+ flags, varp, key_name, nextchar,
+ afterchar, cp_val, stopopteval, errbuf,
+ errbuflen);
}
skip: