diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
| commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
| tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/option.c | |
| parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
| parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
| download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip | |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/option.c')
| -rw-r--r-- | src/nvim/option.c | 3009 |
1 files changed, 1376 insertions, 1633 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c index 0acfc5c261..1e7cd62d22 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -21,37 +21,46 @@ #define IN_OPTION_C #include <assert.h> +#include <ctype.h> #include <inttypes.h> #include <limits.h> #include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include "nvim/arglist.h" +#include "auto/config.h" +#include "nvim/api/private/defs.h" #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/change.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" #include "nvim/cursor_shape.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" -#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/ex_cmds2.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" -#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" -#include "nvim/getchar.h" -#include "nvim/hardcopy.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/insexpand.h" #include "nvim/keycodes.h" +#include "nvim/locale.h" +#include "nvim/log.h" #include "nvim/macros.h" #include "nvim/mapping.h" #include "nvim/mbyte.h" @@ -64,60 +73,49 @@ #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/os/os.h" -#include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/popupmenu.h" +#include "nvim/pos.h" #include "nvim/regexp.h" +#include "nvim/runtime.h" #include "nvim/screen.h" +#include "nvim/search.h" +#include "nvim/sign_defs.h" #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/spellsuggest.h" #include "nvim/strings.h" -#include "nvim/syntax.h" +#include "nvim/tag.h" +#include "nvim/terminal.h" +#include "nvim/types.h" #include "nvim/ui.h" -#include "nvim/ui_compositor.h" #include "nvim/undo.h" #include "nvim/vim.h" #include "nvim/window.h" -#ifdef WIN32 +#ifdef MSWIN # include "nvim/os/pty_conpty_win.h" #endif #include "nvim/api/extmark.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/lua/executor.h" #include "nvim/os/input.h" #include "nvim/os/lang.h" -/* - * The options that are local to a window or buffer have "indir" set to one of - * these values. Special values: - * PV_NONE: global option. - * PV_WIN is added: window-local option - * PV_BUF is added: buffer-local option - * PV_BOTH is added: global option which also has a local value. - */ -#define PV_BOTH 0x1000 -#define PV_WIN 0x2000 -#define PV_BUF 0x4000 -#define PV_MASK 0x0fff -#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x)) -#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x)) -#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x)) - -// WV_ and BV_ values get typecasted to this for the "indir" field -typedef enum { - PV_NONE = 0, - PV_MAXVAL = 0xffff, // to avoid warnings for value out of range -} idopt_T; - -/* - * Options local to a window have a value local to a buffer and global to all - * buffers. Indicate this by setting "var" to VAR_WIN. - */ -#define VAR_WIN ((char_u *)-1) +static char e_unknown_option[] + = N_("E518: Unknown option"); +static char e_not_allowed_in_modeline[] + = N_("E520: Not allowed in a modeline"); +static char e_not_allowed_in_modeline_when_modelineexpr_is_off[] + = N_("E992: Not allowed in a modeline when 'modelineexpr' is off"); +static char e_key_code_not_set[] + = N_("E846: Key code not set"); +static char e_number_required_after_equal[] + = N_("E521: Number required after ="); +static char e_preview_window_already_exists[] + = N_("E590: A preview window already exists"); static char *p_term = NULL; static char *p_ttytype = NULL; @@ -134,29 +132,14 @@ static int p_et_nopaste; static long p_sts_nopaste; static long p_tw_nopaste; static long p_wm_nopaste; -static char_u *p_vsts_nopaste; - -typedef struct vimoption { - char *fullname; // full option name - char *shortname; // permissible abbreviation - uint32_t flags; // see below - char_u *var; // global option: pointer to variable; - // window-local option: VAR_WIN; - // buffer-local option: global value - idopt_T indir; // global option: PV_NONE; - // local option: indirect option index - char_u *def_val; // default values for variable (neovim!!) - LastSet last_set; // script in which the option was last set -} vimoption_T; - -/* - * options[] is initialized here. - * The order of the options MUST be alphabetic for ":set all" and findoption(). - * All option names MUST start with a lowercase letter (for findoption()). - * Exception: "t_" options are at the end. - * The options with a NULL variable are 'hidden': a set command for them is - * ignored and they are not printed. - */ +static char *p_vsts_nopaste; + +// options[] is initialized here. +// The order of the options MUST be alphabetic for ":set all" and findoption(). +// All option names MUST start with a lowercase letter (for findoption()). +// Exception: "t_" options are at the end. +// The options with a NULL variable are 'hidden': a set command for them is +// ignored and they are not printed. #ifdef INCLUDE_GENERATED_DECLARATIONS # include "options.generated.h" @@ -164,10 +147,24 @@ typedef struct vimoption { #define OPTION_COUNT ARRAY_SIZE(options) +typedef enum { + OP_NONE = 0, + OP_ADDING, ///< "opt+=arg" + OP_PREPENDING, ///< "opt^=arg" + OP_REMOVING, ///< "opt-=arg" +} set_op_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" #endif +void set_init_tablocal(void) +{ + // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal! + int ch_idx = findoption("cmdheight"); + p_ch = (long)options[ch_idx].def_val; +} + /// Initialize the options, first part. /// /// Called only once from main(), just after creating the first buffer. @@ -178,14 +175,10 @@ typedef struct vimoption { /// editor state initialized here. Do logging in set_init_2 or later. void set_init_1(bool clean_arg) { - int opt_idx; - langmap_init(); - /* - * Find default value for 'shell' option. - * Don't use it if it is empty. - */ + // Find default value for 'shell' option. + // Don't use it if it is empty. { const char *shell = os_getenv("SHELL"); if (shell != NULL) { @@ -200,10 +193,8 @@ void set_init_1(bool clean_arg) } } - /* - * Set the default for 'backupskip' to include environment variables for - * temp files. - */ + // Set the default for 'backupskip' to include environment variables for + // temp files. { #ifdef UNIX static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" }; @@ -211,7 +202,7 @@ void set_init_1(bool clean_arg) static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" }; #endif garray_T ga; - opt_idx = findoption("backupskip"); + int opt_idx = findoption("backupskip"); ga_init(&ga, 1, 100); for (size_t n = 0; n < ARRAY_SIZE(names); n++) { @@ -225,7 +216,7 @@ void set_init_1(bool clean_arg) p = "/tmp"; # endif mustfree = false; - } else + } else // NOLINT(readability/braces) #endif { p = vim_getenv(names[n]); @@ -237,7 +228,7 @@ void set_init_1(bool clean_arg) xstrlcpy(item, p, len); add_pathsep(item); xstrlcat(item, "*", len); - if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags) + if (find_dup_item(ga.ga_data, item, options[opt_idx].flags) == NULL) { ga_grow(&ga, (int)len); if (!GA_EMPTY(&ga)) { @@ -260,19 +251,14 @@ void set_init_1(bool clean_arg) } { - char_u *cdpath; - char_u *buf; - int i; - int j; - // Initialize the 'cdpath' option's default value. - cdpath = (char_u *)vim_getenv("CDPATH"); + char *cdpath = vim_getenv("CDPATH"); if (cdpath != NULL) { - buf = xmalloc(2 * STRLEN(cdpath) + 2); + char *buf = xmalloc(2 * strlen(cdpath) + 2); { buf[0] = ','; // start with ",", current dir first - j = 1; - for (i = 0; cdpath[i] != NUL; i++) { + int j = 1; + for (int i = 0; cdpath[i] != NUL; i++) { if (vim_ispathlistsep(cdpath[i])) { buf[j++] = ','; } else { @@ -283,7 +269,7 @@ void set_init_1(bool clean_arg) } } buf[j] = NUL; - opt_idx = findoption("cdpath"); + int opt_idx = findoption("cdpath"); if (opt_idx >= 0) { options[opt_idx].def_val = buf; options[opt_idx].flags |= P_DEF_ALLOCED; @@ -295,28 +281,6 @@ void set_init_1(bool clean_arg) } } -#if defined(MSWIN) || defined(MAC) - // Set print encoding on platforms that don't default to latin1 - set_string_default("printencoding", "hp-roman8", false); -#endif - - // 'printexpr' must be allocated to be able to evaluate it. - set_string_default("printexpr", -#ifdef UNIX - "system(['lpr'] " - "+ (empty(&printdevice)?[]:['-P', &printdevice]) " - "+ [v:fname_in])" - ". delete(v:fname_in)" - "+ v:shell_error", -#elif defined(MSWIN) - "system(['copy', v:fname_in, " - "empty(&printdevice)?'LPT1':&printdevice])" - ". delete(v:fname_in)", -#else - "", -#endif - false); - char *backupdir = stdpaths_user_state_subpath("backup", 2, true); const size_t backupdir_len = strlen(backupdir); backupdir = xrealloc(backupdir, backupdir_len + 3); @@ -339,10 +303,8 @@ void set_init_1(bool clean_arg) rtp = NULL; // ownership taken } - /* - * Set all the options (except the terminal options) to their default - * value. Also set the global value for local options. - */ + // Set all the options (except the terminal options) to their default + // value. Also set the global value for local options. set_options_default(0); curbuf->b_p_initialized = true; @@ -365,34 +327,32 @@ void set_init_1(bool clean_arg) // didset_options() because it only depends on 'encoding'. init_spell_chartab(); - /* - * Expand environment variables and things like "~" for the defaults. - * If option_expand() returns non-NULL the variable is expanded. This can - * only happen for non-indirect options. - * Also set the default to the expanded value, so ":set" does not list - * them. - * Don't set the P_ALLOCED flag, because we don't want to free the - * default. - */ - for (opt_idx = 0; options[opt_idx].fullname; opt_idx++) { - if (options[opt_idx].flags & P_NO_DEF_EXP) { + // Expand environment variables and things like "~" for the defaults. + // If option_expand() returns non-NULL the variable is expanded. This can + // only happen for non-indirect options. + // Also set the default to the expanded value, so ":set" does not list + // them. + // Don't set the P_ALLOCED flag, because we don't want to free the + // default. + for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { + vimoption_T *opt = &options[opt_idx]; + if (opt->flags & P_NO_DEF_EXP) { continue; } char *p; - if ((options[opt_idx].flags & P_GETTEXT) - && options[opt_idx].var != NULL) { - p = _(*(char **)options[opt_idx].var); + if ((opt->flags & P_GETTEXT) && opt->var != NULL) { + p = _(*(char **)opt->var); } else { - p = (char *)option_expand(opt_idx, NULL); + p = option_expand(opt_idx, NULL); } if (p != NULL) { p = xstrdup(p); - *(char **)options[opt_idx].var = p; - if (options[opt_idx].flags & P_DEF_ALLOCED) { - xfree(options[opt_idx].def_val); + *(char **)opt->var = p; + if (opt->flags & P_DEF_ALLOCED) { + xfree(opt->def_val); } - options[opt_idx].def_val = (char_u *)p; - options[opt_idx].flags |= P_DEF_ALLOCED; + opt->def_val = p; + opt->flags |= P_DEF_ALLOCED; } } @@ -404,7 +364,7 @@ void set_init_1(bool clean_arg) // NOTE: mlterm's author is being asked to 'set' a variable // instead of an environment variable due to inheritance. if (os_env_exists("MLTERM")) { - set_option_value("tbidi", 1L, NULL, 0); + set_option_value_give_err("tbidi", 1L, NULL, 0); } didset_options2(); @@ -414,17 +374,17 @@ void set_init_1(bool clean_arg) // enc_locale() will try to find the encoding of the current locale. // This will be used when 'default' is used as encoding specifier // in 'fileencodings' - char_u *p = enc_locale(); + char *p = enc_locale(); if (p == NULL) { // use utf-8 as 'default' if locale encoding can't be detected. - p = (char_u *)xmemdupz(S_LEN("utf-8")); + p = xmemdupz(S_LEN("utf-8")); } fenc_default = p; #ifdef HAVE_WORKING_LIBINTL // GNU gettext 0.10.37 supports this feature: set the codeset used for // translated messages independently from the current locale. - (void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc); + (void)bind_textdomain_codeset(PROJECT_NAME, p_enc); #endif // Set the default for 'helplang'. @@ -435,32 +395,32 @@ void set_init_1(bool clean_arg) /// This does not take care of side effects! /// /// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL -static void set_option_default(int opt_idx, int opt_flags) +static void set_option_default(const int opt_idx, int opt_flags) { - char_u *varp; // pointer to variable for current option int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags); - uint32_t flags = options[opt_idx].flags; + // pointer to variable for current option + vimoption_T *opt = &options[opt_idx]; + char_u *varp = (char_u *)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); + uint32_t flags = opt->flags; if (varp != NULL) { // skip hidden option, nothing to do for it if (flags & P_STRING) { // Use set_string_option_direct() for local options to handle // freeing and allocating the value. - if (options[opt_idx].indir != PV_NONE) { - set_string_option_direct(NULL, opt_idx, - (char *)options[opt_idx].def_val, opt_flags, 0); + if (opt->indir != PV_NONE) { + set_string_option_direct(NULL, opt_idx, opt->def_val, opt_flags, 0); } else { if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) { - free_string_option(*(char_u **)(varp)); + free_string_option(*(char **)(varp)); } - *(char_u **)varp = options[opt_idx].def_val; - options[opt_idx].flags &= ~P_ALLOCED; + *(char **)varp = opt->def_val; + opt->flags &= ~P_ALLOCED; } } else if (flags & P_NUM) { - if (options[opt_idx].indir == PV_SCROLL) { + if (opt->indir == PV_SCROLL) { win_comp_scroll(curwin); } else { - long def_val = (long)options[opt_idx].def_val; + long def_val = (long)opt->def_val; if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { // 'scrolloff' and 'sidescrolloff' local values have a @@ -471,21 +431,21 @@ static void set_option_default(int opt_idx, int opt_flags) } // May also set global value for local option. if (both) { - *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = + *(long *)get_varp_scope(opt, OPT_GLOBAL) = def_val; } } } else { // P_BOOL - *(int *)varp = (int)(intptr_t)options[opt_idx].def_val; + *(int *)varp = (int)(intptr_t)opt->def_val; #ifdef UNIX // 'modeline' defaults to off for root - if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) { + if (opt->indir == PV_ML && getuid() == ROOT_UID) { *(int *)varp = false; } #endif // May also set global value for local option. if (both) { - *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = + *(int *)get_varp_scope(opt, OPT_GLOBAL) = *(int *)varp; } } @@ -528,32 +488,31 @@ static void set_string_default(const char *name, char *val, bool allocated) { int opt_idx = findoption(name); if (opt_idx >= 0) { - if (options[opt_idx].flags & P_DEF_ALLOCED) { - xfree(options[opt_idx].def_val); + vimoption_T *opt = &options[opt_idx]; + if (opt->flags & P_DEF_ALLOCED) { + xfree(opt->def_val); } - options[opt_idx].def_val = allocated - ? (char_u *)val - : (char_u *)xstrdup(val); - options[opt_idx].flags |= P_DEF_ALLOCED; + opt->def_val = allocated ? val : xstrdup(val); + opt->flags |= P_DEF_ALLOCED; } } -// For an option value that contains comma separated items, find "newval" in -// "origval". Return NULL if not found. -static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t flags) +/// For an option value that contains comma separated items, find "newval" in +/// "origval". Return NULL if not found. +static char *find_dup_item(char *origval, const char *newval, uint32_t flags) FUNC_ATTR_NONNULL_ARG(2) { - int bs = 0; - if (origval == NULL) { return NULL; } - const size_t newlen = STRLEN(newval); - for (char_u *s = origval; *s != NUL; s++) { + int bs = 0; + + const size_t newlen = strlen(newval); + for (char *s = origval; *s != NUL; s++) { if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1))) - && STRNCMP(s, newval, newlen) == 0 + && strncmp(s, newval, newlen) == 0 && (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) { return s; } @@ -574,11 +533,9 @@ static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t fla /// Used for 'lines' and 'columns'. void set_number_default(char *name, long val) { - int opt_idx; - - opt_idx = findoption(name); + int opt_idx = findoption(name); if (opt_idx >= 0) { - options[opt_idx].def_val = (char_u *)(intptr_t)val; + options[opt_idx].def_val = (char *)(intptr_t)val; } } @@ -590,17 +547,18 @@ void free_all_options(void) if (options[i].indir == PV_NONE) { // global option: free value and default value. if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) { - free_string_option(*(char_u **)options[i].var); + free_string_option(*(char **)options[i].var); } if (options[i].flags & P_DEF_ALLOCED) { free_string_option(options[i].def_val); } } else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) { // buffer-local option: free global value - clear_string_option((char_u **)options[i].var); + clear_string_option((char **)options[i].var); } } free_operatorfunc_option(); + free_tagfunc_option(); } #endif @@ -610,25 +568,20 @@ void set_init_2(bool headless) // set in set_init_1 but logging is not allowed there ILOG("startup runtimepath/packpath value: %s", p_rtp); - int idx; - // 'scroll' defaults to half the window height. The stored default is zero, // which results in the actual value computed from the window height. - idx = findoption("scroll"); + int idx = findoption("scroll"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL); } comp_col(); - /* - * 'window' is only for backwards compatibility with Vi. - * Default is Rows - 1. - */ + // 'window' is only for backwards compatibility with Vi. + // Default is Rows - 1. if (!option_was_set("window")) { p_window = Rows - 1; } set_number_default("window", Rows - 1); - (void)parse_printoptions(); // parse 'printoptions' default value } /// Initialize the options, part three: After reading the .vimrc @@ -649,41 +602,41 @@ void set_init_3(void) : !(options[idx_sp].flags & P_WAS_SET); size_t len = 0; - char_u *p = (char_u *)invocation_path_tail(p_sh, &len); - p = vim_strnsave(p, len); + char *p = (char *)invocation_path_tail(p_sh, &len); + p = xstrnsave(p, len); { // // Default for p_sp is "| tee", for p_srr is ">". // For known shells it is changed here to include stderr. // - if (FNAMECMP(p, "csh") == 0 - || FNAMECMP(p, "tcsh") == 0) { + if (path_fnamecmp(p, "csh") == 0 + || path_fnamecmp(p, "tcsh") == 0) { if (do_sp) { - p_sp = (char_u *)"|& tee"; + p_sp = "|& tee"; options[idx_sp].def_val = p_sp; } if (do_srr) { - p_srr = (char_u *)">&"; + p_srr = ">&"; options[idx_srr].def_val = p_srr; } - } else if (FNAMECMP(p, "sh") == 0 - || FNAMECMP(p, "ksh") == 0 - || FNAMECMP(p, "mksh") == 0 - || FNAMECMP(p, "pdksh") == 0 - || FNAMECMP(p, "zsh") == 0 - || FNAMECMP(p, "zsh-beta") == 0 - || FNAMECMP(p, "bash") == 0 - || FNAMECMP(p, "fish") == 0 - || FNAMECMP(p, "ash") == 0 - || FNAMECMP(p, "dash") == 0) { + } else if (path_fnamecmp(p, "sh") == 0 + || path_fnamecmp(p, "ksh") == 0 + || path_fnamecmp(p, "mksh") == 0 + || path_fnamecmp(p, "pdksh") == 0 + || path_fnamecmp(p, "zsh") == 0 + || path_fnamecmp(p, "zsh-beta") == 0 + || path_fnamecmp(p, "bash") == 0 + || path_fnamecmp(p, "fish") == 0 + || path_fnamecmp(p, "ash") == 0 + || path_fnamecmp(p, "dash") == 0) { // Always use POSIX shell style redirection if we reach this if (do_sp) { - p_sp = (char_u *)"2>&1| tee"; + p_sp = "2>&1| tee"; options[idx_sp].def_val = p_sp; } if (do_srr) { - p_srr = (char_u *)">%s 2>&1"; + p_srr = ">%s 2>&1"; options[idx_srr].def_val = p_srr; } } @@ -715,23 +668,25 @@ void set_helplang_default(const char *lang) return; } int idx = findoption("hlg"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { - if (options[idx].flags & P_ALLOCED) { - free_string_option(p_hlg); - } - p_hlg = (char_u *)xmemdupz(lang, lang_len); - // zh_CN becomes "cn", zh_TW becomes "tw". - if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { - p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); - p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); - } else if (STRLEN(p_hlg) >= 1 && *p_hlg == 'C') { - // any C like setting, such as C.UTF-8, becomes "en" - p_hlg[0] = 'e'; - p_hlg[1] = 'n'; - } - p_hlg[2] = NUL; - options[idx].flags |= P_ALLOCED; + if (idx < 0 || (options[idx].flags & P_WAS_SET)) { + return; } + + if (options[idx].flags & P_ALLOCED) { + free_string_option(p_hlg); + } + p_hlg = xmemdupz(lang, lang_len); + // zh_CN becomes "cn", zh_TW becomes "tw". + if (STRNICMP(p_hlg, "zh_", 3) == 0 && strlen(p_hlg) >= 5) { + p_hlg[0] = (char)TOLOWER_ASC(p_hlg[3]); + p_hlg[1] = (char)TOLOWER_ASC(p_hlg[4]); + } else if (strlen(p_hlg) >= 1 && *p_hlg == 'C') { + // any C like setting, such as C.UTF-8, becomes "en" + p_hlg[0] = 'e'; + p_hlg[1] = 'n'; + } + p_hlg[2] = NUL; + options[idx].flags |= P_ALLOCED; } /// 'title' and 'icon' only default to true if they have not been set or reset @@ -741,21 +696,17 @@ void set_helplang_default(const char *lang) /// machine. void set_title_defaults(void) { - int idx1; - - /* - * If GUI is (going to be) used, we can always set the window title and - * icon name. Saves a bit of time, because the X11 display server does - * not need to be contacted. - */ - idx1 = findoption("title"); + // If GUI is (going to be) used, we can always set the window title and + // icon name. Saves a bit of time, because the X11 display server does + // not need to be contacted. + int idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val = (char_u *)(intptr_t)0; + options[idx1].def_val = 0; p_title = 0; } idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val = (char_u *)(intptr_t)0; + options[idx1].def_val = 0; p_icon = 0; } } @@ -775,6 +726,342 @@ void ex_set(exarg_T *eap) (void)do_set(eap->arg, flags); } +/// Part of do_set() for string options. +/// @return FAIL on failure, do not process further options. +static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, set_op_T op_arg, + uint32_t flags, char *varp_arg, char *errbuf, size_t errbuflen, + int *value_checked, char **errmsg) +{ + char *arg = *argp; + set_op_T op = op_arg; + char *varp = varp_arg; + char *save_arg = NULL; + char *s = NULL; + char_u *origval_l = NULL; + char_u *origval_g = NULL; + char whichwrap[80]; + + // When using ":set opt=val" for a global option + // with a local value the local value will be + // reset, use the global value here. + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 + && ((int)options[opt_idx].indir & PV_BOTH)) { + varp = (char *)options[opt_idx].var; + } + + // The old value is kept until we are sure that the new value is valid. + char *oldval = *(char **)varp; + + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + + // A global-local string option might have an empty option as value to + // indicate that the global value should be used. + if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == (char_u *)empty_option) { + origval_l = origval_g; + } + } + + char *origval; + // When setting the local value of a global option, the old value may be + // the global value. + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) { + origval = *(char **)get_varp(&options[opt_idx]); + } else { + origval = oldval; + } + + char *newval; + if (nextchar == '&') { // set to default val + newval = options[opt_idx].def_val; + // expand environment variables and ~ since the default value was + // already expanded, only required when an environment variable was set + // later + if (newval == NULL) { + newval = empty_option; + } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) { + s = option_expand(opt_idx, newval); + if (s == NULL) { + s = newval; + } + newval = xstrdup(s); + } else { + newval = xstrdup(newval); + } + } else if (nextchar == '<') { // set to global val + newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL)); + } else { + arg++; // jump to after the '=' or ':' + + // Set 'keywordprg' to ":help" if an empty + // value was passed to :set by the user. + if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) { + save_arg = arg; + arg = ":help"; + } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) { + // Convert 'backspace' number to string, for + // adding, prepending and removing string. + int i = getdigits_int((char **)varp, true, 0); + switch (i) { + case 0: + *(char **)varp = empty_option; + break; + case 1: + *(char_u **)varp = (char_u *)xstrdup("indent,eol"); + break; + case 2: + *(char_u **)varp = (char_u *)xstrdup("indent,eol,start"); + break; + case 3: + *(char_u **)varp = (char_u *)xstrdup("indent,eol,nostop"); + break; + } + xfree(oldval); + if (origval == oldval) { + origval = *(char **)varp; + } + if (origval_l == (char_u *)oldval) { + origval_l = *(char_u **)varp; + } + if (origval_g == (char_u *)oldval) { + origval_g = *(char_u **)varp; + } + oldval = *(char **)varp; + } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) { + // Convert 'whichwrap' number to string, for backwards compatibility + // with Vim 3.0. + *whichwrap = NUL; + int i = getdigits_int(&arg, true, 0); + if (i & 1) { + xstrlcat(whichwrap, "b,", sizeof(whichwrap)); + } + if (i & 2) { + xstrlcat(whichwrap, "s,", sizeof(whichwrap)); + } + if (i & 4) { + xstrlcat(whichwrap, "h,l,", sizeof(whichwrap)); + } + if (i & 8) { + xstrlcat(whichwrap, "<,>,", sizeof(whichwrap)); + } + if (i & 16) { + xstrlcat(whichwrap, "[,],", sizeof(whichwrap)); + } + if (*whichwrap != NUL) { // remove trailing , + whichwrap[strlen(whichwrap) - 1] = NUL; + } + save_arg = arg; + arg = whichwrap; + } else if (*arg == '>' && (varp == (char *)&p_dir || varp == (char *)&p_bdir)) { + // Remove '>' before 'dir' and 'bdir', for backwards compatibility with + // version 3.0 + arg++; + } + + // Copy the new string into allocated memory. + // Can't use set_string_option_direct(), because we need to remove the + // backslashes. + + // get a bit too much + size_t newlen = strlen(arg) + 1; + if (op != OP_NONE) { + newlen += strlen(origval) + 1; + } + newval = xmalloc(newlen); + s = newval; + + // Copy the string, skip over escaped chars. + // For MS-Windows backslashes before normal file name characters + // are not removed, and keep backslash at start, for "\\machine\path", + // but do remove it for "\\\\machine\\path". + // The reverse is found in ExpandOldSetting(). + while (*arg != NUL && !ascii_iswhite(*arg)) { + if (*arg == '\\' && arg[1] != NUL +#ifdef BACKSLASH_IN_FILENAME + && !((flags & P_EXPAND) + && vim_isfilec((uint8_t)arg[1]) + && !ascii_iswhite(arg[1]) + && (arg[1] != '\\' + || (s == newval && arg[2] != '\\'))) +#endif + ) { + arg++; // remove backslash + } + int i = utfc_ptr2len(arg); + if (i > 1) { + // copy multibyte char + memmove(s, arg, (size_t)i); + arg += i; + s += i; + } else { + *s++ = *arg++; + } + } + *s = NUL; + + // Expand environment variables and ~. + // Don't do it when adding without inserting a comma. + if (op == OP_NONE || (flags & P_COMMA)) { + s = option_expand(opt_idx, newval); + if (s != NULL) { + xfree(newval); + newlen = (unsigned)strlen(s) + 1; + if (op != OP_NONE) { + newlen += (unsigned)strlen(origval) + 1; + } + newval = xmalloc(newlen); + STRCPY(newval, s); + } + } + + // locate newval[] in origval[] when removing it + // and when adding to avoid duplicates + int len = 0; + if (op == OP_REMOVING || (flags & P_NODUP)) { + len = (int)strlen(newval); + s = find_dup_item(origval, newval, flags); + + // do not add if already there + if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) { + op = OP_NONE; + STRCPY(newval, origval); + } + + // if no duplicate, move pointer to end of original value + if (s == NULL) { + s = origval + (int)strlen(origval); + } + } + + // concatenate the two strings; add a ',' if needed + if (op == OP_ADDING || op == OP_PREPENDING) { + int comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL); + if (op == OP_ADDING) { + len = (int)strlen(origval); + // Strip a trailing comma, would get 2. + if (comma && len > 1 + && (flags & P_ONECOMMA) == P_ONECOMMA + && origval[len - 1] == ',' + && origval[len - 2] != '\\') { + len--; + } + memmove(newval + len + comma, newval, strlen(newval) + 1); + memmove(newval, origval, (size_t)len); + } else { + len = (int)strlen(newval); + STRMOVE(newval + len + comma, origval); + } + if (comma) { + newval[len] = ','; + } + } + + // Remove newval[] from origval[]. (Note: "len" has been set above and + // is used here). + if (op == OP_REMOVING) { + STRCPY(newval, origval); + if (*s) { + // may need to remove a comma + if (flags & P_COMMA) { + if (s == origval) { + // include comma after string + if (s[len] == ',') { + len++; + } + } else { + // include comma before string + s--; + len++; + } + } + STRMOVE(newval + (s - origval), s + len); + } + } + + if (flags & P_FLAGLIST) { + // Remove flags that appear twice. + for (s = newval; *s;) { + // if options have P_FLAGLIST and P_ONECOMMA such as + // 'whichwrap' + if (flags & P_ONECOMMA) { + if (*s != ',' && *(s + 1) == ',' + && vim_strchr(s + 2, (uint8_t)(*s)) != NULL) { + // Remove the duplicated value and the next comma. + STRMOVE(s, s + 2); + continue; + } + } else { + if ((!(flags & P_COMMA) || *s != ',') + && vim_strchr(s + 1, (uint8_t)(*s)) != NULL) { + STRMOVE(s, s + 1); + continue; + } + } + s++; + } + } + + if (save_arg != NULL) { + arg = save_arg; // arg was temporarily changed, restore it + } + } + + // Set the new value. + *(char_u **)(varp) = (char_u *)newval; + + // origval may be freed by did_set_string_option(), make a copy. + char *saved_origval = (origval != NULL) ? xstrdup(origval) : NULL; + char *saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : NULL; + cha |