neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

option.c (196442B)


      1 // User-settable options. Checklist for adding a new option:
      2 // - Put it in options.lua
      3 // - For a global option: Add a variable for it in option_vars.h.
      4 // - For a buffer or window local option:
      5 //   - Add a variable to the window or buffer struct in buffer_defs.h.
      6 //   - For a window option, add some code to copy_winopt().
      7 //   - For a window string option, add code to check_winopt()
      8 //     and clear_winopt(). If setting the option needs parsing,
      9 //     add some code to didset_window_options().
     10 //   - For a buffer option, add some code to buf_copy_options().
     11 //   - For a buffer string option, add code to check_buf_options().
     12 // - If it's a numeric option, add any necessary bounds checks to check_num_option_bounds().
     13 // - If it's a list of flags, add some code in do_set(), search for WW_ALL.
     14 // - Add documentation! "desc" in options.lua, and any other related places.
     15 // - Add an entry in runtime/scripts/optwin.lua.
     16 
     17 #define IN_OPTION_C
     18 #include <assert.h>
     19 #include <inttypes.h>
     20 #include <limits.h>
     21 #include <stdbool.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <uv.h>
     26 
     27 #include "auto/config.h"
     28 #include "klib/kvec.h"
     29 #include "nvim/api/extmark.h"
     30 #include "nvim/api/private/defs.h"
     31 #include "nvim/api/private/helpers.h"
     32 #include "nvim/api/private/validate.h"
     33 #include "nvim/ascii_defs.h"
     34 #include "nvim/assert_defs.h"
     35 #include "nvim/autocmd.h"
     36 #include "nvim/autocmd_defs.h"
     37 #include "nvim/buffer.h"
     38 #include "nvim/buffer_defs.h"
     39 #include "nvim/change.h"
     40 #include "nvim/charset.h"
     41 #include "nvim/cmdexpand.h"
     42 #include "nvim/cmdexpand_defs.h"
     43 #include "nvim/cursor_shape.h"
     44 #include "nvim/decoration_defs.h"
     45 #include "nvim/decoration_provider.h"
     46 #include "nvim/diff.h"
     47 #include "nvim/drawscreen.h"
     48 #include "nvim/errors.h"
     49 #include "nvim/eval.h"
     50 #include "nvim/eval/typval.h"
     51 #include "nvim/eval/typval_defs.h"
     52 #include "nvim/eval/vars.h"
     53 #include "nvim/eval/window.h"
     54 #include "nvim/ex_cmds_defs.h"
     55 #include "nvim/ex_docmd.h"
     56 #include "nvim/ex_getln.h"
     57 #include "nvim/ex_session.h"
     58 #include "nvim/fold.h"
     59 #include "nvim/fuzzy.h"
     60 #include "nvim/garray.h"
     61 #include "nvim/garray_defs.h"
     62 #include "nvim/gettext_defs.h"
     63 #include "nvim/globals.h"
     64 #include "nvim/grid_defs.h"
     65 #include "nvim/highlight.h"
     66 #include "nvim/highlight_defs.h"
     67 #include "nvim/highlight_group.h"
     68 #include "nvim/indent.h"
     69 #include "nvim/indent_c.h"
     70 #include "nvim/insexpand.h"
     71 #include "nvim/keycodes.h"
     72 #include "nvim/log.h"
     73 #include "nvim/lua/executor.h"
     74 #include "nvim/macros_defs.h"
     75 #include "nvim/mapping.h"
     76 #include "nvim/math.h"
     77 #include "nvim/mbyte.h"
     78 #include "nvim/memfile.h"
     79 #include "nvim/memline.h"
     80 #include "nvim/memory.h"
     81 #include "nvim/memory_defs.h"
     82 #include "nvim/message.h"
     83 #include "nvim/mouse.h"
     84 #include "nvim/move.h"
     85 #include "nvim/normal.h"
     86 #include "nvim/ops.h"
     87 #include "nvim/option.h"
     88 #include "nvim/option_defs.h"
     89 #include "nvim/option_vars.h"
     90 #include "nvim/optionstr.h"
     91 #include "nvim/os/input.h"
     92 #include "nvim/os/lang.h"
     93 #include "nvim/os/os.h"
     94 #include "nvim/os/os_defs.h"
     95 #include "nvim/path.h"
     96 #include "nvim/popupmenu.h"
     97 #include "nvim/pos_defs.h"
     98 #include "nvim/regexp.h"
     99 #include "nvim/regexp_defs.h"
    100 #include "nvim/runtime.h"
    101 #include "nvim/spell.h"
    102 #include "nvim/spellfile.h"
    103 #include "nvim/spellsuggest.h"
    104 #include "nvim/state_defs.h"
    105 #include "nvim/strings.h"
    106 #include "nvim/tag.h"
    107 #include "nvim/terminal.h"
    108 #include "nvim/types_defs.h"
    109 #include "nvim/ui.h"
    110 #include "nvim/undo.h"
    111 #include "nvim/undo_defs.h"
    112 #include "nvim/vim_defs.h"
    113 #include "nvim/window.h"
    114 #include "nvim/winfloat.h"
    115 
    116 #ifdef BACKSLASH_IN_FILENAME
    117 # include "nvim/arglist.h"
    118 #endif
    119 
    120 static const char e_unknown_option[]
    121  = N_("E518: Unknown option");
    122 static const char e_not_allowed_in_modeline[]
    123  = N_("E520: Not allowed in a modeline");
    124 static const char e_not_allowed_in_modeline_when_modelineexpr_is_off[]
    125  = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
    126 static const char e_number_required_after_equal[]
    127  = N_("E521: Number required after =");
    128 static const char e_preview_window_already_exists[]
    129  = N_("E590: A preview window already exists");
    130 static const char e_cannot_have_negative_or_zero_number_of_quickfix[]
    131  = N_("E1542: Cannot have a negative or zero number of quickfix/location lists");
    132 static const char e_cannot_have_more_than_hundred_quickfix[]
    133  = N_("E1543: Cannot have more than a hundred quickfix/location lists");
    134 
    135 static char *p_term = NULL;
    136 static char *p_ttytype = NULL;
    137 
    138 // Saved values for when 'bin' is set.
    139 static int p_et_nobin;
    140 static int p_ml_nobin;
    141 static OptInt p_tw_nobin;
    142 static OptInt p_wm_nobin;
    143 
    144 // Saved values for when 'paste' is set.
    145 static int p_ai_nopaste;
    146 static int p_et_nopaste;
    147 static OptInt p_sts_nopaste;
    148 static OptInt p_tw_nopaste;
    149 static OptInt p_wm_nopaste;
    150 static char *p_vsts_nopaste;
    151 
    152 #define OPTION_COUNT ARRAY_SIZE(options)
    153 
    154 /// :set boolean option prefix
    155 typedef enum {
    156  PREFIX_NO = 0,  ///< "no" prefix
    157  PREFIX_NONE,    ///< no prefix
    158  PREFIX_INV,     ///< "inv" prefix
    159 } set_prefix_T;
    160 
    161 #include "option.c.generated.h"
    162 
    163 // options[] is initialized in options.generated.h.
    164 // The options with a NULL variable are 'hidden': a set command for them is
    165 // ignored and they are not printed.
    166 
    167 #include "options.generated.h"
    168 #include "options_map.generated.h"
    169 
    170 static int p_bin_dep_opts[] = {
    171  kOptTextwidth, kOptWrapmargin, kOptModeline, kOptExpandtab, kOptInvalid
    172 };
    173 
    174 static int p_paste_dep_opts[] = {
    175  kOptAutoindent, kOptExpandtab, kOptRuler, kOptShowmatch, kOptSmarttab, kOptSofttabstop,
    176  kOptTextwidth, kOptWrapmargin, kOptRevins, kOptVarsofttabstop, kOptInvalid
    177 };
    178 
    179 void set_init_tablocal(void)
    180 {
    181  // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal!
    182  p_ch = options[kOptCmdheight].def_val.data.number;
    183 }
    184 
    185 /// Initialize the 'shell' option to a default value.
    186 static void set_init_default_shell(void)
    187 {
    188  // Find default value for 'shell' option.
    189  // Don't use it if it is empty.
    190  char *shell = os_getenv("SHELL");
    191  if (shell != NULL) {
    192    if (vim_strchr(shell, ' ') != NULL) {
    193      const size_t len = strlen(shell) + 3;  // two quotes and a trailing NUL
    194      char *const cmd = xmalloc(len);
    195      snprintf(cmd, len, "\"%s\"", shell);
    196      set_string_default(kOptShell, cmd, true);
    197    } else {
    198      set_string_default(kOptShell, shell, false);
    199    }
    200    xfree(shell);
    201  }
    202 }
    203 
    204 /// Set the default for 'backupskip' to include environment variables for
    205 /// temp files.
    206 static void set_init_default_backupskip(void)
    207 {
    208 #ifdef UNIX
    209  static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" };
    210 #else
    211  static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" };
    212 #endif
    213  garray_T ga;
    214  OptIndex opt_idx = kOptBackupskip;
    215 
    216  ga_init(&ga, 1, 100);
    217  for (size_t i = 0; i < ARRAY_SIZE(names); i++) {
    218    bool mustfree = true;
    219    char *p;
    220    size_t plen;
    221 #ifdef UNIX
    222    if (*names[i] == NUL) {
    223 # ifdef __APPLE__
    224      p = "/private/tmp";
    225      plen = STRLEN_LITERAL("/private/tmp");
    226 # else
    227      p = "/tmp";
    228      plen = STRLEN_LITERAL("/tmp");
    229 # endif
    230      mustfree = false;
    231    } else
    232 #endif
    233    {
    234      p = vim_getenv(names[i]);
    235      plen = 0;  // will be calculated below
    236    }
    237    if (p != NULL && *p != NUL) {
    238      bool has_trailing_path_sep = false;
    239 
    240      if (plen == 0) {
    241        // the value was retrieved from the environment
    242        plen = strlen(p);
    243        // does the value include a trailing path separator?
    244        if (after_pathsep(p, p + plen)) {
    245          has_trailing_path_sep = true;
    246        }
    247      }
    248 
    249      // item size needs to be large enough to include "/*" and a trailing NUL
    250      // note: the value (and therefore plen) may already include a path separator
    251      size_t itemsize = plen + (has_trailing_path_sep ? 0 : 1) + 2;
    252      char *item = xmalloc(itemsize);
    253      // add a preceding comma as a separator after the first item
    254      size_t itemseplen = (ga.ga_len == 0) ? 0 : 1;
    255 
    256      size_t itemlen = (size_t)vim_snprintf(item, itemsize, "%s%s*", p,
    257                                            has_trailing_path_sep ? "" : PATHSEPSTR);
    258 
    259      if (find_dup_item(ga.ga_data, item, itemlen, options[opt_idx].flags) == NULL) {
    260        ga_grow(&ga, (int)(itemseplen + itemlen + 1));
    261        ga.ga_len += vim_snprintf((char *)ga.ga_data + ga.ga_len,
    262                                  itemseplen + itemlen + 1,
    263                                  "%s%s", (itemseplen > 0) ? "," : "", item);
    264      }
    265      xfree(item);
    266    }
    267    if (mustfree) {
    268      xfree(p);
    269    }
    270  }
    271  if (ga.ga_data != NULL) {
    272    set_string_default(kOptBackupskip, ga.ga_data, true);
    273  }
    274 }
    275 
    276 /// Initialize the 'cdpath' option to a default value.
    277 static void set_init_default_cdpath(void)
    278 {
    279  char *cdpath = vim_getenv("CDPATH");
    280  if (cdpath == NULL) {
    281    return;
    282  }
    283 
    284  char *buf = xmalloc(2 * strlen(cdpath) + 2);
    285  buf[0] = ',';               // start with ",", current dir first
    286  int j = 1;
    287  for (int i = 0; cdpath[i] != NUL; i++) {
    288    if (vim_ispathlistsep(cdpath[i])) {
    289      buf[j++] = ',';
    290    } else {
    291      if (cdpath[i] == ' ' || cdpath[i] == ',') {
    292        buf[j++] = '\\';
    293      }
    294      buf[j++] = cdpath[i];
    295    }
    296  }
    297  buf[j] = NUL;
    298  change_option_default(kOptCdpath, CSTR_AS_OPTVAL(buf));
    299 
    300  xfree(cdpath);
    301 }
    302 
    303 /// Expand environment variables and things like "~" for the defaults.
    304 /// If option_expand() returns non-NULL the variable is expanded.  This can
    305 /// only happen for non-indirect options.
    306 /// Also set the default to the expanded value, so ":set" does not list
    307 /// them.
    308 static void set_init_expand_env(void)
    309 {
    310  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
    311    vimoption_T *opt = &options[opt_idx];
    312    if (opt->flags & kOptFlagNoDefExp) {
    313      continue;
    314    }
    315    char *p;
    316    if ((opt->flags & kOptFlagGettext) && opt->var != NULL) {
    317      p = _(*(char **)opt->var);
    318    } else {
    319      p = option_expand(opt_idx, NULL);
    320    }
    321    if (p != NULL) {
    322      set_option_varp(opt_idx, opt->var, CSTR_TO_OPTVAL(p), true);
    323      change_option_default(opt_idx, CSTR_TO_OPTVAL(p));
    324    }
    325  }
    326 }
    327 
    328 /// Initialize the encoding used for "default" in 'fileencodings'.
    329 static void set_init_fenc_default(void)
    330 {
    331  // enc_locale() will try to find the encoding of the current locale.
    332  // This will be used when "default" is used as encoding specifier
    333  // in 'fileencodings'.
    334  char *p = enc_locale();
    335  if (p == NULL) {
    336    // Use utf-8 as "default" if locale encoding can't be detected.
    337    p = xmemdupz(S_LEN("utf-8"));
    338  }
    339  fenc_default = p;
    340 }
    341 
    342 /// Initialize the options, first part.
    343 ///
    344 /// Called only once from main(), just after creating the first buffer.
    345 /// If "clean_arg" is true, Nvim was started with --clean.
    346 ///
    347 /// NOTE: ELOG() etc calls are not allowed here, as log location depends on
    348 /// env var expansion which depends on expression evaluation and other
    349 /// editor state initialized here. Do logging in set_init_2 or later.
    350 void set_init_1(bool clean_arg)
    351 {
    352  langmap_init();
    353 
    354  // Allocate the default option values.
    355  alloc_options_default();
    356 
    357  set_init_default_shell();
    358  set_init_default_backupskip();
    359  set_init_default_cdpath();
    360 
    361  char *backupdir = stdpaths_user_state_subpath("backup", 2, true);
    362  const size_t backupdir_len = strlen(backupdir);
    363  backupdir = xrealloc(backupdir, backupdir_len + 3);
    364  memmove(backupdir + 2, backupdir, backupdir_len + 1);
    365  memmove(backupdir, ".,", 2);
    366  set_string_default(kOptBackupdir, backupdir, true);
    367  set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, true),
    368                     true);
    369  set_string_default(kOptDirectory, stdpaths_user_state_subpath("swap", 2, true),
    370                     true);
    371  set_string_default(kOptUndodir, stdpaths_user_state_subpath("undo", 2, true),
    372                     true);
    373  // Set default for &runtimepath. All necessary expansions are performed in
    374  // this function.
    375  char *rtp = runtimepath_default(clean_arg);
    376  if (rtp) {
    377    set_string_default(kOptRuntimepath, rtp, true);
    378    // Make a copy of 'rtp' for 'packpath'
    379    set_string_default(kOptPackpath, rtp, false);
    380    rtp = NULL;  // ownership taken
    381  }
    382 
    383  // Set all the options (except the terminal options) to their default
    384  // value.  Also set the global value for local options.
    385  set_options_default(0);
    386 
    387  curbuf->b_p_initialized = true;
    388  curbuf->b_p_ac = -1;
    389  curbuf->b_p_ar = -1;          // no local 'autoread' value
    390  curbuf->b_p_fs = -1;          // no local 'fsync' value
    391  curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL;
    392  check_buf_options(curbuf);
    393  check_win_options(curwin);
    394  check_options();
    395 
    396  // set 'laststatus'
    397  last_status(false);
    398 
    399  // Must be before option_expand(), because that one needs vim_isIDc()
    400  didset_options();
    401 
    402  // Use the current chartab for the generic chartab. This is not in
    403  // didset_options() because it only depends on 'encoding'.
    404  init_spell_chartab();
    405 
    406  // Expand environment variables and things like "~" for the defaults.
    407  set_init_expand_env();
    408 
    409  save_file_ff(curbuf);         // Buffer is unchanged
    410 
    411  // Detect use of mlterm.
    412  // Mlterm is a terminal emulator akin to xterm that has some special
    413  // abilities (bidi namely).
    414  // NOTE: mlterm's author is being asked to 'set' a variable
    415  //       instead of an environment variable due to inheritance.
    416  if (os_env_exists("MLTERM", false)) {
    417    set_option_value_give_err(kOptTermbidi, BOOLEAN_OPTVAL(true), 0);
    418  }
    419 
    420  didset_options2();
    421 
    422  lang_init();
    423  set_init_fenc_default();
    424 
    425 #ifdef HAVE_WORKING_LIBINTL
    426  // GNU gettext 0.10.37 supports this feature: set the codeset used for
    427  // translated messages independently from the current locale.
    428  (void)bind_textdomain_codeset(PROJECT_NAME, p_enc);
    429 #endif
    430 
    431  // Set the default for 'helplang'.
    432  set_helplang_default(get_mess_lang());
    433 }
    434 
    435 /// Get default value for option, based on the option's type and scope.
    436 ///
    437 /// @param  opt_idx    Option index in options[] table.
    438 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
    439 ///
    440 /// @return Default value of option for the scope specified in opt_flags.
    441 OptVal get_option_default(const OptIndex opt_idx, int opt_flags)
    442 {
    443  vimoption_T *opt = &options[opt_idx];
    444  bool is_global_local_option = option_is_global_local(opt_idx);
    445 
    446 #ifdef UNIX
    447  if (opt_idx == kOptModeline && getuid() == ROOT_UID) {
    448    // 'modeline' defaults to off for root.
    449    return BOOLEAN_OPTVAL(false);
    450  }
    451 #endif
    452 
    453  if ((opt_flags & OPT_LOCAL) && is_global_local_option) {
    454    // Use unset local value instead of default value for local scope of global-local options.
    455    return get_option_unset_value(opt_idx);
    456  } else if (option_has_type(opt_idx, kOptValTypeString) && !(opt->flags & kOptFlagNoDefExp)) {
    457    // For string options, expand environment variables and ~ since the default value was already
    458    // expanded, only required when an environment variable was set later.
    459    char *s = option_expand(opt_idx, opt->def_val.data.string.data);
    460    return s == NULL ? opt->def_val : CSTR_AS_OPTVAL(s);
    461  } else {
    462    return opt->def_val;
    463  }
    464 }
    465 
    466 /// Allocate the default values for all options by copying them from the stack.
    467 /// This ensures that we don't need to always check if the option default is allocated or not.
    468 static void alloc_options_default(void)
    469 {
    470  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
    471    options[opt_idx].def_val = optval_copy(options[opt_idx].def_val);
    472  }
    473 }
    474 
    475 /// Change the default value for an option.
    476 ///
    477 /// @param  opt_idx  Option index in options[] table.
    478 /// @param  value    New default value. Must be allocated.
    479 static void change_option_default(const OptIndex opt_idx, OptVal value)
    480 {
    481  optval_free(options[opt_idx].def_val);
    482  options[opt_idx].def_val = value;
    483 }
    484 
    485 /// Set an option to its default value.
    486 /// This does not take care of side effects!
    487 ///
    488 /// @param  opt_idx    Option index in options[] table.
    489 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
    490 static void set_option_default(const OptIndex opt_idx, int opt_flags)
    491 {
    492  bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
    493  OptVal def_val = get_option_default(opt_idx, opt_flags);
    494  set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid);
    495 
    496  if (opt_idx == kOptScroll) {
    497    win_comp_scroll(curwin);
    498  }
    499 
    500  // The default value is not insecure.
    501  uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
    502  *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure;
    503  if (both) {
    504    flagsp = insecure_flag(curwin, opt_idx, OPT_LOCAL);
    505    *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure;
    506  }
    507 }
    508 
    509 /// Set all options (except terminal options) to their default value.
    510 ///
    511 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
    512 static void set_options_default(int opt_flags)
    513 {
    514  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
    515    if (!(options[opt_idx].flags & kOptFlagNoDefault)) {
    516      set_option_default(opt_idx, opt_flags);
    517    }
    518  }
    519 
    520  // The 'scroll' option must be computed for all windows.
    521  FOR_ALL_TAB_WINDOWS(tp, wp) {
    522    win_comp_scroll(wp);
    523  }
    524 
    525  parse_cino(curbuf);
    526 }
    527 
    528 /// Set the Vi-default value of a string option.
    529 /// Used for 'sh', 'backupskip' and 'term'.
    530 ///
    531 /// @param  opt_idx    Option index in options[] table.
    532 /// @param  val        The value of the option.
    533 /// @param  allocated  If true, do not copy default as it was already allocated.
    534 ///
    535 /// TODO(famiu): Remove this.
    536 static void set_string_default(OptIndex opt_idx, char *val, bool allocated)
    537  FUNC_ATTR_NONNULL_ALL
    538 {
    539  assert(opt_idx != kOptInvalid);
    540  change_option_default(opt_idx, CSTR_AS_OPTVAL(allocated ? val : xstrdup(val)));
    541 }
    542 
    543 /// For an option value that contains comma separated items, find "newval" in
    544 /// "origval".  Return NULL if not found.
    545 static const char *find_dup_item(const char *origval, const char *newval, const size_t newvallen,
    546                                 uint32_t flags)
    547  FUNC_ATTR_NONNULL_ARG(2)
    548 {
    549  if (origval == NULL) {
    550    return NULL;
    551  }
    552 
    553  int bs = 0;
    554 
    555  for (const char *s = origval; *s != NUL; s++) {
    556    if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1)))
    557        && strncmp(s, newval, newvallen) == 0
    558        && (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) {
    559      return s;
    560    }
    561    // Count backslashes.  Only a comma with an even number of backslashes
    562    // or a single backslash preceded by a comma before it is recognized as
    563    // a separator.
    564    if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',')
    565        || (s == origval + 1 && s[-1] == '\\')) {
    566      bs++;
    567    } else {
    568      bs = 0;
    569    }
    570  }
    571  return NULL;
    572 }
    573 
    574 #if defined(EXITFREE)
    575 /// Free all options.
    576 void free_all_options(void)
    577 {
    578  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
    579    bool hidden = is_option_hidden(opt_idx);
    580 
    581    if (option_is_global_only(opt_idx) || hidden) {
    582      // global option: free value and default value.
    583      // hidden option: free default value only.
    584      if (!hidden) {
    585        optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
    586      }
    587    } else if (!option_is_window_local(opt_idx)) {
    588      // buffer-local option: free global value.
    589      optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
    590    }
    591    optval_free(options[opt_idx].def_val);
    592  }
    593  free_operatorfunc_option();
    594  free_tagfunc_option();
    595  free_findfunc_option();
    596  XFREE_CLEAR(fenc_default);
    597  XFREE_CLEAR(p_term);
    598  XFREE_CLEAR(p_ttytype);
    599 }
    600 #endif
    601 
    602 /// Initialize the options, part two: After getting Rows and Columns.
    603 void set_init_2(bool headless)
    604 {
    605  // set in set_init_1 but logging is not allowed there
    606  ILOG("startup runtimepath/packpath value: %s", p_rtp);
    607 
    608  // 'scroll' defaults to half the window height. The stored default is zero,
    609  // which results in the actual value computed from the window height.
    610  if (!(options[kOptScroll].flags & kOptFlagWasSet)) {
    611    set_option_default(kOptScroll, OPT_LOCAL);
    612  }
    613  comp_col();
    614 
    615  // 'window' is only for backwards compatibility with Vi.
    616  // Default is Rows - 1.
    617  if (!option_was_set(kOptWindow)) {
    618    p_window = Rows - 1;
    619  }
    620  change_option_default(kOptWindow, NUMBER_OPTVAL(Rows - 1));
    621 }
    622 
    623 /// Initialize the options, part three: After reading the .vimrc
    624 void set_init_3(void)
    625 {
    626  parse_shape_opt(SHAPE_CURSOR);   // set cursor shapes from 'guicursor'
    627 
    628  // Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
    629  // This is done after other initializations, where 'shell' might have been
    630  // set, but only if they have not been set before.
    631  bool do_srr = !(options[kOptShellredir].flags & kOptFlagWasSet);
    632  bool do_sp = !(options[kOptShellpipe].flags & kOptFlagWasSet);
    633 
    634  size_t len = 0;
    635  char *p = (char *)invocation_path_tail(p_sh, &len);
    636  p = xmemdupz(p, len);
    637 
    638  bool is_csh = path_fnamecmp(p, "csh") == 0 || path_fnamecmp(p, "tcsh") == 0;
    639  bool is_known_shell = path_fnamecmp(p, "sh") == 0 || path_fnamecmp(p, "ksh") == 0
    640                        || path_fnamecmp(p, "mksh") == 0 || path_fnamecmp(p, "pdksh") == 0
    641                        || path_fnamecmp(p, "zsh") == 0 || path_fnamecmp(p, "zsh-beta") == 0
    642                        || path_fnamecmp(p, "bash") == 0 || path_fnamecmp(p, "fish") == 0
    643                        || path_fnamecmp(p, "ash") == 0 || path_fnamecmp(p, "dash") == 0;
    644 
    645  // Default for p_sp is "| tee", for p_srr is ">".
    646  // For known shells it is changed here to include stderr.
    647  if (is_csh || is_known_shell) {
    648    if (do_sp) {
    649      const OptVal sp =
    650        is_csh ? STATIC_CSTR_AS_OPTVAL("|& tee") : STATIC_CSTR_AS_OPTVAL("2>&1| tee");
    651      set_option_direct(kOptShellpipe, sp, 0, SID_NONE);
    652      change_option_default(kOptShellpipe, optval_copy(sp));
    653    }
    654    if (do_srr) {
    655      const OptVal srr = is_csh ? STATIC_CSTR_AS_OPTVAL(">&") : STATIC_CSTR_AS_OPTVAL(">%s 2>&1");
    656      set_option_direct(kOptShellredir, srr, 0, SID_NONE);
    657      change_option_default(kOptShellredir, optval_copy(srr));
    658    }
    659  }
    660  xfree(p);
    661 
    662  if (buf_is_empty(curbuf)) {
    663    // Apply the first entry of 'fileformats' to the initial buffer.
    664    if (options[kOptFileformats].flags & kOptFlagWasSet) {
    665      set_fileformat(default_fileformat(), OPT_LOCAL);
    666    }
    667  }
    668 
    669  set_title_defaults();  // 'title', 'icon'
    670 }
    671 
    672 /// When 'helplang' is still at its default value, set it to "lang".
    673 /// Only the first two characters of "lang" are used.
    674 void set_helplang_default(const char *lang)
    675 {
    676  if (lang == NULL) {
    677    return;
    678  }
    679 
    680  const size_t lang_len = strlen(lang);
    681  if (lang_len < 2) {  // safety check
    682    return;
    683  }
    684  if (options[kOptHelplang].flags & kOptFlagWasSet) {
    685    return;
    686  }
    687 
    688  free_string_option(p_hlg);
    689  p_hlg = xmemdupz(lang, lang_len);
    690  // zh_CN becomes "cn", zh_TW becomes "tw".
    691  if (STRNICMP(p_hlg, "zh_", 3) == 0 && lang_len >= 5) {
    692    p_hlg[0] = (char)TOLOWER_ASC(p_hlg[3]);
    693    p_hlg[1] = (char)TOLOWER_ASC(p_hlg[4]);
    694  } else if (lang_len && *p_hlg == 'C') {
    695    // any C like setting, such as C.UTF-8, becomes "en"
    696    p_hlg[0] = 'e';
    697    p_hlg[1] = 'n';
    698  }
    699  p_hlg[2] = NUL;
    700 }
    701 
    702 /// 'title' and 'icon' only default to true if they have not been set or reset
    703 /// in .vimrc and we can read the old value.
    704 /// When 'title' and 'icon' have been reset in .vimrc, we won't even check if
    705 /// they can be reset.  This reduces startup time when using X on a remote
    706 /// machine.
    707 void set_title_defaults(void)
    708 {
    709  // If GUI is (going to be) used, we can always set the window title and
    710  // icon name.  Saves a bit of time, because the X11 display server does
    711  // not need to be contacted.
    712  if (!(options[kOptTitle].flags & kOptFlagWasSet)) {
    713    change_option_default(kOptTitle, BOOLEAN_OPTVAL(false));
    714    p_title = 0;
    715  }
    716  if (!(options[kOptIcon].flags & kOptFlagWasSet)) {
    717    change_option_default(kOptIcon, BOOLEAN_OPTVAL(false));
    718    p_icon = 0;
    719  }
    720 }
    721 
    722 void ex_set(exarg_T *eap)
    723 {
    724  int flags = 0;
    725 
    726  if (eap->cmdidx == CMD_setlocal) {
    727    flags = OPT_LOCAL;
    728  } else if (eap->cmdidx == CMD_setglobal) {
    729    flags = OPT_GLOBAL;
    730  }
    731  if (eap->forceit) {
    732    flags |= OPT_ONECOLUMN;
    733  }
    734  do_set(eap->arg, flags);
    735 }
    736 
    737 /// Copy the new string value into allocated memory for the option.
    738 /// Can't use set_option_direct(), because we need to remove the backslashes.
    739 static char *stropt_copy_value(const char *origval, char **argp, set_op_T op,
    740                               uint32_t flags FUNC_ATTR_UNUSED)
    741 {
    742  char *arg = *argp;
    743 
    744  // get a bit too much
    745  size_t newlen = strlen(arg) + 1;
    746  if (op != OP_NONE) {
    747    newlen += strlen(origval) + 1;
    748  }
    749  char *newval = xmalloc(newlen);
    750  char *s = newval;
    751 
    752  // Copy the string, skip over escaped chars.
    753  // For MS-Windows backslashes before normal file name characters
    754  // are not removed, and keep backslash at start, for "\\machine\path",
    755  // but do remove it for "\\\\machine\\path".
    756  // The reverse is found in escape_option_str_cmdline().
    757  while (*arg != NUL && !ascii_iswhite(*arg)) {
    758    if (*arg == '\\' && arg[1] != NUL
    759 #ifdef BACKSLASH_IN_FILENAME
    760        && !((flags & kOptFlagExpand)
    761             && vim_isfilec((uint8_t)arg[1])
    762             && !ascii_iswhite(arg[1])
    763             && (arg[1] != '\\'
    764                 || (s == newval && arg[2] != '\\')))
    765 #endif
    766        ) {
    767      arg++;  // remove backslash
    768    }
    769    int i = utfc_ptr2len(arg);
    770    if (i > 1) {
    771      // copy multibyte char
    772      memmove(s, arg, (size_t)i);
    773      arg += i;
    774      s += i;
    775    } else {
    776      *s++ = *arg++;
    777    }
    778  }
    779  *s = NUL;
    780 
    781  *argp = arg;
    782  return newval;
    783 }
    784 
    785 /// Expand environment variables and ~ in string option value 'newval'.
    786 static char *stropt_expand_envvar(OptIndex opt_idx, const char *origval, char *newval, set_op_T op)
    787 {
    788  char *s = option_expand(opt_idx, newval);
    789  if (s == NULL) {
    790    return newval;
    791  }
    792 
    793  xfree(newval);
    794  uint32_t newlen = (unsigned)strlen(s) + 1;
    795  if (op != OP_NONE) {
    796    newlen += (unsigned)strlen(origval) + 1;
    797  }
    798  newval = xmalloc(newlen);
    799  STRCPY(newval, s);
    800 
    801  return newval;
    802 }
    803 
    804 /// Concatenate the original and new values of a string option, adding a "," if
    805 /// needed.
    806 static void stropt_concat_with_comma(const char *origval, char *newval, set_op_T op, uint32_t flags)
    807 {
    808  int len = 0;
    809  int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL);
    810  if (op == OP_ADDING) {
    811    len = (int)strlen(origval);
    812    // Strip a trailing comma, would get 2.
    813    if (comma && len > 1
    814        && (flags & kOptFlagOneComma) == kOptFlagOneComma
    815        && origval[len - 1] == ','
    816        && origval[len - 2] != '\\') {
    817      len--;
    818    }
    819    memmove(newval + len + comma, newval, strlen(newval) + 1);
    820    memmove(newval, origval, (size_t)len);
    821  } else {
    822    len = (int)strlen(newval);
    823    STRMOVE(newval + len + comma, origval);
    824  }
    825  if (comma) {
    826    newval[len] = ',';
    827  }
    828 }
    829 
    830 /// Remove a value from a string option.  Copy string option value in "origval"
    831 /// to "newval" and then remove the string "strval" of length "len".
    832 static void stropt_remove_val(const char *origval, char *newval, uint32_t flags, const char *strval,
    833                              int len)
    834 {
    835  // Remove newval[] from origval[]. (Note: "len" has been set above
    836  // and is used here).
    837  STRCPY(newval, origval);
    838  if (*strval) {
    839    // may need to remove a comma
    840    if (flags & kOptFlagComma) {
    841      if (strval == origval) {
    842        // include comma after string
    843        if (strval[len] == ',') {
    844          len++;
    845        }
    846      } else {
    847        // include comma before string
    848        strval--;
    849        len++;
    850      }
    851    }
    852    STRMOVE(newval + (strval - origval), strval + len);
    853  }
    854 }
    855 
    856 /// Remove flags that appear twice in the string option value 'newval'.
    857 static void stropt_remove_dupflags(char *newval, uint32_t flags)
    858 {
    859  char *s = newval;
    860  // Remove flags that appear twice.
    861  for (s = newval; *s;) {
    862    // if options have kOptFlagFlagList and kOptFlagOneComma such as 'whichwrap'
    863    if (flags & kOptFlagOneComma) {
    864      if (*s != ',' && *(s + 1) == ','
    865          && vim_strchr(s + 2, (uint8_t)(*s)) != NULL) {
    866        // Remove the duplicated value and the next comma.
    867        STRMOVE(s, s + 2);
    868        continue;
    869      }
    870    } else {
    871      if ((!(flags & kOptFlagComma) || *s != ',')
    872          && vim_strchr(s + 1, (uint8_t)(*s)) != NULL) {
    873        STRMOVE(s, s + 1);
    874        continue;
    875      }
    876    }
    877    s++;
    878  }
    879 }
    880 
    881 /// Get the string value specified for a ":set" command.  The following set options are supported:
    882 ///     set {opt}={val}
    883 ///     set {opt}:{val}
    884 static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp,
    885                               const char *origval, set_op_T *op_arg, uint32_t flags)
    886 {
    887  char *arg = *argp;
    888  set_op_T op = *op_arg;
    889  char *save_arg = NULL;
    890  char *newval;
    891  const char *s = NULL;
    892 
    893  arg++;  // jump to after the '=' or ':'
    894 
    895  // Set 'keywordprg' to ":help" if an empty
    896  // value was passed to :set by the user.
    897  if (varp == &p_kp && (*arg == NUL || *arg == ' ')) {
    898    save_arg = arg;
    899    arg = ":help";
    900  }
    901 
    902  // Copy the new string into allocated memory.
    903  newval = stropt_copy_value(origval, &arg, op, flags);
    904 
    905  // Expand environment variables and ~.
    906  // Don't do it when adding without inserting a comma.
    907  if (op == OP_NONE || (flags & kOptFlagComma)) {
    908    newval = stropt_expand_envvar(opt_idx, origval, newval, op);
    909  }
    910 
    911  // locate newval[] in origval[] when removing it
    912  // and when adding to avoid duplicates
    913  int len = 0;
    914  if (op == OP_REMOVING || (flags & kOptFlagNoDup)) {
    915    len = (int)strlen(newval);
    916    s = find_dup_item(origval, newval, (size_t)len, flags);
    917 
    918    // do not add if already there
    919    if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
    920      op = OP_NONE;
    921      STRCPY(newval, origval);
    922    }
    923 
    924    // if no duplicate, move pointer to end of original value
    925    if (s == NULL) {
    926      s = origval + (int)strlen(origval);
    927    }
    928  }
    929 
    930  // concatenate the two strings; add a ',' if needed
    931  if (op == OP_ADDING || op == OP_PREPENDING) {
    932    stropt_concat_with_comma(origval, newval, op, flags);
    933  } else if (op == OP_REMOVING) {
    934    // Remove newval[] from origval[]. (Note: "len" has been set above
    935    // and is used here).
    936    stropt_remove_val(origval, newval, flags, s, len);
    937  }
    938 
    939  if (flags & kOptFlagFlagList) {
    940    // Remove flags that appear twice.
    941    stropt_remove_dupflags(newval, flags);
    942  }
    943 
    944  if (save_arg != NULL) {
    945    arg = save_arg;  // arg was temporarily changed, restore it
    946  }
    947  *argp = arg;
    948  *op_arg = op;
    949 
    950  return newval;
    951 }
    952 
    953 static set_op_T get_op(const char *arg)
    954 {
    955  set_op_T op = OP_NONE;
    956  if (*arg != NUL && *(arg + 1) == '=') {
    957    if (*arg == '+') {
    958      op = OP_ADDING;          // "+="
    959    } else if (*arg == '^') {
    960      op = OP_PREPENDING;      // "^="
    961    } else if (*arg == '-') {
    962      op = OP_REMOVING;        // "-="
    963    }
    964  }
    965  return op;
    966 }
    967 
    968 static set_prefix_T get_option_prefix(char **argp)
    969 {
    970  if (strncmp(*argp, "no", 2) == 0) {
    971    *argp += 2;
    972    return PREFIX_NO;
    973  } else if (strncmp(*argp, "inv", 3) == 0) {
    974    *argp += 3;
    975    return PREFIX_INV;
    976  }
    977 
    978  return PREFIX_NONE;
    979 }
    980 
    981 static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_t flags,
    982                            set_prefix_T prefix, const char **errmsg)
    983 {
    984  // Only bools can have a prefix of 'inv' or 'no'
    985  if (!option_has_type(opt_idx, kOptValTypeBoolean) && prefix != PREFIX_NONE) {
    986    *errmsg = e_invarg;
    987    return FAIL;
    988  }
    989 
    990  // Skip all options that are not window-local (used when showing
    991  // an already loaded buffer in a window).
    992  if ((opt_flags & OPT_WINONLY) && !option_is_window_local(opt_idx)) {
    993    return FAIL;
    994  }
    995 
    996  // Skip all options that are window-local (used for :vimgrep).
    997  if ((opt_flags & OPT_NOWIN) && option_is_window_local(opt_idx)) {
    998    return FAIL;
    999  }
   1000 
   1001  // Disallow changing some options from modelines.
   1002  if (opt_flags & OPT_MODELINE) {
   1003    if (flags & (kOptFlagSecure | kOptFlagNoML)) {
   1004      *errmsg = e_not_allowed_in_modeline;
   1005      return FAIL;
   1006    }
   1007    if ((flags & kOptFlagMLE) && !p_mle) {
   1008      *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
   1009      return FAIL;
   1010    }
   1011    // In diff mode some options are overruled.  This avoids that
   1012    // 'foldmethod' becomes "marker" instead of "diff" and that
   1013    // "wrap" gets set.
   1014    if (win->w_p_diff && (opt_idx == kOptFoldmethod || opt_idx == kOptWrap)) {
   1015      return FAIL;
   1016    }
   1017  }
   1018 
   1019  // Disallow changing some options in the sandbox
   1020  if (sandbox != 0 && (flags & kOptFlagSecure)) {
   1021    *errmsg = e_sandbox;
   1022    return FAIL;
   1023  }
   1024 
   1025  return OK;
   1026 }
   1027 
   1028 /// Skip over the name of a TTY option or keycode option.
   1029 ///
   1030 /// @param[in]  arg  Start of TTY or keycode option name.
   1031 ///
   1032 /// @return NULL when option isn't a TTY or keycode option. Otherwise pointer to the char after the
   1033 /// option name.
   1034 static const char *find_tty_option_end(const char *arg)
   1035 {
   1036  if (strequal(arg, "term")) {
   1037    return arg + sizeof("term") - 1;
   1038  } else if (strequal(arg, "ttytype")) {
   1039    return arg + sizeof("ttytype") - 1;
   1040  }
   1041 
   1042  const char *p = arg;
   1043  bool delimit = false;  // whether to delimit <
   1044 
   1045  if (arg[0] == '<') {
   1046    // look out for <t_>;>
   1047    delimit = true;
   1048    p++;
   1049  }
   1050  if (p[0] == 't' && p[1] == '_' && p[2] && p[3]) {
   1051    // "t_xx" ("t_Co") option.
   1052    p += 4;
   1053  } else if (delimit) {
   1054    // Search for delimiting >.
   1055    while (*p != NUL && *p != '>') {
   1056      p++;
   1057    }
   1058  }
   1059  // Return NULL when delimiting > is not found.
   1060  if (delimit) {
   1061    if (*p != '>') {
   1062      return NULL;
   1063    }
   1064    p++;
   1065  }
   1066 
   1067  return arg == p ? NULL : p;
   1068 }
   1069 
   1070 /// Skip over the name of an option.
   1071 ///
   1072 /// @param[in]   arg       Start of option name.
   1073 /// @param[out]  opt_idxp  Set to option index in options[] table.
   1074 ///
   1075 /// @return NULL when no option name found. Otherwise pointer to the char after the option name.
   1076 const char *find_option_end(const char *arg, OptIndex *opt_idxp)
   1077 {
   1078  const char *p;
   1079 
   1080  // Handle TTY and keycode options separately.
   1081  if ((p = find_tty_option_end(arg)) != NULL) {
   1082    *opt_idxp = kOptInvalid;
   1083    return p;
   1084  } else {
   1085    p = arg;
   1086  }
   1087 
   1088  if (!ASCII_ISALPHA(*p)) {
   1089    *opt_idxp = kOptInvalid;
   1090    return NULL;
   1091  }
   1092  while (ASCII_ISALPHA(*p)) {
   1093    p++;
   1094  }
   1095 
   1096  *opt_idxp = find_option_len(arg, (size_t)(p - arg));
   1097  return p;
   1098 }
   1099 
   1100 /// Get new option value from argp. Allocated OptVal must be freed by caller.
   1101 /// Can unset local value of an option when ":set {option}<" is used.
   1102 static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp,
   1103                                int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
   1104                                const size_t errbuflen, const char **errmsg)
   1105  FUNC_ATTR_WARN_UNUSED_RESULT
   1106 {
   1107  assert(varp != NULL);
   1108 
   1109  vimoption_T *opt = &options[opt_idx];
   1110  char *arg = *argp;
   1111  // When setting the local value of a global option, the old value may be the global value.
   1112  const bool oldval_is_global = option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL);
   1113  OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp);
   1114  OptVal newval = NIL_OPTVAL;
   1115 
   1116  if (nextchar == '&') {
   1117    // ":set opt&": Reset to default value.
   1118    // NOTE: Use OPT_GLOBAL instead of opt_flags to ensure we don't use the unset local value for
   1119    // global-local options when OPT_LOCAL is used.
   1120    return optval_copy(get_option_default(opt_idx, OPT_GLOBAL));
   1121  } else if (nextchar == '<') {
   1122    // ":set opt<": Reset to global value.
   1123    // ":setlocal opt<": Copy global value to local value.
   1124    if (option_is_global_local(opt_idx) && !(opt_flags & OPT_LOCAL)) {
   1125      unset_option_local_value(opt_idx);
   1126    }
   1127    return get_option_value(opt_idx, OPT_GLOBAL);
   1128  }
   1129 
   1130  switch (oldval.type) {
   1131  case kOptValTypeNil:
   1132    abort();
   1133  case kOptValTypeBoolean: {
   1134    TriState newval_bool;
   1135 
   1136    // ":set opt!": invert
   1137    if (nextchar == '!') {
   1138      switch (oldval.data.boolean) {
   1139      case kNone:
   1140        newval_bool = kNone;
   1141        break;
   1142      case kTrue:
   1143        newval_bool = kFalse;
   1144        break;
   1145      case kFalse:
   1146        newval_bool = kTrue;
   1147        break;
   1148      }
   1149    } else {
   1150      // ":set invopt": invert
   1151      // ":set opt" or ":set noopt": set or reset
   1152      if (prefix == PREFIX_INV) {
   1153        newval_bool = *(int *)varp ^ 1;
   1154      } else {
   1155        newval_bool = prefix == PREFIX_NO ? 0 : 1;
   1156      }
   1157    }
   1158 
   1159    newval = BOOLEAN_OPTVAL(newval_bool);
   1160    break;
   1161  }
   1162  case kOptValTypeNumber: {
   1163    OptInt oldval_num = oldval.data.number;
   1164    OptInt newval_num;
   1165 
   1166    // Different ways to set a number option:
   1167    // <xx>         accept special key codes for 'wildchar' or 'wildcharm'
   1168    // ^x           accept ctrl key codes for 'wildchar' or 'wildcharm'
   1169    // c            accept any non-digit for 'wildchar' or 'wildcharm'
   1170    // [-]0-9       set number
   1171    // other        error
   1172    arg++;
   1173    if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
   1174        && (*arg == '<' || *arg == '^'
   1175            || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1])) && !ascii_isdigit(*arg)))) {
   1176      newval_num = string_to_key(arg);
   1177      if (newval_num == 0) {
   1178        *errmsg = e_invarg;
   1179        return newval;
   1180      }
   1181    } else if (*arg == '-' || ascii_isdigit(*arg)) {
   1182      int i;
   1183      // Allow negative, octal and hex numbers.
   1184      vim_str2nr(arg, NULL, &i, STR2NR_ALL, &newval_num, NULL, 0, true, NULL);
   1185      if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
   1186        *errmsg = e_number_required_after_equal;
   1187        return newval;
   1188      }
   1189    } else {
   1190      *errmsg = e_number_required_after_equal;
   1191      return newval;
   1192    }
   1193 
   1194    if (op == OP_ADDING) {
   1195      newval_num = oldval_num + newval_num;
   1196    }
   1197    if (op == OP_PREPENDING) {
   1198      newval_num = oldval_num * newval_num;
   1199    }
   1200    if (op == OP_REMOVING) {
   1201      newval_num = oldval_num - newval_num;
   1202    }
   1203 
   1204    newval = NUMBER_OPTVAL(newval_num);
   1205    break;
   1206  }
   1207  case kOptValTypeString: {
   1208    const char *oldval_str = oldval.data.string.data;
   1209    // Get the new value for the option
   1210    const char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op,
   1211                                               flags);
   1212    newval = CSTR_AS_OPTVAL(newval_str);
   1213    break;
   1214  }
   1215  }
   1216 
   1217  return newval;
   1218 }
   1219 
   1220 static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *errbuf,
   1221                              size_t errbuflen, const char **errmsg)
   1222 {
   1223  // 1: nothing, 0: "no", 2: "inv" in front of name
   1224  set_prefix_T prefix = get_option_prefix(argp);
   1225 
   1226  char *arg = *argp;
   1227 
   1228  // find end of name
   1229  OptIndex opt_idx;
   1230  const char *const option_end = find_option_end(arg, &opt_idx);
   1231 
   1232  if (opt_idx != kOptInvalid) {
   1233    assert(option_end >= arg);
   1234  } else if (is_tty_option(arg)) {  // Silently ignore TTY options.
   1235    return;
   1236  } else {                          // Invalid option name, skip.
   1237    *errmsg = e_unknown_option;
   1238    return;
   1239  }
   1240 
   1241  // Remember character after option name.
   1242  uint8_t afterchar = (uint8_t)(*option_end);
   1243  char *p = (char *)option_end;
   1244 
   1245  // Skip white space, allow ":set ai  ?".
   1246  while (ascii_iswhite(*p)) {
   1247    p++;
   1248  }
   1249 
   1250  set_op_T op = get_op(p);
   1251  if (op != OP_NONE) {
   1252    p++;
   1253  }
   1254 
   1255  uint8_t nextchar = (uint8_t)(*p);  // next non-white char after option name
   1256  // flags for current option
   1257  uint32_t flags = options[opt_idx].flags;
   1258  // pointer to variable for current option
   1259  void *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
   1260 
   1261  if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) {
   1262    return;
   1263  }
   1264 
   1265  if (vim_strchr("?=:!&<", nextchar) != NULL) {
   1266    *argp = p;
   1267 
   1268    if (nextchar == '&' && (*argp)[1] == 'v' && (*argp)[2] == 'i') {
   1269      if ((*argp)[3] == 'm') {  // "opt&vim": set to Vim default
   1270        *argp += 3;
   1271      } else {  // "opt&vi": set to Vi default
   1272        *argp += 2;
   1273      }
   1274    }
   1275    if (vim_strchr("?!&<", nextchar) != NULL
   1276        && (*argp)[1] != NUL && !ascii_iswhite((*argp)[1])) {
   1277      *errmsg = e_trailing;
   1278      return;
   1279    }
   1280  }
   1281 
   1282  // Allow '=' and ':' as MS-DOS command.com allows only one '=' character per "set" command line.
   1283  if (nextchar == '?'
   1284      || (prefix == PREFIX_NONE && vim_strchr("=:&<", nextchar) == NULL
   1285          && !option_has_type(opt_idx, kOptValTypeBoolean))) {
   1286    // print value
   1287    if (*did_show) {
   1288      msg_putchar('\n');                // cursor below last one
   1289    } else {
   1290      msg_ext_set_kind("list_cmd");
   1291      gotocmdline(true);                // cursor at status line
   1292      *did_show = true;                 // remember that we did a line
   1293    }
   1294    showoneopt(&options[opt_idx], opt_flags);
   1295 
   1296    if (p_verbose > 0) {
   1297      // Mention where the option was last set.
   1298      if (varp == options[opt_idx].var) {
   1299        last_set_msg(options[opt_idx].script_ctx);
   1300      } else if (option_has_scope(opt_idx, kOptScopeWin)) {
   1301        last_set_msg(curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)]);
   1302      } else if (option_has_scope(opt_idx, kOptScopeBuf)) {
   1303        last_set_msg(curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)]);
   1304      }
   1305    }
   1306 
   1307    if (nextchar != '?' && nextchar != NUL && !ascii_iswhite(afterchar)) {
   1308      *errmsg = e_trailing;
   1309    }
   1310    return;
   1311  }
   1312 
   1313  if (option_has_type(opt_idx, kOptValTypeBoolean)) {
   1314    if (vim_strchr("=:", nextchar) != NULL) {
   1315      *errmsg = e_invarg;
   1316      return;
   1317    }
   1318 
   1319    if (vim_strchr("!&<", nextchar) == NULL && nextchar != NUL && !ascii_iswhite(afterchar)) {
   1320      *errmsg = e_trailing;
   1321      return;
   1322    }
   1323  } else {
   1324    if (vim_strchr("=:&<", nextchar) == NULL) {
   1325      *errmsg = e_invarg;
   1326      return;
   1327    }
   1328  }
   1329 
   1330  OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp,
   1331                                    errbuf, errbuflen, errmsg);
   1332 
   1333  if (newval.type == kOptValTypeNil || *errmsg != NULL) {
   1334    return;
   1335  }
   1336 
   1337  *errmsg = set_option(opt_idx, newval, opt_flags, 0, false, op == OP_NONE, errbuf, errbuflen);
   1338 }
   1339 
   1340 /// Parse 'arg' for option settings.
   1341 ///
   1342 /// 'arg' may be IObuff, but only when no errors can be present and option
   1343 /// does not need to be expanded with option_expand().
   1344 /// "opt_flags":
   1345 /// 0 for ":set"
   1346 /// OPT_GLOBAL   for ":setglobal"
   1347 /// OPT_LOCAL    for ":setlocal" and a modeline
   1348 /// OPT_MODELINE for a modeline
   1349 /// OPT_WINONLY  to only set window-local options
   1350 /// OPT_NOWIN    to skip setting window-local options
   1351 ///
   1352 /// @param arg  option string (may be written to!)
   1353 ///
   1354 /// @return  FAIL if an error is detected, OK otherwise
   1355 int do_set(char *arg, int opt_flags)
   1356 {
   1357  bool did_show = false;             // already showed one value
   1358 
   1359  if (*arg == NUL) {
   1360    showoptions(false, opt_flags);
   1361    did_show = true;
   1362  } else {
   1363    while (*arg != NUL) {         // loop to process all options
   1364      if (strncmp(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3])
   1365          && !(opt_flags & OPT_MODELINE)) {
   1366        // ":set all"  show all options.
   1367        // ":set all&" set all options to their default value.
   1368        arg += 3;
   1369        if (*arg == '&') {
   1370          arg++;
   1371          // Only for :set command set global value of local options.
   1372          set_options_default(opt_flags);
   1373          didset_options();
   1374          didset_options2();
   1375          ui_refresh_options();
   1376          redraw_all_later(UPD_CLEAR);
   1377        } else {
   1378          showoptions(true, opt_flags);
   1379          did_show = true;
   1380        }
   1381      } else {
   1382        char *startarg = arg;             // remember for error message
   1383        const char *errmsg = NULL;
   1384        char errbuf[ERR_BUFLEN];
   1385 
   1386        do_one_set_option(opt_flags, &arg, &did_show, errbuf, sizeof(errbuf), &errmsg);
   1387 
   1388        // Advance to next argument.
   1389        // - skip until a blank found, taking care of backslashes
   1390        // - skip blanks
   1391        // - skip one "=val" argument (for hidden options ":set gfn =xx")
   1392        for (int i = 0; i < 2; i++) {
   1393          arg = skiptowhite_esc(arg);
   1394          arg = skipwhite(arg);
   1395          if (*arg != '=') {
   1396            break;
   1397          }
   1398        }
   1399 
   1400        if (errmsg != NULL) {
   1401          int i = vim_snprintf((char *)IObuff, IOSIZE, "%s", _(errmsg)) + 2;
   1402          if (i + (arg - startarg) < IOSIZE) {
   1403            // append the argument with the error
   1404            xstrlcpy(IObuff + i - 2, ": ", (size_t)(IOSIZE - i + 2));
   1405            assert(arg >= startarg);
   1406            memmove(IObuff + i, startarg, (size_t)(arg - startarg));
   1407            IObuff[i + (arg - startarg)] = NUL;
   1408          }
   1409          // make sure all characters are printable
   1410          trans_characters(IObuff, IOSIZE);
   1411 
   1412          no_wait_return++;         // wait_return() done later
   1413          emsg(IObuff);             // show error highlighted
   1414          no_wait_return--;
   1415 
   1416          return FAIL;
   1417        }
   1418      }
   1419 
   1420      arg = skipwhite(arg);
   1421    }
   1422  }
   1423 
   1424  if (silent_mode && did_show) {
   1425    // After displaying option values in silent mode.
   1426    silent_mode = false;
   1427    info_message = true;        // use stdout, not stderr
   1428    msg_putchar('\n');
   1429    silent_mode = true;
   1430    info_message = false;       // use stdout, not stderr
   1431  }
   1432 
   1433  return OK;
   1434 }
   1435 
   1436 // Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
   1437 // When "has_lt" is true there is a '<' before "*arg_arg".
   1438 // Returns 0 when the key is not recognized.
   1439 static int find_key_len(const char *arg_arg, size_t len, bool has_lt)
   1440 {
   1441  int key = 0;
   1442  const char *arg = arg_arg;
   1443 
   1444  // Don't use get_special_key_code() for t_xx, we don't want it to call
   1445  // add_termcap_entry().
   1446  if (len >= 4 && arg[0] == 't' && arg[1] == '_') {
   1447    if (!has_lt || arg[4] == '>') {
   1448      key = TERMCAP2KEY((uint8_t)arg[2], (uint8_t)arg[3]);
   1449    }
   1450  } else if (has_lt) {
   1451    arg--;  // put arg at the '<'
   1452    int modifiers = 0;
   1453    key = find_special_key(&arg, len + 1, &modifiers, FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY,
   1454                           NULL);
   1455    if (modifiers) {  // can't handle modifiers here
   1456      key = 0;
   1457    }
   1458  }
   1459  return key;
   1460 }
   1461 
   1462 /// Convert a key name or string into a key value.
   1463 /// Used for 'cedit', 'wildchar' and 'wildcharm' options.
   1464 int string_to_key(char *arg)
   1465 {
   1466  if (*arg == '<' && arg[1]) {
   1467    return find_key_len(arg + 1, strlen(arg), true);
   1468  }
   1469  if (*arg == '^' && arg[1]) {
   1470    int key = CTRL_CHR((uint8_t)arg[1]);
   1471    if (key == 0) {  // ^@ is <Nul>
   1472      key = K_ZERO;
   1473    }
   1474    return key;
   1475  }
   1476  return (uint8_t)(*arg);
   1477 }
   1478 
   1479 // When changing 'title', 'titlestring', 'icon' or 'iconstring', call
   1480 // maketitle() to create and display it.
   1481 // When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
   1482 // the old value back.
   1483 void did_set_title(void)
   1484 {
   1485  if (starting != NO_SCREEN) {
   1486    maketitle();
   1487  }
   1488 }
   1489 
   1490 /// set_options_bin -  called when 'bin' changes value.
   1491 ///
   1492 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   1493 void set_options_bin(int oldval, int newval, int opt_flags)
   1494 {
   1495  // The option values that are changed when 'bin' changes are
   1496  // copied when 'bin is set and restored when 'bin' is reset.
   1497  if (newval) {
   1498    if (!oldval) {              // switched on
   1499      if (!(opt_flags & OPT_GLOBAL)) {
   1500        curbuf->b_p_tw_nobin = curbuf->b_p_tw;
   1501        curbuf->b_p_wm_nobin = curbuf->b_p_wm;
   1502        curbuf->b_p_ml_nobin = curbuf->b_p_ml;
   1503        curbuf->b_p_et_nobin = curbuf->b_p_et;
   1504      }
   1505      if (!(opt_flags & OPT_LOCAL)) {
   1506        p_tw_nobin = p_tw;
   1507        p_wm_nobin = p_wm;
   1508        p_ml_nobin = p_ml;
   1509        p_et_nobin = p_et;
   1510      }
   1511    }
   1512 
   1513    if (!(opt_flags & OPT_GLOBAL)) {
   1514      curbuf->b_p_tw = 0;       // no automatic line wrap
   1515      curbuf->b_p_wm = 0;       // no automatic line wrap
   1516      curbuf->b_p_ml = 0;       // no modelines
   1517      curbuf->b_p_et = 0;       // no expandtab
   1518    }
   1519    if (!(opt_flags & OPT_LOCAL)) {
   1520      p_tw = 0;
   1521      p_wm = 0;
   1522      p_ml = false;
   1523      p_et = false;
   1524      p_bin = true;             // needed when called for the "-b" argument
   1525    }
   1526  } else if (oldval) {        // switched off
   1527    if (!(opt_flags & OPT_GLOBAL)) {
   1528      curbuf->b_p_tw = curbuf->b_p_tw_nobin;
   1529      curbuf->b_p_wm = curbuf->b_p_wm_nobin;
   1530      curbuf->b_p_ml = curbuf->b_p_ml_nobin;
   1531      curbuf->b_p_et = curbuf->b_p_et_nobin;
   1532    }
   1533    if (!(opt_flags & OPT_LOCAL)) {
   1534      p_tw = p_tw_nobin;
   1535      p_wm = p_wm_nobin;
   1536      p_ml = p_ml_nobin;
   1537      p_et = p_et_nobin;
   1538    }
   1539  }
   1540 
   1541  // Remember where the dependent option were reset
   1542  didset_options_sctx(opt_flags, p_bin_dep_opts);
   1543 }
   1544 
   1545 /// Expand environment variables for some string options.
   1546 /// These string options cannot be indirect!
   1547 /// If "val" is NULL expand the current value of the option.
   1548 /// Return pointer to NameBuff, or NULL when not expanded.
   1549 static char *option_expand(OptIndex opt_idx, const char *val)
   1550 {
   1551  // if option doesn't need expansion nothing to do
   1552  if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
   1553    return NULL;
   1554  }
   1555 
   1556  if (val == NULL) {
   1557    val = *(char **)options[opt_idx].var;
   1558  }
   1559 
   1560  // If val is longer than MAXPATHL no meaningful expansion can be done,
   1561  // expand_env() would truncate the string.
   1562  if (val == NULL || strlen(val) > MAXPATHL) {
   1563    return NULL;
   1564  }
   1565 
   1566  // Expanding this with NameBuff, expand_env() must not be passed IObuff.
   1567  // Escape spaces when expanding 'tags' or 'path', they are used to separate
   1568  // file names.
   1569  // For 'spellsuggest' expand after "file:".
   1570  char **var = (char **)options[opt_idx].var;
   1571  bool esc = var == &p_tags || var == &p_path;
   1572  expand_env_esc(val, NameBuff, MAXPATHL, esc, false,
   1573                 (char **)options[opt_idx].var == &p_sps ? "file:" : NULL);
   1574  if (strcmp(NameBuff, val) == 0) {   // they are the same
   1575    return NULL;
   1576  }
   1577 
   1578  return NameBuff;
   1579 }
   1580 
   1581 /// After setting various option values: recompute variables that depend on
   1582 /// option values.
   1583 static void didset_options(void)
   1584 {
   1585  // initialize the table for 'iskeyword' et.al.
   1586  init_chartab();
   1587 
   1588  didset_string_options();
   1589 
   1590  spell_check_msm();
   1591  spell_check_sps();
   1592  compile_cap_prog(curwin->w_s);
   1593  did_set_spell_option();
   1594  // set cedit_key
   1595  did_set_cedit(NULL);
   1596  // initialize the table for 'breakat'.
   1597  did_set_breakat(NULL);
   1598  didset_window_options(curwin, true);
   1599 }
   1600 
   1601 // More side effects of setting options.
   1602 static void didset_options2(void)
   1603 {
   1604  // Initialize the highlight_attr[] table.
   1605  highlight_changed();
   1606 
   1607  // Parse default for 'fillchars'.
   1608  set_chars_option(curwin, curwin->w_p_fcs, kFillchars, true, NULL, 0);
   1609 
   1610  // Parse default for 'listchars'.
   1611  set_chars_option(curwin, curwin->w_p_lcs, kListchars, true, NULL, 0);
   1612 
   1613  // Parse default for 'wildmode'.
   1614  check_opt_wim();
   1615  xfree(curbuf->b_p_vsts_array);
   1616  tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
   1617  xfree(curbuf->b_p_vts_array);
   1618  tabstop_set(curbuf->b_p_vts,  &curbuf->b_p_vts_array);
   1619 }
   1620 
   1621 /// Check for string options that are NULL (normally only termcap options).
   1622 void check_options(void)
   1623 {
   1624  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   1625    if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) {
   1626      check_string_option((char **)get_varp(&(options[opt_idx])));
   1627    }
   1628  }
   1629 }
   1630 
   1631 /// Check if option was set insecurely.
   1632 ///
   1633 /// @param  wp         Window.
   1634 /// @param  opt_idx    Option index in options[] table.
   1635 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   1636 ///
   1637 /// @return  True if option was set from a modeline or in secure mode, false if it wasn't.
   1638 int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags)
   1639 {
   1640  assert(opt_idx != kOptInvalid);
   1641 
   1642  uint32_t *flagp = insecure_flag(wp, opt_idx, opt_flags);
   1643  return (*flagp & kOptFlagInsecure) != 0;
   1644 }
   1645 
   1646 /// Get a pointer to the flags used for the kOptFlagInsecure flag of option
   1647 /// "opt_idx".  For some local options a local flags field is used.
   1648 /// NOTE: Caller must make sure that "wp" is set to the window from which
   1649 /// the option is used.
   1650 uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags)
   1651 {
   1652  if (opt_flags & OPT_LOCAL) {
   1653    assert(wp != NULL);
   1654    switch (opt_idx) {
   1655    case kOptWrap:
   1656      return &wp->w_p_wrap_flags;
   1657    case kOptStatusline:
   1658      return &wp->w_p_stl_flags;
   1659    case kOptWinbar:
   1660      return &wp->w_p_wbr_flags;
   1661    case kOptFoldexpr:
   1662      return &wp->w_p_fde_flags;
   1663    case kOptFoldtext:
   1664      return &wp->w_p_fdt_flags;
   1665    case kOptIndentexpr:
   1666      return &wp->w_buffer->b_p_inde_flags;
   1667    case kOptFormatexpr:
   1668      return &wp->w_buffer->b_p_fex_flags;
   1669    case kOptIncludeexpr:
   1670      return &wp->w_buffer->b_p_inex_flags;
   1671    default:
   1672      break;
   1673    }
   1674  } else {
   1675    // For global value of window-local options, use flags in w_allbuf_opt.
   1676    switch (opt_idx) {
   1677    case kOptWrap:
   1678      return &wp->w_allbuf_opt.wo_wrap_flags;
   1679    case kOptFoldexpr:
   1680      return &wp->w_allbuf_opt.wo_fde_flags;
   1681    case kOptFoldtext:
   1682      return &wp->w_allbuf_opt.wo_fdt_flags;
   1683    default:
   1684      break;
   1685    }
   1686  }
   1687  // Nothing special, return global flags field.
   1688  return &options[opt_idx].flags;
   1689 }
   1690 
   1691 /// Redraw the window title and/or tab page text later.
   1692 void redraw_titles(void)
   1693 {
   1694  need_maketitle = true;
   1695  redraw_tabline = true;
   1696 }
   1697 
   1698 /// Return true if "val" is a valid name: only consists of alphanumeric ASCII
   1699 /// characters or characters in "allowed".
   1700 bool valid_name(const char *val, const char *allowed)
   1701  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   1702 {
   1703  for (const char *s = val; *s != NUL; s++) {
   1704    if (!ASCII_ISALNUM(*s)
   1705        && vim_strchr(allowed, (uint8_t)(*s)) == NULL) {
   1706      return false;
   1707    }
   1708  }
   1709  return true;
   1710 }
   1711 
   1712 void check_blending(win_T *wp)
   1713 {
   1714  wp->w_grid_alloc.blending =
   1715    wp->w_p_winbl > 0 || (wp->w_floating && wp->w_config.shadow);
   1716 }
   1717 
   1718 /// Handle setting `winhighlight' in window "wp"
   1719 ///
   1720 /// @param winhl  when NULL: use "wp->w_p_winhl"
   1721 /// @param wp     when NULL: only parse "winhl"
   1722 ///
   1723 /// @return  whether the option value is valid.
   1724 bool parse_winhl_opt(const char *winhl, win_T *wp)
   1725 {
   1726  const char *p = empty_string_option;
   1727  if (winhl != NULL) {
   1728    p = winhl;
   1729  } else if (wp != NULL) {
   1730    p = wp->w_p_winhl;
   1731  }
   1732 
   1733  if (!*p) {
   1734    if (wp != NULL && wp->w_ns_hl_winhl > 0 && wp->w_ns_hl == wp->w_ns_hl_winhl) {
   1735      wp->w_ns_hl = 0;
   1736      wp->w_hl_needs_update = true;
   1737    }
   1738 
   1739    return true;
   1740  }
   1741 
   1742  int ns_hl = 0;
   1743  if (wp != NULL) {
   1744    if (wp->w_ns_hl_winhl == 0) {
   1745      wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING);
   1746    } else {
   1747      // Namespace already exists. Invalidate existing items.
   1748      DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true);
   1749      dp->hl_valid++;
   1750    }
   1751    ns_hl = wp->w_ns_hl_winhl;
   1752    if (wp->w_ns_hl <= 0) {
   1753      wp->w_ns_hl = wp->w_ns_hl_winhl;
   1754    }
   1755  }
   1756 
   1757  while (*p) {
   1758    const char *colon = strchr(p, ':');
   1759    if (!colon) {
   1760      return false;
   1761    }
   1762    size_t nlen = (size_t)(colon - p);
   1763    const char *hi = colon + 1;
   1764    const char *commap = xstrchrnul(hi, ',');
   1765    size_t len = (size_t)(commap - hi);
   1766    int hl_id = len ? syn_check_group(hi, len) : -1;
   1767    if (hl_id == 0) {
   1768      return false;
   1769    }
   1770    int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
   1771    if (hl_id_link == 0) {
   1772      return false;
   1773    }
   1774 
   1775    if (wp != NULL) {
   1776      HlAttrs attrs = HLATTRS_INIT;
   1777      attrs.rgb_ae_attr |= HL_GLOBAL;
   1778      ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL);
   1779    }
   1780 
   1781    p = *commap ? commap + 1 : "";
   1782  }
   1783 
   1784  if (wp != NULL) {
   1785    wp->w_hl_needs_update = true;
   1786  }
   1787  return true;
   1788 }
   1789 
   1790 /// Get the script context of global option at index opt_idx.
   1791 sctx_T *get_option_sctx(OptIndex opt_idx)
   1792 {
   1793  assert(opt_idx != kOptInvalid);
   1794  return &options[opt_idx].script_ctx;
   1795 }
   1796 
   1797 /// Set the script_ctx for an option, taking care of setting the buffer- or
   1798 /// window-local value.
   1799 void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx)
   1800 {
   1801  bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
   1802 
   1803  // Modeline already has the line number set.
   1804  if (!(opt_flags & OPT_MODELINE)) {
   1805    script_ctx.sc_lnum += SOURCING_LNUM;
   1806  }
   1807  nlua_set_sctx(&script_ctx);
   1808 
   1809  // Remember where the option was set.  For local options need to do that
   1810  // in the buffer or window structure.
   1811  if (both || (opt_flags & OPT_GLOBAL) || option_is_global_only(opt_idx)) {
   1812    options[opt_idx].script_ctx = script_ctx;
   1813  }
   1814  if (both || (opt_flags & OPT_LOCAL)) {
   1815    if (option_has_scope(opt_idx, kOptScopeBuf)) {
   1816      curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)] = script_ctx;
   1817    } else if ((option_has_scope(opt_idx, kOptScopeWin))) {
   1818      curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = script_ctx;
   1819      if (both) {
   1820        // also setting the "all buffers" value
   1821        curwin->w_allbuf_opt.wo_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = script_ctx;
   1822      }
   1823    }
   1824  }
   1825 }
   1826 
   1827 /// Apply the OptionSet autocommand.
   1828 static void apply_optionset_autocmd(OptIndex opt_idx, int opt_flags, OptVal oldval, OptVal oldval_g,
   1829                                    OptVal oldval_l, OptVal newval, const char *errmsg)
   1830 {
   1831  // Don't do this while starting up, failure or recursively.
   1832  if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
   1833    return;
   1834  }
   1835 
   1836  char buf_type[7];
   1837  typval_T oldval_tv = optval_as_tv(oldval, false);
   1838  typval_T oldval_g_tv = optval_as_tv(oldval_g, false);
   1839  typval_T oldval_l_tv = optval_as_tv(oldval_l, false);
   1840  typval_T newval_tv = optval_as_tv(newval, false);
   1841 
   1842  vim_snprintf(buf_type, sizeof(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global");
   1843  set_vim_var_tv(VV_OPTION_NEW, &newval_tv);
   1844  set_vim_var_tv(VV_OPTION_OLD, &oldval_tv);
   1845  set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
   1846  if (opt_flags & OPT_LOCAL) {
   1847    set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
   1848    set_vim_var_tv(VV_OPTION_OLDLOCAL, &oldval_tv);
   1849  }
   1850  if (opt_flags & OPT_GLOBAL) {
   1851    set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
   1852    set_vim_var_tv(VV_OPTION_OLDGLOBAL, &oldval_tv);
   1853  }
   1854  if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
   1855    set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
   1856    set_vim_var_tv(VV_OPTION_OLDLOCAL, &oldval_l_tv);
   1857    set_vim_var_tv(VV_OPTION_OLDGLOBAL, &oldval_g_tv);
   1858  }
   1859  if (opt_flags & OPT_MODELINE) {
   1860    set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
   1861    set_vim_var_tv(VV_OPTION_OLDLOCAL, &oldval_tv);
   1862  }
   1863  apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
   1864  reset_v_option_vars();
   1865 }
   1866 
   1867 /// Process the updated 'arabic' option value.
   1868 static const char *did_set_arabic(optset_T *args)
   1869 {
   1870  win_T *win = (win_T *)args->os_win;
   1871  const char *errmsg = NULL;
   1872 
   1873  if (win->w_p_arab) {
   1874    // 'arabic' is set, handle various sub-settings.
   1875    if (!p_tbidi) {
   1876      // set rightleft mode
   1877      if (!win->w_p_rl) {
   1878        win->w_p_rl = true;
   1879        changed_window_setting(win);
   1880      }
   1881 
   1882      // Enable Arabic shaping (major part of what Arabic requires)
   1883      if (!p_arshape) {
   1884        p_arshape = true;
   1885        redraw_all_later(UPD_NOT_VALID);
   1886      }
   1887    }
   1888 
   1889    // Arabic requires a utf-8 encoding, inform the user if it's not
   1890    // set.
   1891    if (strcmp(p_enc, "utf-8") != 0) {
   1892      static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'");
   1893 
   1894      msg_source(HLF_W);
   1895      msg(_(w_arabic), HLF_W);
   1896      set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
   1897    }
   1898 
   1899    // set 'delcombine'
   1900    p_deco = true;
   1901 
   1902    // Force-set the necessary keymap for arabic.
   1903    errmsg = set_option_value(kOptKeymap, STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL);
   1904  } else {
   1905    // 'arabic' is reset, handle various sub-settings.
   1906    if (!p_tbidi) {
   1907      // reset rightleft mode
   1908      if (win->w_p_rl) {
   1909        win->w_p_rl = false;
   1910        changed_window_setting(win);
   1911      }
   1912 
   1913      // 'arabicshape' isn't reset, it is a global option and
   1914      // another window may still need it "on".
   1915    }
   1916 
   1917    // 'delcombine' isn't reset, it is a global option and another
   1918    // window may still want it "on".
   1919 
   1920    // Revert to the default keymap
   1921    win->w_buffer->b_p_iminsert = B_IMODE_NONE;
   1922    win->w_buffer->b_p_imsearch = B_IMODE_USE_INSERT;
   1923  }
   1924 
   1925  return errmsg;
   1926 }
   1927 
   1928 /// Process the updated 'autochdir' option value.
   1929 static const char *did_set_autochdir(optset_T *args FUNC_ATTR_UNUSED)
   1930 {
   1931  // Change directories when the 'acd' option is set now.
   1932  do_autochdir();
   1933  return NULL;
   1934 }
   1935 
   1936 /// Process the updated 'binary' option value.
   1937 static const char *did_set_binary(optset_T *args)
   1938 {
   1939  buf_T *buf = (buf_T *)args->os_buf;
   1940 
   1941  // when 'bin' is set also set some other options
   1942  set_options_bin((int)args->os_oldval.boolean, buf->b_p_bin, args->os_flags);
   1943  redraw_titles();
   1944 
   1945  return NULL;
   1946 }
   1947 
   1948 /// Process the updated 'buflisted' option value.
   1949 static const char *did_set_buflisted(optset_T *args)
   1950 {
   1951  buf_T *buf = (buf_T *)args->os_buf;
   1952 
   1953  // when 'buflisted' changes, trigger autocommands
   1954  if (args->os_oldval.boolean != buf->b_p_bl) {
   1955    apply_autocmds(buf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE,
   1956                   NULL, NULL, true, buf);
   1957  }
   1958  return NULL;
   1959 }
   1960 
   1961 /// Process the new 'cmdheight' option value.
   1962 static const char *did_set_cmdheight(optset_T *args)
   1963 {
   1964  OptInt old_value = args->os_oldval.number;
   1965 
   1966  if (p_ch > Rows - min_rows(curtab) + 1) {
   1967    p_ch = Rows - min_rows(curtab) + 1;
   1968  }
   1969 
   1970  // if p_ch changed value, change the command line height
   1971  // Only compute the new window layout when startup has been
   1972  // completed. Otherwise the frame sizes may be wrong.
   1973  if ((p_ch != old_value
   1974       || tabline_height() + global_stl_height() + topframe->fr_height != Rows - p_ch)
   1975      && full_screen) {
   1976    command_height();
   1977  }
   1978 
   1979  return NULL;
   1980 }
   1981 
   1982 /// Process the updated 'diff' option value.
   1983 static const char *did_set_diff(optset_T *args)
   1984 {
   1985  win_T *win = (win_T *)args->os_win;
   1986  // May add or remove the buffer from the list of diff buffers.
   1987  diff_buf_adjust(win);
   1988  if (foldmethodIsDiff(win)) {
   1989    foldUpdateAll(win);
   1990  }
   1991  return NULL;
   1992 }
   1993 
   1994 /// Process the updated 'endoffile' or 'endofline' or 'fixendofline' or 'bomb'
   1995 /// option value.
   1996 static const char *did_set_eof_eol_fixeol_bomb(optset_T *args FUNC_ATTR_UNUSED)
   1997 {
   1998  // redraw the window title and tab page text
   1999  redraw_titles();
   2000  return NULL;
   2001 }
   2002 
   2003 /// Process the updated 'equalalways' option value.
   2004 static const char *did_set_equalalways(optset_T *args)
   2005 {
   2006  win_T *win = (win_T *)args->os_win;
   2007  if (p_ea && !args->os_oldval.boolean) {
   2008    win_equal(win, false, 0);
   2009  }
   2010 
   2011  return NULL;
   2012 }
   2013 
   2014 /// Process the new 'foldlevel' option value.
   2015 static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED)
   2016 {
   2017  newFoldLevel();
   2018  return NULL;
   2019 }
   2020 
   2021 /// Process the new 'foldminlines' option value.
   2022 static const char *did_set_foldminlines(optset_T *args)
   2023 {
   2024  win_T *win = (win_T *)args->os_win;
   2025  foldUpdateAll(win);
   2026  return NULL;
   2027 }
   2028 
   2029 /// Process the new 'foldnestmax' option value.
   2030 static const char *did_set_foldnestmax(optset_T *args)
   2031 {
   2032  win_T *win = (win_T *)args->os_win;
   2033  if (foldmethodIsSyntax(win) || foldmethodIsIndent(win)) {
   2034    foldUpdateAll(win);
   2035  }
   2036  return NULL;
   2037 }
   2038 
   2039 /// Process the new 'helpheight' option value.
   2040 static const char *did_set_helpheight(optset_T *args)
   2041 {
   2042  // Change window height NOW
   2043  if (!ONE_WINDOW) {
   2044    if (curbuf->b_help && curwin->w_height < p_hh) {
   2045      win_setheight((int)p_hh);
   2046    }
   2047  }
   2048 
   2049  return NULL;
   2050 }
   2051 
   2052 /// Process the updated 'hlsearch' option value.
   2053 static const char *did_set_hlsearch(optset_T *args FUNC_ATTR_UNUSED)
   2054 {
   2055  // when 'hlsearch' is set or reset: reset no_hlsearch
   2056  set_no_hlsearch(false);
   2057  return NULL;
   2058 }
   2059 
   2060 /// Process the updated 'ignorecase' option value.
   2061 static const char *did_set_ignorecase(optset_T *args FUNC_ATTR_UNUSED)
   2062 {
   2063  // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
   2064  if (p_hls) {
   2065    redraw_all_later(UPD_SOME_VALID);
   2066  }
   2067  return NULL;
   2068 }
   2069 
   2070 /// Process the new 'iminset' option value.
   2071 static const char *did_set_iminsert(optset_T *args FUNC_ATTR_UNUSED)
   2072 {
   2073  showmode();
   2074  // Show/unshow value of 'keymap' in status lines.
   2075  status_redraw_curbuf();
   2076 
   2077  return NULL;
   2078 }
   2079 
   2080 /// Process the updated 'langnoremap' option value.
   2081 static const char *did_set_langnoremap(optset_T *args FUNC_ATTR_UNUSED)
   2082 {
   2083  // 'langnoremap' -> !'langremap'
   2084  p_lrm = !p_lnr;
   2085  return NULL;
   2086 }
   2087 
   2088 /// Process the updated 'langremap' option value.
   2089 static const char *did_set_langremap(optset_T *args FUNC_ATTR_UNUSED)
   2090 {
   2091  // 'langremap' -> !'langnoremap'
   2092  p_lnr = !p_lrm;
   2093  return NULL;
   2094 }
   2095 
   2096 /// Process the new 'laststatus' option value.
   2097 static const char *did_set_laststatus(optset_T *args)
   2098 {
   2099  OptInt old_value = args->os_oldval.number;
   2100  OptInt value = args->os_newval.number;
   2101 
   2102  // When switching to global statusline, decrease topframe height
   2103  // Also clear the cmdline to remove the ruler if there is one
   2104  if (value == 3 && old_value != 3) {
   2105    frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false, false);
   2106    win_comp_pos();
   2107    clear_cmdline = true;
   2108  }
   2109  // When switching from global statusline, increase height of topframe by STATUS_HEIGHT
   2110  // in order to to re-add the space that was previously taken by the global statusline
   2111  if (old_value == 3 && value != 3) {
   2112    frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false, false);
   2113    win_comp_pos();
   2114  }
   2115 
   2116  status_redraw_curbuf();
   2117  last_status(false);  // (re)set last window status line.
   2118  win_float_update_statusline();
   2119  return NULL;
   2120 }
   2121 
   2122 /// Process the updated 'lines' or 'columns' option value.
   2123 static const char *did_set_lines_or_columns(optset_T *args)
   2124 {
   2125  // If the screen (shell) height has been changed, assume it is the
   2126  // physical screenheight.
   2127  if (p_lines != Rows || p_columns != Columns) {
   2128    // Changing the screen size is not allowed while updating the screen.
   2129    if (updating_screen) {
   2130      OptVal oldval = (OptVal){ .type = kOptValTypeNumber, .data = args->os_oldval };
   2131      set_option_varp(args->os_idx, args->os_varp, oldval, false);
   2132    } else if (full_screen) {
   2133      screen_resize((int)p_columns, (int)p_lines);
   2134    } else {
   2135      // TODO(bfredl): is this branch ever needed?
   2136      // Postpone the resizing; check the size and cmdline position for
   2137      // messages.
   2138      Rows = (int)p_lines;
   2139      Columns = (int)p_columns;
   2140      check_screensize();
   2141      int new_row = (int)(Rows - MAX(p_ch, 1));
   2142      if (cmdline_row > new_row && Rows > p_ch) {
   2143        assert(p_ch >= 0 && new_row <= INT_MAX);
   2144        cmdline_row = new_row;
   2145      }
   2146    }
   2147    if (p_window >= Rows || !option_was_set(kOptWindow)) {
   2148      p_window = Rows - 1;
   2149    }
   2150  }
   2151 
   2152  // Adjust 'scrolljump' if needed.
   2153  if (p_sj >= Rows && full_screen) {
   2154    p_sj = Rows / 2;
   2155  }
   2156 
   2157  return NULL;
   2158 }
   2159 
   2160 /// Process the updated 'lisp' option value.
   2161 static const char *did_set_lisp(optset_T *args)
   2162 {
   2163  buf_T *buf = (buf_T *)args->os_buf;
   2164  // When 'lisp' option changes include/exclude '-' in keyword characters.
   2165  buf_init_chartab(buf, false);          // ignore errors
   2166  return NULL;
   2167 }
   2168 
   2169 /// Process the updated 'modifiable' option value.
   2170 static const char *did_set_modifiable(optset_T *args FUNC_ATTR_UNUSED)
   2171 {
   2172  // when 'modifiable' is changed, redraw the window title
   2173  redraw_titles();
   2174 
   2175  return NULL;
   2176 }
   2177 
   2178 /// Process the updated 'modified' option value.
   2179 static const char *did_set_modified(optset_T *args)
   2180 {
   2181  buf_T *buf = (buf_T *)args->os_buf;
   2182  if (!args->os_newval.boolean) {
   2183    save_file_ff(buf);  // Buffer is unchanged
   2184  }
   2185  redraw_titles();
   2186  buf->b_modified_was_set = (int)args->os_newval.boolean;
   2187  return NULL;
   2188 }
   2189 
   2190 /// Process the updated 'number' or 'relativenumber' option value.
   2191 static const char *did_set_number_relativenumber(optset_T *args)
   2192 {
   2193  win_T *win = (win_T *)args->os_win;
   2194  if (*win->w_p_stc != NUL) {
   2195    // When 'relativenumber'/'number' is changed and 'statuscolumn' is set, reset width.
   2196    win->w_nrwidth_line_count = 0;
   2197  }
   2198  check_signcolumn(NULL, win);
   2199  return NULL;
   2200 }
   2201 
   2202 /// Process the new 'numberwidth' option value.
   2203 static const char *did_set_numberwidth(optset_T *args)
   2204 {
   2205  win_T *win = (win_T *)args->os_win;
   2206  win->w_nrwidth_line_count = 0;  // trigger a redraw
   2207 
   2208  return NULL;
   2209 }
   2210 
   2211 /// Process the updated 'paste' option value.
   2212 static const char *did_set_paste(optset_T *args FUNC_ATTR_UNUSED)
   2213 {
   2214  static int old_p_paste = false;
   2215  static int save_sm = 0;
   2216  static int save_sta = 0;
   2217  static int save_ru = 0;
   2218  static int save_ri = 0;
   2219 
   2220  if (p_paste) {
   2221    // Paste switched from off to on.
   2222    // Save the current values, so they can be restored later.
   2223    if (!old_p_paste) {
   2224      // save options for each buffer
   2225      FOR_ALL_BUFFERS(buf) {
   2226        buf->b_p_tw_nopaste = buf->b_p_tw;
   2227        buf->b_p_wm_nopaste = buf->b_p_wm;
   2228        buf->b_p_sts_nopaste = buf->b_p_sts;
   2229        buf->b_p_ai_nopaste = buf->b_p_ai;
   2230        buf->b_p_et_nopaste = buf->b_p_et;
   2231        if (buf->b_p_vsts_nopaste) {
   2232          xfree(buf->b_p_vsts_nopaste);
   2233        }
   2234        buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_string_option
   2235                                ? xstrdup(buf->b_p_vsts)
   2236                                : NULL;
   2237      }
   2238 
   2239      // save global options
   2240      save_sm = p_sm;
   2241      save_sta = p_sta;
   2242      save_ru = p_ru;
   2243      save_ri = p_ri;
   2244      // save global values for local buffer options
   2245      p_ai_nopaste = p_ai;
   2246      p_et_nopaste = p_et;
   2247      p_sts_nopaste = p_sts;
   2248      p_tw_nopaste = p_tw;
   2249      p_wm_nopaste = p_wm;
   2250      if (p_vsts_nopaste) {
   2251        xfree(p_vsts_nopaste);
   2252      }
   2253      p_vsts_nopaste = p_vsts && p_vsts != empty_string_option ? xstrdup(p_vsts) : NULL;
   2254    }
   2255 
   2256    // Always set the option values, also when 'paste' is set when it is
   2257    // already on.
   2258    // set options for each buffer
   2259    FOR_ALL_BUFFERS(buf) {
   2260      buf->b_p_tw = 0;              // textwidth is 0
   2261      buf->b_p_wm = 0;              // wrapmargin is 0
   2262      buf->b_p_sts = 0;             // softtabstop is 0
   2263      buf->b_p_ai = 0;              // no auto-indent
   2264      buf->b_p_et = 0;              // no expandtab
   2265      if (buf->b_p_vsts) {
   2266        free_string_option(buf->b_p_vsts);
   2267      }
   2268      buf->b_p_vsts = empty_string_option;
   2269      XFREE_CLEAR(buf->b_p_vsts_array);
   2270    }
   2271 
   2272    // set global options
   2273    p_sm = 0;                       // no showmatch
   2274    p_sta = 0;                      // no smarttab
   2275    if (p_ru) {
   2276      status_redraw_all();          // redraw to remove the ruler
   2277    }
   2278    p_ru = 0;                       // no ruler
   2279    p_ri = 0;                       // no reverse insert
   2280    // set global values for local buffer options
   2281    p_tw = 0;
   2282    p_wm = 0;
   2283    p_sts = 0;
   2284    p_ai = 0;
   2285    p_et = 0;
   2286    if (p_vsts) {
   2287      free_string_option(p_vsts);
   2288    }
   2289    p_vsts = empty_string_option;
   2290  } else if (old_p_paste) {
   2291    // Paste switched from on to off: Restore saved values.
   2292 
   2293    // restore options for each buffer
   2294    FOR_ALL_BUFFERS(buf) {
   2295      buf->b_p_tw = buf->b_p_tw_nopaste;
   2296      buf->b_p_wm = buf->b_p_wm_nopaste;
   2297      buf->b_p_sts = buf->b_p_sts_nopaste;
   2298      buf->b_p_ai = buf->b_p_ai_nopaste;
   2299      buf->b_p_et = buf->b_p_et_nopaste;
   2300      if (buf->b_p_vsts) {
   2301        free_string_option(buf->b_p_vsts);
   2302      }
   2303      buf->b_p_vsts = buf->b_p_vsts_nopaste ? xstrdup(buf->b_p_vsts_nopaste) : empty_string_option;
   2304      xfree(buf->b_p_vsts_array);
   2305      if (buf->b_p_vsts && buf->b_p_vsts != empty_string_option) {
   2306        tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
   2307      } else {
   2308        buf->b_p_vsts_array = NULL;
   2309      }
   2310    }
   2311 
   2312    // restore global options
   2313    p_sm = save_sm;
   2314    p_sta = save_sta;
   2315    if (p_ru != save_ru) {
   2316      status_redraw_all();          // redraw to draw the ruler
   2317    }
   2318    p_ru = save_ru;
   2319    p_ri = save_ri;
   2320    // set global values for local buffer options
   2321    p_ai = p_ai_nopaste;
   2322    p_et = p_et_nopaste;
   2323    p_sts = p_sts_nopaste;
   2324    p_tw = p_tw_nopaste;
   2325    p_wm = p_wm_nopaste;
   2326    if (p_vsts) {
   2327      free_string_option(p_vsts);
   2328    }
   2329    p_vsts = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : empty_string_option;
   2330  }
   2331 
   2332  old_p_paste = p_paste;
   2333 
   2334  // Remember where the dependent options were reset
   2335  didset_options_sctx((OPT_LOCAL | OPT_GLOBAL), p_paste_dep_opts);
   2336 
   2337  return NULL;
   2338 }
   2339 
   2340 /// Process the updated 'previewwindow' option value.
   2341 static const char *did_set_previewwindow(optset_T *args)
   2342 {
   2343  win_T *win = (win_T *)args->os_win;
   2344 
   2345  if (!win->w_p_pvw) {
   2346    return NULL;
   2347  }
   2348 
   2349  // There can be only one window with 'previewwindow' set.
   2350  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   2351    if (wp->w_p_pvw && wp != win) {
   2352      win->w_p_pvw = false;
   2353      return e_preview_window_already_exists;
   2354    }
   2355  }
   2356 
   2357  return NULL;
   2358 }
   2359 
   2360 /// Process the new 'pumblend' option value.
   2361 static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED)
   2362 {
   2363  hl_invalidate_blends();
   2364  if (pum_drawn()) {
   2365    pum_redraw();
   2366  }
   2367 
   2368  return NULL;
   2369 }
   2370 
   2371 /// Process the updated 'readonly' option value.
   2372 static const char *did_set_readonly(optset_T *args)
   2373 {
   2374  buf_T *buf = (buf_T *)args->os_buf;
   2375 
   2376  // when 'readonly' is reset globally, also reset readonlymode
   2377  if (!buf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) {
   2378    readonlymode = false;
   2379  }
   2380 
   2381  // when 'readonly' is set may give W10 again
   2382  if (buf->b_p_ro) {
   2383    buf->b_did_warn = false;
   2384  }
   2385 
   2386  redraw_titles();
   2387 
   2388  return NULL;
   2389 }
   2390 
   2391 /// Process the new 'scrollback' option value.
   2392 static const char *did_set_scrollback(optset_T *args)
   2393 {
   2394  buf_T *buf = (buf_T *)args->os_buf;
   2395  OptInt old_value = args->os_oldval.number;
   2396  OptInt value = args->os_newval.number;
   2397 
   2398  if (buf->terminal && value < old_value) {
   2399    // Force the scrollback to take immediate effect only when decreasing it.
   2400    on_scrollback_option_changed(buf->terminal);
   2401  }
   2402  return NULL;
   2403 }
   2404 
   2405 /// Process the updated 'scrollbind' option value.
   2406 static const char *did_set_scrollbind(optset_T *args)
   2407 {
   2408  win_T *win = (win_T *)args->os_win;
   2409 
   2410  // when 'scrollbind' is set: snapshot the current position to avoid a jump
   2411  // at the end of normal_cmd()
   2412  if (!win->w_p_scb) {
   2413    return NULL;
   2414  }
   2415  do_check_scrollbind(false);
   2416  win->w_scbind_pos = get_vtopline(win);
   2417  return NULL;
   2418 }
   2419 
   2420 #ifdef BACKSLASH_IN_FILENAME
   2421 /// Process the updated 'shellslash' option value.
   2422 static const char *did_set_shellslash(optset_T *args FUNC_ATTR_UNUSED)
   2423 {
   2424  if (p_ssl) {
   2425    psepc = '/';
   2426    psepcN = '\\';
   2427    pseps[0] = '/';
   2428  } else {
   2429    psepc = '\\';
   2430    psepcN = '/';
   2431    pseps[0] = '\\';
   2432  }
   2433 
   2434  // need to adjust the file name arguments and buffer names.
   2435  buflist_slash_adjust();
   2436  alist_slash_adjust();
   2437  scriptnames_slash_adjust();
   2438  return NULL;
   2439 }
   2440 #endif
   2441 
   2442 /// Process the new 'shiftwidth' or the 'tabstop' option value.
   2443 static const char *did_set_shiftwidth_tabstop(optset_T *args)
   2444 {
   2445  buf_T *buf = (buf_T *)args->os_buf;
   2446  win_T *win = (win_T *)args->os_win;
   2447  OptInt *pp = (OptInt *)args->os_varp;
   2448 
   2449  if (foldmethodIsIndent(win)) {
   2450    foldUpdateAll(win);
   2451  }
   2452  // When 'shiftwidth' changes, or it's zero and 'tabstop' changes:
   2453  // parse 'cinoptions'.
   2454  if (pp == &buf->b_p_sw || buf->b_p_sw == 0) {
   2455    parse_cino(buf);
   2456  }
   2457 
   2458  return NULL;
   2459 }
   2460 
   2461 /// Process the new 'showtabline' option value.
   2462 static const char *did_set_showtabline(optset_T *args FUNC_ATTR_UNUSED)
   2463 {
   2464  // (re)set tab page line
   2465  win_new_screen_rows();  // recompute window positions and heights
   2466  return NULL;
   2467 }
   2468 
   2469 /// Process the updated 'smoothscroll' option value.
   2470 static const char *did_set_smoothscroll(optset_T *args FUNC_ATTR_UNUSED)
   2471 {
   2472  win_T *win = (win_T *)args->os_win;
   2473  if (!win->w_p_sms) {
   2474    win->w_skipcol = 0;
   2475  }
   2476 
   2477  return NULL;
   2478 }
   2479 
   2480 /// Process the updated 'spell' option value.
   2481 static const char *did_set_spell(optset_T *args)
   2482 {
   2483  win_T *win = (win_T *)args->os_win;
   2484  if (win->w_p_spell) {
   2485    return parse_spelllang(win);
   2486  }
   2487 
   2488  return NULL;
   2489 }
   2490 
   2491 /// Process the updated 'swapfile' option value.
   2492 static const char *did_set_swapfile(optset_T *args)
   2493 {
   2494  buf_T *buf = (buf_T *)args->os_buf;
   2495  // when 'swf' is set, create swapfile, when reset remove swapfile
   2496  if (buf->b_p_swf && p_uc) {
   2497    ml_open_file(buf);                     // create the swap file
   2498  } else {
   2499    // no need to reset buf->b_may_swap, ml_open_file() will check buf->b_p_swf
   2500    mf_close_file(buf, true);              // remove the swap file
   2501  }
   2502  return NULL;
   2503 }
   2504 
   2505 /// Process the new 'textwidth' option value.
   2506 static const char *did_set_textwidth(optset_T *args FUNC_ATTR_UNUSED)
   2507 {
   2508  FOR_ALL_TAB_WINDOWS(tp, wp) {
   2509    check_colorcolumn(NULL, wp);
   2510  }
   2511 
   2512  return NULL;
   2513 }
   2514 
   2515 /// Process the updated 'title' or the 'icon' option value.
   2516 static const char *did_set_title_icon(optset_T *args FUNC_ATTR_UNUSED)
   2517 {
   2518  // when 'title' changed, may need to change the title; same for 'icon'
   2519  did_set_title();
   2520  return NULL;
   2521 }
   2522 
   2523 /// Process the new 'titlelen' option value.
   2524 static const char *did_set_titlelen(optset_T *args)
   2525 {
   2526  OptInt old_value = args->os_oldval.number;
   2527 
   2528  // if 'titlelen' has changed, redraw the title
   2529  if (starting != NO_SCREEN && old_value != p_titlelen) {
   2530    need_maketitle = true;
   2531  }
   2532 
   2533  return NULL;
   2534 }
   2535 
   2536 /// Process the updated 'undofile' option value.
   2537 static const char *did_set_undofile(optset_T *args)
   2538 {
   2539  buf_T *buf = (buf_T *)args->os_buf;
   2540 
   2541  // Only take action when the option was set.
   2542  if (!buf->b_p_udf && !p_udf) {
   2543    return NULL;
   2544  }
   2545 
   2546  // When reset we do not delete the undo file, the option may be set again
   2547  // without making any changes in between.
   2548  uint8_t hash[UNDO_HASH_SIZE];
   2549 
   2550  FOR_ALL_BUFFERS(bp) {
   2551    // When 'undofile' is set globally: for every buffer, otherwise
   2552    // only for the current buffer: Try to read in the undofile,
   2553    // if one exists, the buffer wasn't changed and the buffer was
   2554    // loaded
   2555    if ((buf == bp
   2556         || (args->os_flags & OPT_GLOBAL) || args->os_flags == 0)
   2557        && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) {
   2558      u_compute_hash(bp, hash);
   2559      u_read_undo(NULL, hash, bp->b_fname);
   2560    }
   2561  }
   2562 
   2563  return NULL;
   2564 }
   2565 
   2566 /// Process the new global 'undolevels' option value.
   2567 const char *did_set_global_undolevels(OptInt value, OptInt old_value)
   2568 {
   2569  // sync undo before 'undolevels' changes
   2570  // use the old value, otherwise u_sync() may not work properly
   2571  p_ul = old_value;
   2572  u_sync(true);
   2573  p_ul = value;
   2574  return NULL;
   2575 }
   2576 
   2577 /// Process the new buffer local 'undolevels' option value.
   2578 const char *did_set_buflocal_undolevels(buf_T *buf, OptInt value, OptInt old_value)
   2579 {
   2580  // use the old value, otherwise u_sync() may not work properly
   2581  buf->b_p_ul = old_value;
   2582  u_sync(true);
   2583  buf->b_p_ul = value;
   2584  return NULL;
   2585 }
   2586 
   2587 /// Process the new 'undolevels' option value.
   2588 static const char *did_set_undolevels(optset_T *args)
   2589 {
   2590  buf_T *buf = (buf_T *)args->os_buf;
   2591  OptInt *pp = (OptInt *)args->os_varp;
   2592 
   2593  if (pp == &p_ul) {                  // global 'undolevels'
   2594    did_set_global_undolevels(args->os_newval.number, args->os_oldval.number);
   2595  } else if (pp == &buf->b_p_ul) {      // buffer local 'undolevels'
   2596    did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number);
   2597  }
   2598 
   2599  return NULL;
   2600 }
   2601 
   2602 /// Process the new 'updatecount' option value.
   2603 static const char *did_set_updatecount(optset_T *args)
   2604 {
   2605  OptInt old_value = args->os_oldval.number;
   2606 
   2607  // when 'updatecount' changes from zero to non-zero, open swap files
   2608  if (p_uc && !old_value) {
   2609    ml_open_files();
   2610  }
   2611 
   2612  return NULL;
   2613 }
   2614 
   2615 /// Process the new 'wildchar' / 'wildcharm' option value.
   2616 static const char *did_set_wildchar(optset_T *args)
   2617 {
   2618  OptInt c = *(OptInt *)args->os_varp;
   2619 
   2620  // Don't allow key values that wouldn't work as wildchar.
   2621  if (c == Ctrl_C || c == '\n' || c == '\r' || c == K_KENTER) {
   2622    return e_invarg;
   2623  }
   2624 
   2625  return NULL;
   2626 }
   2627 
   2628 /// Process the new 'winblend' option value.
   2629 static const char *did_set_winblend(optset_T *args)
   2630 {
   2631  win_T *win = (win_T *)args->os_win;
   2632  OptInt old_value = args->os_oldval.number;
   2633  OptInt value = args->os_newval.number;
   2634 
   2635  if (value != old_value) {
   2636    win->w_p_winbl = MAX(MIN(win->w_p_winbl, 100), 0);
   2637    win->w_hl_needs_update = true;
   2638    check_blending(win);
   2639  }
   2640 
   2641  return NULL;
   2642 }
   2643 
   2644 /// Process the new 'window' option value.
   2645 static const char *did_set_window(optset_T *args FUNC_ATTR_UNUSED)
   2646 {
   2647  if (p_window < 1) {
   2648    p_window = Rows - 1;
   2649  } else if (p_window >= Rows) {
   2650    p_window = Rows - 1;
   2651  }
   2652  return NULL;
   2653 }
   2654 
   2655 /// Process the new 'winheight' value.
   2656 static const char *did_set_winheight(optset_T *args)
   2657 {
   2658  // Change window height NOW
   2659  if (!ONE_WINDOW) {
   2660    if (curwin->w_height < p_wh) {
   2661      win_setheight((int)p_wh);
   2662    }
   2663  }
   2664 
   2665  return NULL;
   2666 }
   2667 
   2668 /// Process the new 'winwidth' option value.
   2669 static const char *did_set_winwidth(optset_T *args)
   2670 {
   2671  if (!ONE_WINDOW && curwin->w_width < p_wiw) {
   2672    win_setwidth((int)p_wiw);
   2673  }
   2674  return NULL;
   2675 }
   2676 
   2677 /// Process the updated 'wrap' option value.
   2678 static const char *did_set_wrap(optset_T *args)
   2679 {
   2680  win_T *win = (win_T *)args->os_win;
   2681  // Set w_leftcol or w_skipcol to zero.
   2682  if (win->w_p_wrap) {
   2683    win->w_leftcol = 0;
   2684  } else {
   2685    win->w_skipcol = 0;
   2686  }
   2687 
   2688  return NULL;
   2689 }
   2690 
   2691 /// Process the new 'chistory' or 'lhistory' option value. 'chistory' will
   2692 /// be used if args->os_varp is the same as p_chi, else 'lhistory'.
   2693 static const char *did_set_xhistory(optset_T *args)
   2694 {
   2695  win_T *win = (win_T *)args->os_win;
   2696  bool is_p_chi = (OptInt *)args->os_varp == &p_chi;
   2697  OptInt *arg = is_p_chi ? &p_chi : (OptInt *)args->os_varp;
   2698 
   2699  if (is_p_chi) {
   2700    qf_resize_stack((int)(*arg));
   2701  } else {
   2702    ll_resize_stack(win, (int)(*arg));
   2703  }
   2704 
   2705  return NULL;
   2706 }
   2707 
   2708 // When 'syntax' is set, load the syntax of that name
   2709 static void do_syntax_autocmd(buf_T *buf, bool value_changed)
   2710 {
   2711  static int syn_recursive = 0;
   2712 
   2713  syn_recursive++;
   2714  buf->b_flags |= BF_SYN_SET;
   2715  // Only pass true for "force" when the value changed or not used
   2716  // recursively, to avoid endless recurrence.
   2717  apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname,
   2718                 value_changed || syn_recursive == 1, buf);
   2719  syn_recursive--;
   2720 }
   2721 
   2722 static void do_spelllang_source(win_T *win)
   2723 {
   2724  char fname[200];
   2725  char *q = win->w_s->b_p_spl;
   2726 
   2727  // Skip the first name if it is "cjk".
   2728  if (strncmp(q, "cjk,", 4) == 0) {
   2729    q += 4;
   2730  }
   2731 
   2732  // Source the spell/LANG.{vim,lua} in 'runtimepath'.
   2733  // They could set 'spellcapcheck' depending on the language.
   2734  // Use the first name in 'spelllang' up to '_region' or
   2735  // '.encoding'.
   2736  char *p;
   2737  for (p = q; *p != NUL; p++) {
   2738    if (!ASCII_ISALNUM(*p) && *p != '-') {
   2739      break;
   2740    }
   2741  }
   2742  if (p > q) {
   2743    vim_snprintf(fname, sizeof(fname), "spell/%.*s.*", (int)(p - q), q);
   2744    source_runtime_vim_lua(fname, DIP_ALL);
   2745  }
   2746 }
   2747 
   2748 /// Check the bounds of numeric options.
   2749 ///
   2750 /// @param          opt_idx    Index in options[] table. Must not be kOptInvalid.
   2751 /// @param[in,out]  newval     Pointer to new option value. Will be set to bound checked value.
   2752 /// @param[out]     errbuf     Buffer for error message. Cannot be NULL.
   2753 /// @param          errbuflen  Length of error buffer.
   2754 ///
   2755 /// @return Error message, if any.
   2756 static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, char *errbuf,
   2757                                           size_t errbuflen)
   2758  FUNC_ATTR_NONNULL_ARG(3)
   2759 {
   2760  const char *errmsg = NULL;
   2761 
   2762  switch (opt_idx) {
   2763  case kOptLines:
   2764    if (*newval < min_rows_for_all_tabpages() && full_screen) {
   2765      vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"),
   2766                   min_rows_for_all_tabpages());
   2767      errmsg = errbuf;
   2768      *newval = min_rows_for_all_tabpages();
   2769    }
   2770    // True max size is defined by check_screensize().
   2771    *newval = MIN(*newval, INT_MAX);
   2772    break;
   2773  case kOptColumns:
   2774    if (*newval < MIN_COLUMNS && full_screen) {
   2775      vim_snprintf(errbuf, errbuflen, _("E594: Need at least %d columns"), MIN_COLUMNS);
   2776      errmsg = errbuf;
   2777      *newval = MIN_COLUMNS;
   2778    }
   2779    // True max size is defined by check_screensize().
   2780    *newval = MIN(*newval, INT_MAX);
   2781    break;
   2782  case kOptPumblend:
   2783    *newval = MAX(MIN(*newval, 100), 0);
   2784    break;
   2785  case kOptScrolljump:
   2786    if ((*newval < -100 || *newval >= Rows) && full_screen) {
   2787      errmsg = e_scroll;
   2788      *newval = 1;
   2789    }
   2790    break;
   2791  case kOptScroll:
   2792    if ((*newval <= 0 || (*newval > curwin->w_view_height && curwin->w_view_height > 0))
   2793        && full_screen) {
   2794      if (*newval != 0) {
   2795        errmsg = e_scroll;
   2796      }
   2797      *newval = win_default_scroll(curwin);
   2798    }
   2799    break;
   2800  default:
   2801    break;
   2802  }
   2803 
   2804  return errmsg;
   2805 }
   2806 
   2807 /// Validate and bound check option value.
   2808 ///
   2809 /// @param          opt_idx    Index in options[] table. Must not be kOptInvalid.
   2810 /// @param[in,out]  newval     Pointer to new option value. Will be set to bound checked value.
   2811 /// @param[out]     errbuf     Buffer for error message. Cannot be NULL.
   2812 /// @param          errbuflen  Length of error buffer.
   2813 ///
   2814 /// @return Error message, if any.
   2815 static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *errbuf,
   2816                                       size_t errbuflen)
   2817 {
   2818  OptInt value = *newval;
   2819 
   2820  // Many number options assume their value is in the signed int range.
   2821  if (value < INT_MIN || value > INT_MAX) {
   2822    return e_invarg;
   2823  }
   2824 
   2825  // if you increase this, also increase SEARCH_STAT_BUF_LEN in search.c
   2826  enum { MAX_SEARCH_COUNT = 9999, };
   2827 
   2828  switch (opt_idx) {
   2829  case kOptHelpheight:
   2830  case kOptTitlelen:
   2831  case kOptUpdatecount:
   2832  case kOptReport:
   2833  case kOptUpdatetime:
   2834  case kOptSidescroll:
   2835  case kOptFoldlevel:
   2836  case kOptShiftwidth:
   2837  case kOptTextwidth:
   2838  case kOptWritedelay:
   2839  case kOptTimeoutlen:
   2840    if (value < 0) {
   2841      return e_positive;
   2842    }
   2843    break;
   2844  case kOptWinheight:
   2845    if (value < 1) {
   2846      return e_positive;
   2847    } else if (p_wmh > value) {
   2848      return e_winheight;
   2849    }
   2850    break;
   2851  case kOptWinminheight:
   2852    if (value < 0) {
   2853      return e_positive;
   2854    } else if (value > p_wh) {
   2855      return e_winheight;
   2856    }
   2857    break;
   2858  case kOptWinwidth:
   2859    if (value < 1) {
   2860      return e_positive;
   2861    } else if (p_wmw > value) {
   2862      return e_winwidth;
   2863    }
   2864    break;
   2865  case kOptWinminwidth:
   2866    if (value < 0) {
   2867      return e_positive;
   2868    } else if (value > p_wiw) {
   2869      return e_winwidth;
   2870    }
   2871    break;
   2872  case kOptMaxcombine:
   2873    *newval = MAX_MCO;
   2874    break;
   2875  case kOptCmdheight:
   2876    if (value < 0) {
   2877      return e_positive;
   2878    }
   2879    break;
   2880  case kOptHistory:
   2881    if (value < 0) {
   2882      return e_positive;
   2883    } else if (value > 10000) {
   2884      return e_invarg;
   2885    }
   2886    break;
   2887  case kOptPyxversion:
   2888    if (value == 0) {
   2889      *newval = 3;
   2890    } else if (value != 3) {
   2891      return e_invarg;
   2892    }
   2893    break;
   2894  case kOptRegexpengine:
   2895    if (value < 0 || value > 2) {
   2896      return e_invarg;
   2897    }
   2898    break;
   2899  case kOptScrolloff:
   2900    if (value < 0 && full_screen) {
   2901      return e_positive;
   2902    }
   2903    break;
   2904  case kOptSidescrolloff:
   2905    if (value < 0 && full_screen) {
   2906      return e_positive;
   2907    }
   2908    break;
   2909  case kOptCmdwinheight:
   2910    if (value < 1) {
   2911      return e_positive;
   2912    }
   2913    break;
   2914  case kOptConceallevel:
   2915    if (value < 0) {
   2916      return e_positive;
   2917    } else if (value > 3) {
   2918      return e_invarg;
   2919    }
   2920    break;
   2921  case kOptNumberwidth:
   2922    if (value < 1) {
   2923      return e_positive;
   2924    } else if (value > MAX_NUMBERWIDTH) {
   2925      return e_invarg;
   2926    }
   2927    break;
   2928  case kOptIminsert:
   2929    if (value < 0 || value > B_IMODE_LAST) {
   2930      return e_invarg;
   2931    }
   2932    break;
   2933  case kOptImsearch:
   2934    if (value < -1 || value > B_IMODE_LAST) {
   2935      return e_invarg;
   2936    }
   2937    break;
   2938  case kOptChannel:
   2939    return e_invarg;
   2940  case kOptScrollback:
   2941    if (value < -1 || value > SB_MAX) {
   2942      return e_invarg;
   2943    }
   2944    break;
   2945  case kOptTabstop:
   2946    if (value < 1) {
   2947      return e_positive;
   2948    } else if (value > TABSTOP_MAX) {
   2949      return e_invarg;
   2950    }
   2951    break;
   2952  case kOptChistory:
   2953  case kOptLhistory:
   2954    if (value < 1) {
   2955      return e_cannot_have_negative_or_zero_number_of_quickfix;
   2956    } else if (value > 100) {
   2957      return e_cannot_have_more_than_hundred_quickfix;
   2958    }
   2959    break;
   2960  case kOptMaxsearchcount:
   2961    if (value <= 0) {
   2962      return e_positive;
   2963    } else if (value > MAX_SEARCH_COUNT) {
   2964      return e_invarg;
   2965    }
   2966    break;
   2967  default:
   2968    break;
   2969  }
   2970 
   2971  return check_num_option_bounds(opt_idx, newval, errbuf, errbuflen);
   2972 }
   2973 
   2974 /// Called after an option changed: check if something needs to be redrawn.
   2975 void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags)
   2976 {
   2977  // Careful: kOptFlagRedrAll is a combination of other redraw flags
   2978  bool all = (flags & kOptFlagRedrAll) == kOptFlagRedrAll;
   2979 
   2980  if ((flags & kOptFlagRedrStat) || all) {  // mark all status lines and window bars dirty
   2981    status_redraw_all();
   2982  }
   2983 
   2984  if ((flags & kOptFlagRedrTabl) || all) {  // mark tablines dirty
   2985    redraw_tabline = true;
   2986  }
   2987 
   2988  if ((flags & kOptFlagRedrBuf) || (flags & kOptFlagRedrWin) || all) {
   2989    if (flags & kOptFlagHLOnly) {
   2990      redraw_later(win, UPD_NOT_VALID);
   2991    } else {
   2992      changed_window_setting(win);
   2993    }
   2994  }
   2995  if (flags & kOptFlagRedrBuf) {
   2996    redraw_buf_later(buf, UPD_NOT_VALID);
   2997  }
   2998  if (all) {
   2999    redraw_all_later(UPD_NOT_VALID);
   3000  }
   3001 }
   3002 
   3003 void check_redraw(uint32_t flags)
   3004 {
   3005  check_redraw_for(curbuf, curwin, flags);
   3006 }
   3007 
   3008 bool is_tty_option(const char *name)
   3009  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   3010 {
   3011  return find_tty_option_end(name) != NULL;
   3012 }
   3013 
   3014 /// Get value of TTY option.
   3015 ///
   3016 /// @param  name  Name of TTY option.
   3017 ///
   3018 /// @return [allocated] TTY option value. Returns NIL_OPTVAL if option isn't a TTY option.
   3019 OptVal get_tty_option(const char *name)
   3020 {
   3021  char *value = NULL;
   3022 
   3023  if (strequal(name, "t_Co")) {
   3024    if (t_colors <= 1) {
   3025      value = xstrdup("");
   3026    } else {
   3027      value = xmalloc(NUMBUFLEN);
   3028      snprintf(value, NUMBUFLEN, "%d", t_colors);
   3029    }
   3030  } else if (strequal(name, "term")) {
   3031    value = p_term ? xstrdup(p_term) : xstrdup("nvim");
   3032  } else if (strequal(name, "ttytype")) {
   3033    value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim");
   3034  } else if (is_tty_option(name)) {
   3035    // XXX: All other t_* options were removed in 3baba1e7.
   3036    value = xstrdup("");
   3037  }
   3038 
   3039  return value == NULL ? NIL_OPTVAL : CSTR_AS_OPTVAL(value);
   3040 }
   3041 
   3042 bool set_tty_option(const char *name, char *value)
   3043 {
   3044  if (strequal(name, "term")) {
   3045    if (p_term) {
   3046      xfree(p_term);
   3047    }
   3048    p_term = value;
   3049    return true;
   3050  }
   3051 
   3052  if (strequal(name, "ttytype")) {
   3053    if (p_ttytype) {
   3054      xfree(p_ttytype);
   3055    }
   3056    p_ttytype = value;
   3057    return true;
   3058  }
   3059 
   3060  return false;
   3061 }
   3062 
   3063 /// Find index for an option. Don't go beyond `len` length.
   3064 ///
   3065 /// @param[in]  name  Option name.
   3066 /// @param      len   Option name length.
   3067 ///
   3068 /// @return Option index or kOptInvalid if option was not found.
   3069 OptIndex find_option_len(const char *const name, size_t len)
   3070  FUNC_ATTR_NONNULL_ALL
   3071 {
   3072  int index = find_option_hash(name, len);
   3073  return index >= 0 ? option_hash_elems[index].opt_idx : kOptInvalid;
   3074 }
   3075 
   3076 /// Find index for an option.
   3077 ///
   3078 /// @param[in]  name  Option name.
   3079 ///
   3080 /// @return Option index or kOptInvalid if option was not found.
   3081 OptIndex find_option(const char *const name)
   3082  FUNC_ATTR_NONNULL_ALL
   3083 {
   3084  return find_option_len(name, strlen(name));
   3085 }
   3086 
   3087 /// Free an allocated OptVal.
   3088 void optval_free(OptVal o)
   3089 {
   3090  switch (o.type) {
   3091  case kOptValTypeNil:
   3092  case kOptValTypeBoolean:
   3093  case kOptValTypeNumber:
   3094    break;
   3095  case kOptValTypeString:
   3096    // Don't free empty string option
   3097    if (o.data.string.data != empty_string_option) {
   3098      api_free_string(o.data.string);
   3099    }
   3100    break;
   3101  }
   3102 }
   3103 
   3104 /// Copy an OptVal.
   3105 OptVal optval_copy(OptVal o)
   3106 {
   3107  switch (o.type) {
   3108  case kOptValTypeNil:
   3109  case kOptValTypeBoolean:
   3110  case kOptValTypeNumber:
   3111    return o;
   3112  case kOptValTypeString:
   3113    return STRING_OPTVAL(copy_string(o.data.string, NULL));
   3114  }
   3115  UNREACHABLE;
   3116 }
   3117 
   3118 /// Check if two option values are equal.
   3119 bool optval_equal(OptVal o1, OptVal o2)
   3120 {
   3121  if (o1.type != o2.type) {
   3122    return false;
   3123  }
   3124 
   3125  switch (o1.type) {
   3126  case kOptValTypeNil:
   3127    return true;
   3128  case kOptValTypeBoolean:
   3129    return o1.data.boolean == o2.data.boolean;
   3130  case kOptValTypeNumber:
   3131    return o1.data.number == o2.data.number;
   3132  case kOptValTypeString:
   3133    return o1.data.string.size == o2.data.string.size
   3134           && (o1.data.string.data == o2.data.string.data
   3135               || strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size));
   3136  }
   3137  UNREACHABLE;
   3138 }
   3139 
   3140 /// Get type of option.
   3141 static OptValType option_get_type(const OptIndex opt_idx)
   3142 {
   3143  return options[opt_idx].type;
   3144 }
   3145 
   3146 /// Create OptVal from var pointer.
   3147 ///
   3148 /// @param       opt_idx  Option index in options[] table.
   3149 /// @param[out]  varp     Pointer to option variable.
   3150 ///
   3151 /// @return Option value stored in varp.
   3152 OptVal optval_from_varp(OptIndex opt_idx, void *varp)
   3153  FUNC_ATTR_NONNULL_ARG(2)
   3154 {
   3155  // Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc'
   3156  // changed.
   3157  if ((int *)varp == &curbuf->b_changed) {
   3158    return BOOLEAN_OPTVAL(curbufIsChanged());
   3159  }
   3160 
   3161  OptValType type = option_get_type(opt_idx);
   3162 
   3163  switch (type) {
   3164  case kOptValTypeNil:
   3165    return NIL_OPTVAL;
   3166  case kOptValTypeBoolean:
   3167    return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp));
   3168  case kOptValTypeNumber:
   3169    return NUMBER_OPTVAL(*(OptInt *)varp);
   3170  case kOptValTypeString:
   3171    return STRING_OPTVAL(cstr_as_string(*(char **)varp));
   3172  }
   3173  UNREACHABLE;
   3174 }
   3175 
   3176 /// Set option var pointer value from OptVal.
   3177 ///
   3178 /// @param       opt_idx      Option index in options[] table.
   3179 /// @param[out]  varp         Pointer to option variable.
   3180 /// @param[in]   value        New option value.
   3181 /// @param       free_oldval  Free old value.
   3182 static void set_option_varp(OptIndex opt_idx, void *varp, OptVal value, bool free_oldval)
   3183  FUNC_ATTR_NONNULL_ARG(2)
   3184 {
   3185  assert(option_has_type(opt_idx, value.type));
   3186 
   3187  if (free_oldval) {
   3188    optval_free(optval_from_varp(opt_idx, varp));
   3189  }
   3190 
   3191  switch (value.type) {
   3192  case kOptValTypeNil:
   3193    abort();
   3194  case kOptValTypeBoolean:
   3195    *(int *)varp = value.data.boolean;
   3196    return;
   3197  case kOptValTypeNumber:
   3198    *(OptInt *)varp = value.data.number;
   3199    return;
   3200  case kOptValTypeString:
   3201    *(char **)varp = value.data.string.data;
   3202    return;
   3203  }
   3204  UNREACHABLE;
   3205 }
   3206 
   3207 /// Return C-string representation of OptVal. Caller must free the returned C-string.
   3208 static char *optval_to_cstr(OptVal o)
   3209 {
   3210  switch (o.type) {
   3211  case kOptValTypeNil:
   3212    return xstrdup("");
   3213  case kOptValTypeBoolean:
   3214    return xstrdup(o.data.boolean ? "true" : "false");
   3215  case kOptValTypeNumber: {
   3216    char *buf = xmalloc(NUMBUFLEN);
   3217    snprintf(buf, NUMBUFLEN, "%" PRId64, o.data.number);
   3218    return buf;
   3219  }
   3220  case kOptValTypeString: {
   3221    char *buf = xmalloc(o.data.string.size + 3);
   3222    snprintf(buf, o.data.string.size + 3, "\"%s\"", o.data.string.data);
   3223    return buf;
   3224  }
   3225  }
   3226  UNREACHABLE;
   3227 }
   3228 
   3229 /// Convert an OptVal to an API Object.
   3230 Object optval_as_object(OptVal o)
   3231 {
   3232  switch (o.type) {
   3233  case kOptValTypeNil:
   3234    return NIL;
   3235  case kOptValTypeBoolean:
   3236    switch (o.data.boolean) {
   3237    case kFalse:
   3238    case kTrue:
   3239      return BOOLEAN_OBJ(o.data.boolean);
   3240    case kNone:
   3241      return NIL;
   3242    }
   3243    UNREACHABLE;
   3244  case kOptValTypeNumber:
   3245    return INTEGER_OBJ(o.data.number);
   3246  case kOptValTypeString:
   3247    return STRING_OBJ(o.data.string);
   3248  }
   3249  UNREACHABLE;
   3250 }
   3251 
   3252 /// Convert an API Object to an OptVal.
   3253 OptVal object_as_optval(Object o, bool *error)
   3254 {
   3255  switch (o.type) {
   3256  case kObjectTypeNil:
   3257    return NIL_OPTVAL;
   3258  case kObjectTypeBoolean:
   3259    return BOOLEAN_OPTVAL(o.data.boolean);
   3260  case kObjectTypeInteger:
   3261    return NUMBER_OPTVAL((OptInt)o.data.integer);
   3262  case kObjectTypeString:
   3263    return STRING_OPTVAL(o.data.string);
   3264  default:
   3265    *error = true;
   3266    return NIL_OPTVAL;
   3267  }
   3268  UNREACHABLE;
   3269 }
   3270 
   3271 /// Check if option is hidden.
   3272 ///
   3273 /// @param  opt_idx  Option index in options[] table.
   3274 ///
   3275 /// @return  True if option is hidden, false otherwise. Returns false if option name is invalid.
   3276 bool is_option_hidden(OptIndex opt_idx)
   3277 {
   3278  // Hidden options are always immutable and point to their default value
   3279  return opt_idx != kOptInvalid && options[opt_idx].immutable
   3280         && options[opt_idx].var == &options[opt_idx].def_val.data;
   3281 }
   3282 
   3283 /// Check if option supports a specific type.
   3284 bool option_has_type(OptIndex opt_idx, OptValType type)
   3285 {
   3286  return opt_idx != kOptInvalid && options[opt_idx].type == type;
   3287 }
   3288 
   3289 /// Check if option supports a specific scope.
   3290 bool option_has_scope(OptIndex opt_idx, OptScope scope)
   3291 {
   3292  // Ensure that scope flags variable can hold all scopes.
   3293  STATIC_ASSERT(kOptScopeSize <= sizeof(OptScopeFlags) * 8,
   3294                "Option scope_flags cannot fit all option scopes");
   3295  // Ensure that the scope is valid before accessing scope_flags.
   3296  assert(scope >= kOptScopeGlobal && scope < kOptScopeSize);
   3297  // Bitshift 1 by the value of scope to get the scope's corresponding flag, and check if it's set
   3298  // in the scope_flags bit field.
   3299  return get_option(opt_idx)->scope_flags & (1 << scope);
   3300 }
   3301 
   3302 /// Check if option is global-local.
   3303 static inline bool option_is_global_local(OptIndex opt_idx)
   3304 {
   3305  // Global-local options have at least two types, so their type flag cannot be a power of two.
   3306  return opt_idx != kOptInvalid && !is_power_of_two(options[opt_idx].scope_flags);
   3307 }
   3308 
   3309 /// Check if option only supports global scope.
   3310 static inline bool option_is_global_only(OptIndex opt_idx)
   3311 {
   3312  // For an option to be global-only, it has to only have a single scope, which means the scope
   3313  // flags must be a power of two, and it must have the global scope.
   3314  return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags)
   3315         && option_has_scope(opt_idx, kOptScopeGlobal);
   3316 }
   3317 
   3318 /// Check if option only supports window scope.
   3319 static inline bool option_is_window_local(OptIndex opt_idx)
   3320 {
   3321  // For an option to be window-local it has to only have a single scope, which means the scope
   3322  // flags must be a power of two, and it must have the window scope.
   3323  return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags)
   3324         && option_has_scope(opt_idx, kOptScopeWin);
   3325 }
   3326 
   3327 /// Get option index for scope.
   3328 ssize_t option_scope_idx(OptIndex opt_idx, OptScope scope)
   3329 {
   3330  return options[opt_idx].scope_idx[scope];
   3331 }
   3332 
   3333 /// Get option flags.
   3334 ///
   3335 /// @param  opt_idx  Option index in options[] table.
   3336 ///
   3337 /// @return  Option flags. Returns 0 for invalid option name.
   3338 uint32_t get_option_flags(OptIndex opt_idx)
   3339 {
   3340  return opt_idx == kOptInvalid ? 0 : options[opt_idx].flags;
   3341 }
   3342 
   3343 /// Gets the value for an option.
   3344 ///
   3345 /// @param  opt_idx    Option index in options[] table.
   3346 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3347 ///
   3348 /// @return [allocated] Option value. Returns NIL_OPTVAL for invalid option index.
   3349 OptVal get_option_value(OptIndex opt_idx, int opt_flags)
   3350 {
   3351  if (opt_idx == kOptInvalid) {  // option not in the options[] table.
   3352    return NIL_OPTVAL;
   3353  }
   3354 
   3355  vimoption_T *opt = &options[opt_idx];
   3356  void *varp = get_varp_scope(opt, opt_flags);
   3357 
   3358  return optval_copy(optval_from_varp(opt_idx, varp));
   3359 }
   3360 
   3361 /// Return information for option at 'opt_idx'
   3362 vimoption_T *get_option(OptIndex opt_idx)
   3363 {
   3364  assert(opt_idx != kOptInvalid);
   3365  return &options[opt_idx];
   3366 }
   3367 
   3368 /// Get option value that represents an unset local value for an option.
   3369 /// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options.
   3370 ///
   3371 /// @param      opt_idx  Option index in options[] table.
   3372 /// @param[in]  varp  Pointer to option variable.
   3373 ///
   3374 /// @return Option value equal to the unset value for the option.
   3375 static OptVal get_option_unset_value(OptIndex opt_idx)
   3376 {
   3377  assert(opt_idx != kOptInvalid);
   3378  vimoption_T *opt = &options[opt_idx];
   3379 
   3380  // For global-local options, use the unset value of the local value.
   3381  if (option_is_global_local(opt_idx)) {
   3382    // String global-local options always use an empty string for the unset value.
   3383    if (option_has_type(opt_idx, kOptValTypeString)) {
   3384      return STATIC_CSTR_AS_OPTVAL("");
   3385    }
   3386 
   3387    switch (opt_idx) {
   3388    case kOptAutocomplete:
   3389    case kOptAutoread:
   3390    case kOptFsync:
   3391      return BOOLEAN_OPTVAL(kNone);
   3392    case kOptScrolloff:
   3393    case kOptSidescrolloff:
   3394      return NUMBER_OPTVAL(-1);
   3395    case kOptUndolevels:
   3396      return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL);
   3397    default:
   3398      abort();
   3399    }
   3400  }
   3401 
   3402  // For options that aren't global-local, use the global value to represent an unset local value.
   3403  return optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL));
   3404 }
   3405 
   3406 /// Check if local value of global-local option is unset for current buffer / window.
   3407 /// Always returns false for options that aren't global-local.
   3408 ///
   3409 /// TODO(famiu): Remove this once we have an OptVal type to indicate an unset local value.
   3410 static bool is_option_local_value_unset(OptIndex opt_idx)
   3411 {
   3412  vimoption_T *opt = get_option(opt_idx);
   3413 
   3414  // Local value of option that isn't global-local is always considered set.
   3415  if (!option_is_global_local(opt_idx)) {
   3416    return false;
   3417  }
   3418 
   3419  void *varp_local = get_varp_scope(opt, OPT_LOCAL);
   3420  OptVal local_value = optval_from_varp(opt_idx, varp_local);
   3421  OptVal unset_local_value = get_option_unset_value(opt_idx);
   3422 
   3423  return optval_equal(local_value, unset_local_value);
   3424 }
   3425 
   3426 /// Handle side-effects of setting an option.
   3427 ///
   3428 /// @param       opt_idx         Index in options[] table. Must not be kOptInvalid.
   3429 /// @param[in]   varp            Option variable pointer, cannot be NULL.
   3430 /// @param       old_value       Old option value.
   3431 /// @param       opt_flags       Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3432 /// @param       set_sid         Script ID. Special values:
   3433 ///                                0: Use current script ID.
   3434 ///                                SID_NONE: Don't set script ID.
   3435 /// @param       direct          Don't process side-effects.
   3436 /// @param       value_replaced  Value was replaced completely.
   3437 /// @param[out]  errbuf          Buffer for error message.
   3438 /// @param       errbuflen       Length of error buffer.
   3439 ///
   3440 /// @return  NULL on success, an untranslated error message on error.
   3441 static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value,
   3442                                  int opt_flags, scid_T set_sid, const bool direct,
   3443                                  const bool value_replaced, char *errbuf, size_t errbuflen)
   3444 {
   3445  vimoption_T *opt = &options[opt_idx];
   3446  const char *errmsg = NULL;
   3447  bool restore_chartab = false;
   3448  bool value_changed = false;
   3449  bool value_checked = false;
   3450 
   3451  optset_T did_set_cb_args = {
   3452    .os_varp = varp,
   3453    .os_idx = opt_idx,
   3454    .os_flags = opt_flags,
   3455    .os_oldval = old_value.data,
   3456    .os_newval = new_value.data,
   3457    .os_value_checked = false,
   3458    .os_value_changed = false,
   3459    .os_restore_chartab = false,
   3460    .os_errbuf = errbuf,
   3461    .os_errbuflen = errbuflen,
   3462    .os_buf = curbuf,
   3463    .os_win = curwin,
   3464  };
   3465 
   3466  if (direct) {
   3467    // Don't do any extra processing if setting directly.
   3468  }
   3469  // Disallow changing immutable options.
   3470  else if (opt->immutable && !optval_equal(old_value, new_value)) {
   3471    errmsg = e_unsupportedoption;
   3472  }
   3473  // Disallow changing some options from secure mode.
   3474  else if ((secure || sandbox != 0) && (opt->flags & kOptFlagSecure)) {
   3475    errmsg = e_secure;
   3476  }
   3477  // Check for a "normal" directory or file name in some string options.
   3478  else if (new_value.type == kOptValTypeString
   3479           && check_illegal_path_names(*(char **)varp, opt->flags)) {
   3480    errmsg = e_invarg;
   3481  } else if (opt->opt_did_set_cb != NULL) {
   3482    // Invoke the option specific callback function to validate and apply the new value.
   3483    errmsg = opt->opt_did_set_cb(&did_set_cb_args);
   3484    // The 'filetype' and 'syntax' option callback functions may change the os_value_changed field.
   3485    value_changed = did_set_cb_args.os_value_changed;
   3486    // The 'keymap', 'filetype' and 'syntax' option callback functions may change the
   3487    // os_value_checked field.
   3488    value_checked = did_set_cb_args.os_value_checked;
   3489    // The 'isident', 'iskeyword', 'isprint' and 'isfname' options may change the character table.
   3490    // On failure, this needs to be restored.
   3491    restore_chartab = did_set_cb_args.os_restore_chartab;
   3492  }
   3493 
   3494  // If option is hidden or if an error is detected, restore the previous value and don't do any
   3495  // further processing.
   3496  if (errmsg != NULL) {
   3497    set_option_varp(opt_idx, varp, old_value, true);
   3498    // When resetting some values, need to act on it.
   3499    if (restore_chartab) {
   3500      buf_init_chartab(curbuf, true);
   3501    }
   3502 
   3503    return errmsg;
   3504  }
   3505 
   3506  // Re-assign the new value as its value may get freed or modified by the option callback.
   3507  new_value = optval_from_varp(opt_idx, varp);
   3508 
   3509  if (set_sid != SID_NONE) {
   3510    sctx_T script_ctx = set_sid == 0 ? current_sctx : (sctx_T){ .sc_sid = set_sid };
   3511    // Remember where the option was set.
   3512    set_option_sctx(opt_idx, opt_flags, script_ctx);
   3513  }
   3514 
   3515  optval_free(old_value);
   3516 
   3517  const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
   3518 
   3519  if (scope_both) {
   3520    if (option_is_global_local(opt_idx)) {
   3521      // Global option with local value set to use global value.
   3522      // Free the local value and clear it.
   3523      void *varp_local = get_varp_scope(opt, OPT_LOCAL);
   3524      OptVal local_unset_value = get_option_unset_value(opt_idx);
   3525      set_option_varp(opt_idx, varp_local, optval_copy(local_unset_value), true);
   3526    } else {
   3527      // May set global value for local option.
   3528      void *varp_global = get_varp_scope(opt, OPT_GLOBAL);
   3529      set_option_varp(opt_idx, varp_global, optval_copy(new_value), true);
   3530    }
   3531  }
   3532 
   3533  // Don't do anything else if setting the option directly.
   3534  if (direct) {
   3535    return errmsg;
   3536  }
   3537 
   3538  // Trigger the autocommand only after setting the flags.
   3539  if (varp == &curbuf->b_p_syn) {
   3540    do_syntax_autocmd(curbuf, value_changed);
   3541  } else if (varp == &curbuf->b_p_ft) {
   3542    // 'filetype' is set, trigger the FileType autocommand
   3543    // Skip this when called from a modeline
   3544    // Force autocmd when the filetype was changed
   3545    if (!(opt_flags & OPT_MODELINE) || value_changed) {
   3546      do_filetype_autocmd(curbuf, value_changed);
   3547    }
   3548  } else if (varp == &curwin->w_s->b_p_spl) {
   3549    do_spelllang_source(curwin);
   3550  }
   3551 
   3552  // In case 'ruler' or 'showcmd' or 'columns' or 'ls' changed.
   3553  comp_col();
   3554 
   3555  if (varp == &p_mouse) {
   3556    setmouse();  // in case 'mouse' changed
   3557  } else if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) {
   3558    // Changing Formatlistpattern when briopt includes the list setting:
   3559    // redraw
   3560    redraw_all_later(UPD_NOT_VALID);
   3561  } else if (varp == &p_wbr || varp == &(curwin->w_p_wbr)) {
   3562    // add / remove window bars for 'winbar'
   3563    set_winbar(true);
   3564  }
   3565 
   3566  if (curwin->w_curswant != MAXCOL
   3567      && (opt->flags & (kOptFlagCurswant | kOptFlagRedrAll)) != 0
   3568      && (opt->flags & kOptFlagHLOnly) == 0) {
   3569    curwin->w_set_curswant = true;
   3570  }
   3571 
   3572  check_redraw(opt->flags);
   3573 
   3574  if (errmsg == NULL) {
   3575    opt->flags |= kOptFlagWasSet;
   3576 
   3577    uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
   3578    uint32_t *flagsp_local = scope_both ? insecure_flag(curwin, opt_idx, OPT_LOCAL) : NULL;
   3579    // When an option is set in the sandbox, from a modeline or in secure mode set the
   3580    // kOptFlagInsecure flag.  Otherwise, if a new value is stored reset the flag.
   3581    if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) {
   3582      *flagsp |= kOptFlagInsecure;
   3583      if (flagsp_local != NULL) {
   3584        *flagsp_local |= kOptFlagInsecure;
   3585      }
   3586    } else if (value_replaced) {
   3587      *flagsp &= ~(unsigned)kOptFlagInsecure;
   3588      if (flagsp_local != NULL) {
   3589        *flagsp_local &= ~(unsigned)kOptFlagInsecure;
   3590      }
   3591    }
   3592  }
   3593 
   3594  return errmsg;
   3595 }
   3596 
   3597 /// Validate the new value for an option.
   3598 ///
   3599 /// @param  opt_idx         Index in options[] table. Must not be kOptInvalid.
   3600 /// @param  newval[in,out]  New option value. Might be modified.
   3601 static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, int opt_flags,
   3602                                         char *errbuf, size_t errbuflen)
   3603 {
   3604  const char *errmsg = NULL;
   3605  vimoption_T *opt = &options[opt_idx];
   3606 
   3607  // Always allow unsetting local value of global-local option.
   3608  if (option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL)
   3609      && optval_equal(*newval, get_option_unset_value(opt_idx))) {
   3610    return NULL;
   3611  }
   3612 
   3613  if (newval->type == kOptValTypeNil) {
   3614    // Don't try to unset local value if scope is global.
   3615    // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is
   3616    // fixed.
   3617    if (opt_flags == OPT_GLOBAL) {
   3618      errmsg = _("Cannot unset global option value");
   3619    } else {
   3620      *newval = optval_copy(get_option_unset_value(opt_idx));
   3621    }
   3622  } else if (!option_has_type(opt_idx, newval->type)) {
   3623    char *rep = optval_to_cstr(*newval);
   3624    const char *type_str = optval_type_get_name(opt->type);
   3625    snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
   3626             opt->fullname, type_str, optval_type_get_name(newval->type), rep);
   3627    xfree(rep);
   3628    errmsg = errbuf;
   3629  } else if (newval->type == kOptValTypeNumber) {
   3630    // Validate and bound check num option values.
   3631    errmsg = validate_num_option(opt_idx, &newval->data.number, errbuf, errbuflen);
   3632  }
   3633 
   3634  return errmsg;
   3635 }
   3636 
   3637 /// Set the value of an option using an OptVal.
   3638 ///
   3639 /// @param       opt_idx         Index in options[] table. Must not be kOptInvalid.
   3640 /// @param       value           New option value. Might get freed.
   3641 /// @param       opt_flags       Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3642 /// @param       set_sid         Script ID. Special values:
   3643 ///                                0: Use current script ID.
   3644 ///                                SID_NONE: Don't set script ID.
   3645 /// @param       direct          Don't process side-effects.
   3646 /// @param       value_replaced  Value was replaced completely.
   3647 /// @param[out]  errbuf          Buffer for error message.
   3648 /// @param       errbuflen       Length of error buffer.
   3649 ///
   3650 /// @return  NULL on success, an untranslated error message on error.
   3651 static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
   3652                              const bool direct, const bool value_replaced, char *errbuf,
   3653                              size_t errbuflen)
   3654 {
   3655  assert(opt_idx != kOptInvalid);
   3656 
   3657  const char *errmsg = NULL;
   3658 
   3659  if (!direct) {
   3660    errmsg = validate_option_value(opt_idx, &value, opt_flags, errbuf, errbuflen);
   3661 
   3662    if (errmsg != NULL) {
   3663      optval_free(value);
   3664      return errmsg;
   3665    }
   3666  }
   3667 
   3668  vimoption_T *opt = &options[opt_idx];
   3669  const bool scope_local = opt_flags & OPT_LOCAL;
   3670  const bool scope_global = opt_flags & OPT_GLOBAL;
   3671  const bool scope_both = !scope_local && !scope_global;
   3672  // Whether local value of global-local option is unset.
   3673  // NOTE: When this is true, it also implies that the option is global-local.
   3674  const bool is_opt_local_unset = is_option_local_value_unset(opt_idx);
   3675 
   3676  // When using ":set opt=val" for a global option with a local value the local value will be reset,
   3677  // use the global value in that case.
   3678  void *varp
   3679    = scope_both && option_is_global_local(opt_idx) ? opt->var : get_varp_scope(opt, opt_flags);
   3680  void *varp_local = get_varp_scope(opt, OPT_LOCAL);
   3681  void *varp_global = get_varp_scope(opt, OPT_GLOBAL);
   3682 
   3683  OptVal old_value = optval_from_varp(opt_idx, varp);
   3684  OptVal old_global_value = optval_from_varp(opt_idx, varp_global);
   3685  // If local value of global-local option is unset, use global value as local value.
   3686  OptVal old_local_value = is_opt_local_unset
   3687                           ? old_global_value
   3688                           : optval_from_varp(opt_idx, varp_local);
   3689  // Value that's actually being used.
   3690  // For local scope of a global-local option, it's equal to the global value if the local value is
   3691  // unset. In every other case, it is the same as old_value.
   3692  // This value is used instead of old_value when triggering the OptionSet autocommand.
   3693  OptVal used_old_value = (scope_local && is_opt_local_unset)
   3694                          ? optval_from_varp(opt_idx, get_varp(opt))
   3695                          : old_value;
   3696 
   3697  // Save the old values and the new value in case they get changed.
   3698  OptVal saved_used_value = optval_copy(used_old_value);
   3699  OptVal saved_old_global_value = optval_copy(old_global_value);
   3700  OptVal saved_old_local_value = optval_copy(old_local_value);
   3701  // New value (and varp) may become invalid if the buffer is closed by autocommands.
   3702  OptVal saved_new_value = optval_copy(value);
   3703 
   3704  uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
   3705  const int secure_saved = secure;
   3706 
   3707  // When an option is set in the sandbox, from a modeline or in secure mode, then deal with side
   3708  // effects in secure mode. Also when the value was set with the kOptFlagInsecure flag and is not
   3709  // completely replaced.
   3710  if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & kOptFlagInsecure))) {
   3711    secure = 1;
   3712  }
   3713 
   3714  // Set option through its variable pointer.
   3715  set_option_varp(opt_idx, varp, value, false);
   3716  // Process any side effects.
   3717  errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, set_sid, direct,
   3718                          value_replaced, errbuf, errbuflen);
   3719 
   3720  secure = secure_saved;
   3721 
   3722  if (errmsg == NULL && !direct) {
   3723    if (!starting) {
   3724      apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value,
   3725                              saved_old_local_value, saved_new_value, errmsg);
   3726    }
   3727    if (opt->flags & kOptFlagUIOption) {
   3728      ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value));
   3729    }
   3730  }
   3731 
   3732  // Free copied values as they are not needed anymore
   3733  optval_free(saved_used_value);
   3734  optval_free(saved_old_local_value);
   3735  optval_free(saved_old_global_value);
   3736  optval_free(saved_new_value);
   3737 
   3738  return errmsg;
   3739 }
   3740 
   3741 /// Set option value directly, without processing any side effects.
   3742 ///
   3743 /// @param  opt_idx    Option index in options[] table.
   3744 /// @param  value      Option value.
   3745 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3746 /// @param  set_sid    Script ID. Special values:
   3747 ///                      0: Use current script ID.
   3748 ///                      SID_NONE: Don't set script ID.
   3749 void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid)
   3750 {
   3751  static char errbuf[IOSIZE];
   3752 
   3753  if (is_option_hidden(opt_idx)) {
   3754    return;
   3755  }
   3756 
   3757  const char *errmsg = set_option(opt_idx, optval_copy(value), opt_flags, set_sid, true, true,
   3758                                  errbuf, sizeof(errbuf));
   3759  assert(errmsg == NULL);
   3760  (void)errmsg;  // ignore unused warning
   3761 }
   3762 
   3763 /// Set option value directly for buffer / window, without processing any side effects.
   3764 ///
   3765 /// @param      opt_idx    Option index in options[] table.
   3766 /// @param      value      Option value.
   3767 /// @param      opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3768 /// @param      set_sid    Script ID. Special values:
   3769 ///                          0: Use current script ID.
   3770 ///                          SID_NONE: Don't set script ID.
   3771 /// @param      scope      Option scope. See OptScope in option.h.
   3772 /// @param[in]  from       Target buffer/window.
   3773 void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
   3774                           OptScope scope, void *const from)
   3775 {
   3776  buf_T *save_curbuf = curbuf;
   3777  win_T *save_curwin = curwin;
   3778 
   3779  // Don't use switch_option_context(), as that calls aucmd_prepbuf(), which may have unintended
   3780  // side-effects when setting an option directly. Just change the values of curbuf and curwin if
   3781  // needed, no need to properly switch the window / buffer.
   3782  switch (scope) {
   3783  case kOptScopeGlobal:
   3784    break;
   3785  case kOptScopeWin:
   3786    curwin = (win_T *)from;
   3787    curbuf = curwin->w_buffer;
   3788    break;
   3789  case kOptScopeBuf:
   3790    curbuf = (buf_T *)from;
   3791    break;
   3792  }
   3793 
   3794  set_option_direct(opt_idx, value, opt_flags, set_sid);
   3795 
   3796  curwin = save_curwin;
   3797  curbuf = save_curbuf;
   3798 }
   3799 
   3800 /// Set the value of an option.
   3801 ///
   3802 /// @param      opt_idx    Index in options[] table. Must not be kOptInvalid.
   3803 /// @param[in]  value      Option value. If NIL_OPTVAL, the option value is cleared.
   3804 /// @param[in]  opt_flags  Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
   3805 ///
   3806 /// @return  NULL on success, an untranslated error message on error.
   3807 const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt_flags)
   3808 {
   3809  assert(opt_idx != kOptInvalid);
   3810 
   3811  static char errbuf[IOSIZE];
   3812  uint32_t flags = options[opt_idx].flags;
   3813 
   3814  // Disallow changing some options in the sandbox
   3815  if (sandbox > 0 && (flags & kOptFlagSecure)) {
   3816    return _(e_sandbox);
   3817  }
   3818 
   3819  return set_option(opt_idx, optval_copy(value), opt_flags, 0, false, true, errbuf, sizeof(errbuf));
   3820 }
   3821 
   3822 /// Unset the local value of a global-local option.
   3823 ///
   3824 /// @param      opt_idx    Index in options[] table. Must not be kOptInvalid.
   3825 ///
   3826 /// @return  NULL on success, an untranslated error message on error.
   3827 static inline const char *unset_option_local_value(const OptIndex opt_idx)
   3828 {
   3829  assert(option_is_global_local(opt_idx));
   3830  return set_option_value(opt_idx, get_option_unset_value(opt_idx), OPT_LOCAL);
   3831 }
   3832 
   3833 /// Set the value of an option. Supports TTY options, unlike set_option_value().
   3834 ///
   3835 /// @param      name       Option name. Used for error messages and for setting TTY options.
   3836 /// @param      opt_idx    Option indx in options[] table. If kOptInvalid, `name` is used to
   3837 ///                        check if the option is a TTY option, and an error is shown if it's not.
   3838 ///                        If the option is a TTY option, the function fails silently.
   3839 /// @param      value      Option value. If NIL_OPTVAL, the option value is cleared.
   3840 /// @param[in]  opt_flags  Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
   3841 ///
   3842 /// @return  NULL on success, an untranslated error message on error.
   3843 const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, const OptVal value,
   3844                                        int opt_flags)
   3845  FUNC_ATTR_NONNULL_ARG(1)
   3846 {
   3847  static char errbuf[IOSIZE];
   3848 
   3849  if (opt_idx == kOptInvalid) {
   3850    if (is_tty_option(name)) {
   3851      return NULL;  // Fail silently; many old vimrcs set t_xx options.
   3852    }
   3853 
   3854    snprintf(errbuf, sizeof(errbuf), _(e_unknown_option2), name);
   3855    return errbuf;
   3856  }
   3857 
   3858  return set_option_value(opt_idx, value, opt_flags);
   3859 }
   3860 
   3861 /// Call set_option_value() and when an error is returned, report it.
   3862 ///
   3863 /// @param  opt_idx    Option index in options[] table.
   3864 /// @param  value      Option value. If NIL_OPTVAL, the option value is cleared.
   3865 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3866 void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_flags)
   3867 {
   3868  const char *errmsg = set_option_value(opt_idx, value, opt_flags);
   3869 
   3870  if (errmsg != NULL) {
   3871    emsg(_(errmsg));
   3872  }
   3873 }
   3874 
   3875 /// Switch current context to get/set option value for window/buffer.
   3876 ///
   3877 /// @param[out]  ctx        Current context. switchwin_T for window and aco_save_T for buffer.
   3878 /// @param       scope      Option scope. See OptScope in option.h.
   3879 /// @param[in]   from       Target buffer/window.
   3880 /// @param[out]  err        Error message, if any.
   3881 ///
   3882 /// @return  true if context was switched, false otherwise.
   3883 static bool switch_option_context(void *const ctx, OptScope scope, void *const from, Error *err)
   3884 {
   3885  switch (scope) {
   3886  case kOptScopeGlobal:
   3887    return false;
   3888  case kOptScopeWin: {
   3889    win_T *const win = (win_T *)from;
   3890    switchwin_T *const switchwin = (switchwin_T *)ctx;
   3891 
   3892    if (win == curwin) {
   3893      return false;
   3894    }
   3895 
   3896    if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true)
   3897        == FAIL) {
   3898      restore_win_noblock(switchwin, true);
   3899 
   3900      if (ERROR_SET(err)) {
   3901        return false;
   3902      }
   3903      api_set_error(err, kErrorTypeException, "Problem while switching windows");
   3904      return false;
   3905    }
   3906    return true;
   3907  }
   3908  case kOptScopeBuf: {
   3909    buf_T *const buf = (buf_T *)from;
   3910    aco_save_T *const aco = (aco_save_T *)ctx;
   3911 
   3912    if (buf == curbuf) {
   3913      return false;
   3914    }
   3915    aucmd_prepbuf(aco, buf);
   3916    return true;
   3917  }
   3918  }
   3919  UNREACHABLE;
   3920 }
   3921 
   3922 /// Restore context after getting/setting option for window/buffer. See switch_option_context() for
   3923 /// params.
   3924 static void restore_option_context(void *const ctx, OptScope scope)
   3925 {
   3926  switch (scope) {
   3927  case kOptScopeGlobal:
   3928    break;
   3929  case kOptScopeWin:
   3930    restore_win_noblock((switchwin_T *)ctx, true);
   3931    break;
   3932  case kOptScopeBuf:
   3933    aucmd_restbuf((aco_save_T *)ctx);
   3934    break;
   3935  }
   3936 }
   3937 
   3938 /// Get option value for buffer / window.
   3939 ///
   3940 /// @param       opt_idx    Option index in options[] table.
   3941 /// @param[out]  flagsp     Set to the option flags (see OptFlags) (if not NULL).
   3942 /// @param[in]   scope      Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   3943 /// @param[out]  hidden     Whether option is hidden.
   3944 /// @param       scope      Option scope. See OptScope in option.h.
   3945 /// @param[in]   from       Target buffer/window.
   3946 /// @param[out]  err        Error message, if any.
   3947 ///
   3948 /// @return  Option value. Must be freed by caller.
   3949 OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scope, void *const from,
   3950                            Error *err)
   3951 {
   3952  switchwin_T switchwin;
   3953  aco_save_T aco;
   3954  void *ctx = scope == kOptScopeWin ? (void *)&switchwin
   3955                                    : (scope == kOptScopeBuf ? (void *)&aco : NULL);
   3956 
   3957  bool switched = switch_option_context(ctx, scope, from, err);
   3958  if (ERROR_SET(err)) {
   3959    return NIL_OPTVAL;
   3960  }
   3961 
   3962  OptVal retv = get_option_value(opt_idx, opt_flags);
   3963 
   3964  if (switched) {
   3965    restore_option_context(ctx, scope);
   3966  }
   3967 
   3968  return retv;
   3969 }
   3970 
   3971 /// Set option value for buffer / window.
   3972 ///
   3973 /// @param       name        Option name.
   3974 /// @param       opt_idx     Option index in options[] table.
   3975 /// @param[in]   value       Option value.
   3976 /// @param[in]   opt_flags   Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
   3977 /// @param       scope       Option scope. See OptScope in option.h.
   3978 /// @param[in]   from        Target buffer/window.
   3979 /// @param[out]  err         Error message, if any.
   3980 void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags,
   3981                          const OptScope scope, void *const from, Error *err)
   3982  FUNC_ATTR_NONNULL_ARG(1)
   3983 {
   3984  switchwin_T switchwin;
   3985  aco_save_T aco;
   3986  void *ctx = scope == kOptScopeWin ? (void *)&switchwin
   3987                                    : (scope == kOptScopeBuf ? (void *)&aco : NULL);
   3988 
   3989  bool switched = switch_option_context(ctx, scope, from, err);
   3990  if (ERROR_SET(err)) {
   3991    return;
   3992  }
   3993 
   3994  const char *const errmsg = set_option_value_handle_tty(name, opt_idx, value, opt_flags);
   3995  if (errmsg) {
   3996    api_set_error(err, kErrorTypeException, "%s", errmsg);
   3997  }
   3998 
   3999  if (switched) {
   4000    restore_option_context(ctx, scope);
   4001  }
   4002 }
   4003 
   4004 /// if 'all' == false: show changed options
   4005 /// if 'all' == true: show all normal options
   4006 ///
   4007 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   4008 static void showoptions(bool all, int opt_flags)
   4009 {
   4010 #define INC 20
   4011 #define GAP 3
   4012 
   4013  vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT);
   4014 
   4015  msg_ext_set_kind("list_cmd");
   4016  // Highlight title
   4017  if (opt_flags & OPT_GLOBAL) {
   4018    msg_puts_title(_("\n--- Global option values ---"));
   4019  } else if (opt_flags & OPT_LOCAL) {
   4020    msg_puts_title(_("\n--- Local option values ---"));
   4021  } else {
   4022    msg_puts_title(_("\n--- Options ---"));
   4023  }
   4024 
   4025  // Do the loop two times:
   4026  // 1. display the short items
   4027  // 2. display the long items (only strings and numbers)
   4028  // When "opt_flags" has OPT_ONECOLUMN do everything in run 2.
   4029  for (int run = 1; run <= 2 && !got_int; run++) {
   4030    // collect the items in items[]
   4031    int item_count = 0;
   4032    vimoption_T *opt;
   4033    for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   4034      opt = &options[opt_idx];
   4035      // apply :filter /pat/
   4036      if (message_filtered(opt->fullname)) {
   4037        continue;
   4038      }
   4039 
   4040      void *varp = NULL;
   4041      if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
   4042        if (!option_is_global_only(opt_idx)) {
   4043          varp = get_varp_scope(opt, opt_flags);
   4044        }
   4045      } else {
   4046        varp = get_varp(opt);
   4047      }
   4048      if (varp != NULL && (all || !optval_default(opt_idx, varp))) {
   4049        int len;
   4050        if (opt_flags & OPT_ONECOLUMN) {
   4051          len = Columns;
   4052        } else if (option_has_type(opt_idx, kOptValTypeBoolean)) {
   4053          len = 1;                      // a toggle option fits always
   4054        } else {
   4055          option_value2string(opt, opt_flags);
   4056          len = (int)strlen(opt->fullname) + vim_strsize(NameBuff) + 1;
   4057        }
   4058        if ((len <= INC - GAP && run == 1)
   4059            || (len > INC - GAP && run == 2)) {
   4060          items[item_count++] = opt;
   4061        }
   4062      }
   4063    }
   4064 
   4065    int rows;
   4066 
   4067    // display the items
   4068    if (run == 1) {
   4069      assert(Columns <= INT_MAX - GAP
   4070             && Columns + GAP >= INT_MIN + 3
   4071             && (Columns + GAP - 3) / INC >= INT_MIN
   4072             && (Columns + GAP - 3) / INC <= INT_MAX);
   4073      int cols = (Columns + GAP - 3) / INC;
   4074      if (cols == 0) {
   4075        cols = 1;
   4076      }
   4077      rows = (item_count + cols - 1) / cols;
   4078    } else {    // run == 2
   4079      rows = item_count;
   4080    }
   4081    for (int row = 0; row < rows && !got_int; row++) {
   4082      msg_putchar('\n');                        // go to next line
   4083      if (got_int) {                            // 'q' typed in more
   4084        break;
   4085      }
   4086      int col = 0;
   4087      for (int i = row; i < item_count; i += rows) {
   4088        msg_advance(col);                       // make columns
   4089        showoneopt(items[i], opt_flags);
   4090        col += INC;
   4091      }
   4092      os_breakcheck();
   4093    }
   4094  }
   4095  xfree(items);
   4096 }
   4097 
   4098 /// Return true if option "p" has its default value.
   4099 static int optval_default(OptIndex opt_idx, void *varp)
   4100 {
   4101  vimoption_T *opt = &options[opt_idx];
   4102 
   4103  // Hidden options always use their default value.
   4104  if (is_option_hidden(opt_idx)) {
   4105    return true;
   4106  }
   4107 
   4108  OptVal current_val = optval_from_varp(opt_idx, varp);
   4109  OptVal default_val = opt->def_val;
   4110 
   4111  return optval_equal(current_val, default_val);
   4112 }
   4113 
   4114 /// Send update to UIs with values of UI relevant options
   4115 void ui_refresh_options(void)
   4116 {
   4117  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   4118    uint32_t flags = options[opt_idx].flags;
   4119    if (!(flags & kOptFlagUIOption)) {
   4120      continue;
   4121    }
   4122    String name = cstr_as_string(options[opt_idx].fullname);
   4123    Object value = optval_as_object(optval_from_varp(opt_idx, options[opt_idx].var));
   4124    ui_call_option_set(name, value);
   4125  }
   4126  if (p_mouse != NULL) {
   4127    setmouse();
   4128  }
   4129 }
   4130 
   4131 /// showoneopt: show the value of one option
   4132 /// must not be called with a hidden option!
   4133 ///
   4134 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   4135 static void showoneopt(vimoption_T *opt, int opt_flags)
   4136 {
   4137  int save_silent = silent_mode;
   4138 
   4139  silent_mode = false;
   4140  info_message = true;          // use stdout, not stderr
   4141 
   4142  OptIndex opt_idx = get_opt_idx(opt);
   4143  void *varp = get_varp_scope(opt, opt_flags);
   4144 
   4145  // for 'modified' we also need to check if 'ff' or 'fenc' changed.
   4146  if (option_has_type(opt_idx, kOptValTypeBoolean)
   4147      && ((int *)varp == &curbuf->b_changed ? !curbufIsChanged() : !*(int *)varp)) {
   4148    msg_puts("no");
   4149  } else if (option_has_type(opt_idx, kOptValTypeBoolean) && *(int *)varp < 0) {
   4150    msg_puts("--");
   4151  } else {
   4152    msg_puts("  ");
   4153  }
   4154  msg_puts(opt->fullname);
   4155  if (!(option_has_type(opt_idx, kOptValTypeBoolean))) {
   4156    msg_putchar('=');
   4157    // put value string in NameBuff
   4158    option_value2string(opt, opt_flags);
   4159    if (*NameBuff != NUL) {
   4160      msg_outtrans(NameBuff, 0, false);
   4161    }
   4162  }
   4163 
   4164  silent_mode = save_silent;
   4165  info_message = false;
   4166 }
   4167 
   4168 /// Write modified options as ":set" commands to a file.
   4169 ///
   4170 /// There are three values for "opt_flags":
   4171 /// OPT_GLOBAL:         Write global option values and fresh values of
   4172 ///             buffer-local options (used for start of a session
   4173 ///             file).
   4174 /// OPT_GLOBAL + OPT_LOCAL: Idem, add fresh values of window-local options for
   4175 ///             curwin (used for a vimrc file).
   4176 /// OPT_LOCAL:          Write buffer-local option values for curbuf, fresh
   4177 ///             and local values for window-local options of
   4178 ///             curwin.  Local values are also written when at the
   4179 ///             default value, because a modeline or autocommand
   4180 ///             may have set them when doing ":edit file" and the
   4181 ///             user has set them back at the default or fresh
   4182 ///             value.
   4183 ///             When "local_only" is true, don't write fresh
   4184 ///             values, only local values (for ":mkview").
   4185 /// (fresh value = value used for a new buffer or window for a local option).
   4186 ///
   4187 /// Return FAIL on error, OK otherwise.
   4188 int makeset(FILE *fd, int opt_flags, int local_only)
   4189 {
   4190  // Some options are never written:
   4191  // - Options that don't have a default (terminal name, columns, lines).
   4192  // - Terminal options.
   4193  // - Hidden options.
   4194  //
   4195  // Do the loop over "options[]" twice: once for options with the
   4196  // kOptFlagPriMkrc flag and once without.
   4197  for (int pri = 1; pri >= 0; pri--) {
   4198    vimoption_T *opt;
   4199    for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   4200      opt = &options[opt_idx];
   4201 
   4202      if (!(opt->flags & kOptFlagNoMkrc)
   4203          && ((pri == 1) == ((opt->flags & kOptFlagPriMkrc) != 0))) {
   4204        // skip global option when only doing locals
   4205        if (option_is_global_only(opt_idx) && !(opt_flags & OPT_GLOBAL)) {
   4206          continue;
   4207        }
   4208 
   4209        // Do not store options like 'bufhidden' and 'syntax' in a vimrc
   4210        // file, they are always buffer-specific.
   4211        if ((opt_flags & OPT_GLOBAL) && (opt->flags & kOptFlagNoGlob)) {
   4212          continue;
   4213        }
   4214 
   4215        void *varp = get_varp_scope(opt, opt_flags);  // currently used value
   4216        // Hidden options are never written.
   4217        if (!varp) {
   4218          continue;
   4219        }
   4220        // Global values are only written when not at the default value.
   4221        if ((opt_flags & OPT_GLOBAL) && optval_default(opt_idx, varp)) {
   4222          continue;
   4223        }
   4224 
   4225        if ((opt_flags & OPT_SKIPRTP)
   4226            && (opt->var == &p_rtp || opt->var == &p_pp)) {
   4227          continue;
   4228        }
   4229 
   4230        int round = 2;
   4231        void *varp_local = NULL;  // fresh value
   4232        if (option_is_window_local(opt_idx)) {
   4233          // skip window-local option when only doing globals
   4234          if (!(opt_flags & OPT_LOCAL)) {
   4235            continue;
   4236          }
   4237          // When fresh value of window-local option is not at the
   4238          // default, need to write it too.
   4239          if (!(opt_flags & OPT_GLOBAL) && !local_only) {
   4240            void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL);  // local value
   4241            if (!optval_default(opt_idx, varp_fresh)) {
   4242              round = 1;
   4243              varp_local = varp;
   4244              varp = varp_fresh;
   4245            }
   4246          }
   4247        }
   4248 
   4249        // Round 1: fresh value for window-local options.
   4250        // Round 2: other values
   4251        for (; round <= 2; varp = varp_local, round++) {
   4252          char *cmd;
   4253          if (round == 1 || (opt_flags & OPT_GLOBAL)) {
   4254            cmd = "set";
   4255          } else {
   4256            cmd = "setlocal";
   4257          }
   4258 
   4259          bool do_endif = false;
   4260          // Don't set 'syntax' and 'filetype' again if the value is already right, avoids reloading
   4261          // the syntax file.
   4262          if (opt_idx == kOptSyntax || opt_idx == kOptFiletype) {
   4263            if (fprintf(fd, "if &%s != '%s'", opt->fullname,
   4264                        *(char **)(varp)) < 0
   4265                || put_eol(fd) < 0) {
   4266              return FAIL;
   4267            }
   4268            do_endif = true;
   4269          }
   4270          if (put_set(fd, cmd, opt_idx, varp) == FAIL) {
   4271            return FAIL;
   4272          }
   4273          if (do_endif) {
   4274            if (put_line(fd, "endif") == FAIL) {
   4275              return FAIL;
   4276            }
   4277          }
   4278        }
   4279      }
   4280    }
   4281  }
   4282  return OK;
   4283 }
   4284 
   4285 /// Generate set commands for the local fold options only.  Used when
   4286 /// 'sessionoptions' or 'viewoptions' contains "folds" but not "options".
   4287 int makefoldset(FILE *fd)
   4288 {
   4289  if (put_set(fd, "setlocal", kOptFoldmethod, &curwin->w_p_fdm) == FAIL
   4290      || put_set(fd, "setlocal", kOptFoldexpr, &curwin->w_p_fde) == FAIL
   4291      || put_set(fd, "setlocal", kOptFoldmarker, &curwin->w_p_fmr) == FAIL
   4292      || put_set(fd, "setlocal", kOptFoldignore, &curwin->w_p_fdi) == FAIL
   4293      || put_set(fd, "setlocal", kOptFoldlevel, &curwin->w_p_fdl) == FAIL
   4294      || put_set(fd, "setlocal", kOptFoldminlines, &curwin->w_p_fml) == FAIL
   4295      || put_set(fd, "setlocal", kOptFoldnestmax, &curwin->w_p_fdn) == FAIL
   4296      || put_set(fd, "setlocal", kOptFoldenable, &curwin->w_p_fen) == FAIL) {
   4297    return FAIL;
   4298  }
   4299 
   4300  return OK;
   4301 }
   4302 
   4303 /// Print the ":set" command to set a single option to file.
   4304 ///
   4305 /// @param  fd       File descriptor.
   4306 /// @param  cmd      Command name.
   4307 /// @param  opt_idx  Option index in options[] table.
   4308 /// @param  varp     Pointer to option variable.
   4309 ///
   4310 /// @return FAIL on error, OK otherwise.
   4311 static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
   4312 {
   4313  OptVal value = optval_from_varp(opt_idx, varp);
   4314  vimoption_T *opt = &options[opt_idx];
   4315  char *name = opt->fullname;
   4316  uint64_t flags = opt->flags;
   4317 
   4318  if (option_is_global_local(opt_idx) && varp != opt->var
   4319      && optval_equal(value, get_option_unset_value(opt_idx))) {
   4320    // Processing unset local value of global-local option. Do nothing.
   4321    return OK;
   4322  }
   4323 
   4324  switch (value.type) {
   4325  case kOptValTypeNil:
   4326    abort();
   4327  case kOptValTypeBoolean: {
   4328    assert(value.data.boolean != kNone);
   4329    bool value_bool = TRISTATE_TO_BOOL(value.data.boolean, false);
   4330 
   4331    if (fprintf(fd, "%s %s%s", cmd, value_bool ? "" : "no", name) < 0) {
   4332      return FAIL;
   4333    }
   4334    break;
   4335  }
   4336  case kOptValTypeNumber: {
   4337    if (fprintf(fd, "%s %s=", cmd, name) < 0) {
   4338      return FAIL;
   4339    }
   4340 
   4341    OptInt value_num = value.data.number;
   4342 
   4343    OptInt wc;
   4344    if (wc_use_keyname(varp, &wc)) {
   4345      // print 'wildchar' and 'wildcharm' as a key name
   4346      if (fputs(get_special_key_name((int)wc, 0), fd) < 0) {
   4347        return FAIL;
   4348      }
   4349    } else if (fprintf(fd, "%" PRId64, value_num) < 0) {
   4350      return FAIL;
   4351    }
   4352    break;
   4353  }
   4354  case kOptValTypeString: {
   4355    if (fprintf(fd, "%s %s=", cmd, name) < 0) {
   4356      return FAIL;
   4357    }
   4358 
   4359    const char *value_str = value.data.string.data;
   4360    char *buf = NULL;
   4361    char *part = NULL;
   4362 
   4363    if (value_str != NULL) {
   4364      if ((flags & kOptFlagExpand) != 0) {
   4365        size_t size = (size_t)strlen(value_str) + 1;
   4366 
   4367        // replace home directory in the whole option value into "buf"
   4368        buf = xmalloc(size);
   4369        home_replace(NULL, value_str, buf, size, false);
   4370 
   4371        // If the option value is longer than MAXPATHL, we need to append
   4372        // each comma separated part of the option separately, so that it
   4373        // can be expanded when read back.
   4374        if (size >= MAXPATHL && (flags & kOptFlagComma) != 0
   4375            && vim_strchr(value_str, ',') != NULL) {
   4376          part = xmalloc(size);
   4377 
   4378          // write line break to clear the option, e.g. ':set rtp='
   4379          if (put_eol(fd) == FAIL) {
   4380            goto fail;
   4381          }
   4382          char *p = buf;
   4383          while (*p != NUL) {
   4384            // for each comma separated option part, append value to
   4385            // the option, :set rtp+=value
   4386            if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
   4387              goto fail;
   4388            }
   4389            copy_option_part(&p, part, size, ",");
   4390            if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
   4391              goto fail;
   4392            }
   4393          }
   4394          xfree(buf);
   4395          xfree(part);
   4396          return OK;
   4397        }
   4398        if (put_escstr(fd, buf, 2) == FAIL) {
   4399          xfree(buf);
   4400          return FAIL;
   4401        }
   4402        xfree(buf);
   4403      } else if (put_escstr(fd, value_str, 2) == FAIL) {
   4404        return FAIL;
   4405      }
   4406    }
   4407    break;
   4408  fail:
   4409    xfree(buf);
   4410    xfree(part);
   4411    return FAIL;
   4412  }
   4413  }
   4414 
   4415  if (put_eol(fd) < 0) {
   4416    return FAIL;
   4417  }
   4418  return OK;
   4419 }
   4420 
   4421 void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
   4422 {
   4423  OptIndex opt_idx = get_opt_idx(p);
   4424 
   4425  if ((opt_flags & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
   4426    if (option_is_window_local(opt_idx)) {
   4427      return GLOBAL_WO(get_varp_from(p, buf, win));
   4428    }
   4429    return p->var;
   4430  }
   4431 
   4432  if ((opt_flags & OPT_LOCAL) && option_is_global_local(opt_idx)) {
   4433    switch (opt_idx) {
   4434    case kOptFormatprg:
   4435      return &(buf->b_p_fp);
   4436    case kOptFsync:
   4437      return &(buf->b_p_fs);
   4438    case kOptFindfunc:
   4439      return &(buf->b_p_ffu);
   4440    case kOptErrorformat:
   4441      return &(buf->b_p_efm);
   4442    case kOptGrepformat:
   4443      return &(buf->b_p_gefm);
   4444    case kOptGrepprg:
   4445      return &(buf->b_p_gp);
   4446    case kOptMakeprg:
   4447      return &(buf->b_p_mp);
   4448    case kOptEqualprg:
   4449      return &(buf->b_p_ep);
   4450    case kOptKeywordprg:
   4451      return &(buf->b_p_kp);
   4452    case kOptPath:
   4453      return &(buf->b_p_path);
   4454    case kOptAutocomplete:
   4455      return &(buf->b_p_ac);
   4456    case kOptAutoread:
   4457      return &(buf->b_p_ar);
   4458    case kOptTags:
   4459      return &(buf->b_p_tags);
   4460    case kOptTagcase:
   4461      return &(buf->b_p_tc);
   4462    case kOptSidescrolloff:
   4463      return &(win->w_p_siso);
   4464    case kOptScrolloff:
   4465      return &(win->w_p_so);
   4466    case kOptDefine:
   4467      return &(buf->b_p_def);
   4468    case kOptInclude:
   4469      return &(buf->b_p_inc);
   4470    case kOptCompleteopt:
   4471      return &(buf->b_p_cot);
   4472    case kOptDictionary:
   4473      return &(buf->b_p_dict);
   4474    case kOptDiffanchors:
   4475      return &(buf->b_p_dia);
   4476    case kOptThesaurus:
   4477      return &(buf->b_p_tsr);
   4478    case kOptThesaurusfunc:
   4479      return &(buf->b_p_tsrfu);
   4480    case kOptTagfunc:
   4481      return &(buf->b_p_tfu);
   4482    case kOptShowbreak:
   4483      return &(win->w_p_sbr);
   4484    case kOptStatusline:
   4485      return &(win->w_p_stl);
   4486    case kOptWinbar:
   4487      return &(win->w_p_wbr);
   4488    case kOptUndolevels:
   4489      return &(buf->b_p_ul);
   4490    case kOptLispwords:
   4491      return &(buf->b_p_lw);
   4492    case kOptBackupcopy:
   4493      return &(buf->b_p_bkc);
   4494    case kOptMakeencoding:
   4495      return &(buf->b_p_menc);
   4496    case kOptFillchars:
   4497      return &(win->w_p_fcs);
   4498    case kOptListchars:
   4499      return &(win->w_p_lcs);
   4500    case kOptVirtualedit:
   4501      return &(win->w_p_ve);
   4502    default:
   4503      abort();
   4504    }
   4505  }
   4506  return get_varp_from(p, buf, win);
   4507 }
   4508 
   4509 /// Get pointer to option variable, depending on local or global scope.
   4510 ///
   4511 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   4512 void *get_varp_scope(vimoption_T *p, int opt_flags)
   4513 {
   4514  return get_varp_scope_from(p, opt_flags, curbuf, curwin);
   4515 }
   4516 
   4517 /// Get pointer to option variable at 'opt_idx', depending on local or global
   4518 /// scope.
   4519 void *get_option_varp_scope_from(OptIndex opt_idx, int opt_flags, buf_T *buf, win_T *win)
   4520 {
   4521  return get_varp_scope_from(&(options[opt_idx]), opt_flags, buf, win);
   4522 }
   4523 
   4524 void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
   4525 {
   4526  OptIndex opt_idx = get_opt_idx(p);
   4527 
   4528  // Hidden options and global-only options always use the same var pointer
   4529  if (is_option_hidden(opt_idx) || option_is_global_only(opt_idx)) {
   4530    return p->var;
   4531  }
   4532 
   4533  switch (opt_idx) {
   4534  // global option with local value: use local value if it's been set
   4535  case kOptEqualprg:
   4536    return *buf->b_p_ep != NUL ? &buf->b_p_ep : p->var;
   4537  case kOptKeywordprg:
   4538    return *buf->b_p_kp != NUL ? &buf->b_p_kp : p->var;
   4539  case kOptPath:
   4540    return *buf->b_p_path != NUL ? &(buf->b_p_path) : p->var;
   4541  case kOptAutocomplete:
   4542    return buf->b_p_ac >= 0 ? &(buf->b_p_ac) : p->var;
   4543  case kOptAutoread:
   4544    return buf->b_p_ar >= 0 ? &(buf->b_p_ar) : p->var;
   4545  case kOptTags:
   4546    return *buf->b_p_tags != NUL ? &(buf->b_p_tags) : p->var;
   4547  case kOptTagcase:
   4548    return *buf->b_p_tc != NUL ? &(buf->b_p_tc) : p->var;
   4549  case kOptSidescrolloff:
   4550    return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var;
   4551  case kOptScrolloff:
   4552    return win->w_p_so >= 0 ? &(win->w_p_so) : p->var;
   4553  case kOptBackupcopy:
   4554    return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var;
   4555  case kOptDefine:
   4556    return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var;
   4557  case kOptInclude:
   4558    return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var;
   4559  case kOptCompleteopt:
   4560    return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var;
   4561  case kOptDictionary:
   4562    return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var;
   4563  case kOptDiffanchors:
   4564    return *buf->b_p_dia != NUL ? &(buf->b_p_dia) : p->var;
   4565  case kOptThesaurus:
   4566    return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var;
   4567  case kOptThesaurusfunc:
   4568    return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var;
   4569  case kOptFormatprg:
   4570    return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var;
   4571  case kOptFsync:
   4572    return buf->b_p_fs >= 0 ? &(buf->b_p_fs) : p->var;
   4573  case kOptFindfunc:
   4574    return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var;
   4575  case kOptErrorformat:
   4576    return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
   4577  case kOptGrepformat:
   4578    return *buf->b_p_gefm != NUL ? &(buf->b_p_gefm) : p->var;
   4579  case kOptGrepprg:
   4580    return *buf->b_p_gp != NUL ? &(buf->b_p_gp) : p->var;
   4581  case kOptMakeprg:
   4582    return *buf->b_p_mp != NUL ? &(buf->b_p_mp) : p->var;
   4583  case kOptShowbreak:
   4584    return *win->w_p_sbr != NUL ? &(win->w_p_sbr) : p->var;
   4585  case kOptStatusline:
   4586    return *win->w_p_stl != NUL ? &(win->w_p_stl) : p->var;
   4587  case kOptWinbar:
   4588    return *win->w_p_wbr != NUL ? &(win->w_p_wbr) : p->var;
   4589  case kOptUndolevels:
   4590    return buf->b_p_ul != NO_LOCAL_UNDOLEVEL ? &(buf->b_p_ul) : p->var;
   4591  case kOptLispwords:
   4592    return *buf->b_p_lw != NUL ? &(buf->b_p_lw) : p->var;
   4593  case kOptMakeencoding:
   4594    return *buf->b_p_menc != NUL ? &(buf->b_p_menc) : p->var;
   4595  case kOptFillchars:
   4596    return *win->w_p_fcs != NUL ? &(win->w_p_fcs) : p->var;
   4597  case kOptListchars:
   4598    return *win->w_p_lcs != NUL ? &(win->w_p_lcs) : p->var;
   4599  case kOptVirtualedit:
   4600    return *win->w_p_ve != NUL ? &win->w_p_ve : p->var;
   4601 
   4602  case kOptArabic:
   4603    return &(win->w_p_arab);
   4604  case kOptList:
   4605    return &(win->w_p_list);
   4606  case kOptSpell:
   4607    return &(win->w_p_spell);
   4608  case kOptCursorcolumn:
   4609    return &(win->w_p_cuc);
   4610  case kOptCursorline:
   4611    return &(win->w_p_cul);
   4612  case kOptCursorlineopt:
   4613    return &(win->w_p_culopt);
   4614  case kOptColorcolumn:
   4615    return &(win->w_p_cc);
   4616  case kOptDiff:
   4617    return &(win->w_p_diff);
   4618  case kOptEventignorewin:
   4619    return &(win->w_p_eiw);
   4620  case kOptFoldcolumn:
   4621    return &(win->w_p_fdc);
   4622  case kOptFoldenable:
   4623    return &(win->w_p_fen);
   4624  case kOptFoldignore:
   4625    return &(win->w_p_fdi);
   4626  case kOptFoldlevel:
   4627    return &(win->w_p_fdl);
   4628  case kOptFoldmethod:
   4629    return &(win->w_p_fdm);
   4630  case kOptFoldminlines:
   4631    return &(win->w_p_fml);
   4632  case kOptFoldnestmax:
   4633    return &(win->w_p_fdn);
   4634  case kOptFoldexpr:
   4635    return &(win->w_p_fde);
   4636  case kOptFoldtext:
   4637    return &(win->w_p_fdt);
   4638  case kOptFoldmarker:
   4639    return &(win->w_p_fmr);
   4640  case kOptNumber:
   4641    return &(win->w_p_nu);
   4642  case kOptRelativenumber:
   4643    return &(win->w_p_rnu);
   4644  case kOptNumberwidth:
   4645    return &(win->w_p_nuw);
   4646  case kOptWinfixbuf:
   4647    return &(win->w_p_wfb);
   4648  case kOptWinfixheight:
   4649    return &(win->w_p_wfh);
   4650  case kOptWinfixwidth:
   4651    return &(win->w_p_wfw);
   4652  case kOptPreviewwindow:
   4653    return &(win->w_p_pvw);
   4654  case kOptLhistory:
   4655    return &(win->w_p_lhi);
   4656  case kOptRightleft:
   4657    return &(win->w_p_rl);
   4658  case kOptRightleftcmd:
   4659    return &(win->w_p_rlc);
   4660  case kOptScroll:
   4661    return &(win->w_p_scr);
   4662  case kOptSmoothscroll:
   4663    return &(win->w_p_sms);
   4664  case kOptWrap:
   4665    return &(win->w_p_wrap);
   4666  case kOptLinebreak:
   4667    return &(win->w_p_lbr);
   4668  case kOptBreakindent:
   4669    return &(win->w_p_bri);
   4670  case kOptBreakindentopt:
   4671    return &(win->w_p_briopt);
   4672  case kOptScrollbind:
   4673    return &(win->w_p_scb);
   4674  case kOptCursorbind:
   4675    return &(win->w_p_crb);
   4676  case kOptConcealcursor:
   4677    return &(win->w_p_cocu);
   4678  case kOptConceallevel:
   4679    return &(win->w_p_cole);
   4680 
   4681  case kOptAutoindent:
   4682    return &(buf->b_p_ai);
   4683  case kOptBinary:
   4684    return &(buf->b_p_bin);
   4685  case kOptBomb:
   4686    return &(buf->b_p_bomb);
   4687  case kOptBufhidden:
   4688    return &(buf->b_p_bh);
   4689  case kOptBuftype:
   4690    return &(buf->b_p_bt);
   4691  case kOptBuflisted:
   4692    return &(buf->b_p_bl);
   4693  case kOptBusy:
   4694    return &(buf->b_p_busy);
   4695  case kOptChannel:
   4696    return &(buf->b_p_channel);
   4697  case kOptCopyindent:
   4698    return &(buf->b_p_ci);
   4699  case kOptCindent:
   4700    return &(buf->b_p_cin);
   4701  case kOptCinkeys:
   4702    return &(buf->b_p_cink);
   4703  case kOptCinoptions:
   4704    return &(buf->b_p_cino);
   4705  case kOptCinscopedecls:
   4706    return &(buf->b_p_cinsd);
   4707  case kOptCinwords:
   4708    return &(buf->b_p_cinw);
   4709  case kOptComments:
   4710    return &(buf->b_p_com);
   4711  case kOptCommentstring:
   4712    return &(buf->b_p_cms);
   4713  case kOptComplete:
   4714    return &(buf->b_p_cpt);
   4715 #ifdef BACKSLASH_IN_FILENAME
   4716  case kOptCompleteslash:
   4717    return &(buf->b_p_csl);
   4718 #endif
   4719  case kOptCompletefunc:
   4720    return &(buf->b_p_cfu);
   4721  case kOptOmnifunc:
   4722    return &(buf->b_p_ofu);
   4723  case kOptEndoffile:
   4724    return &(buf->b_p_eof);
   4725  case kOptEndofline:
   4726    return &(buf->b_p_eol);
   4727  case kOptFixendofline:
   4728    return &(buf->b_p_fixeol);
   4729  case kOptExpandtab:
   4730    return &(buf->b_p_et);
   4731  case kOptFileencoding:
   4732    return &(buf->b_p_fenc);
   4733  case kOptFileformat:
   4734    return &(buf->b_p_ff);
   4735  case kOptFiletype:
   4736    return &(buf->b_p_ft);
   4737  case kOptFormatoptions:
   4738    return &(buf->b_p_fo);
   4739  case kOptFormatlistpat:
   4740    return &(buf->b_p_flp);
   4741  case kOptIminsert:
   4742    return &(buf->b_p_iminsert);
   4743  case kOptImsearch:
   4744    return &(buf->b_p_imsearch);
   4745  case kOptInfercase:
   4746    return &(buf->b_p_inf);
   4747  case kOptIskeyword:
   4748    return &(buf->b_p_isk);
   4749  case kOptIncludeexpr:
   4750    return &(buf->b_p_inex);
   4751  case kOptIndentexpr:
   4752    return &(buf->b_p_inde);
   4753  case kOptIndentkeys:
   4754    return &(buf->b_p_indk);
   4755  case kOptFormatexpr:
   4756    return &(buf->b_p_fex);
   4757  case kOptLisp:
   4758    return &(buf->b_p_lisp);
   4759  case kOptLispoptions:
   4760    return &(buf->b_p_lop);
   4761  case kOptModeline:
   4762    return &(buf->b_p_ml);
   4763  case kOptMatchpairs:
   4764    return &(buf->b_p_mps);
   4765  case kOptModifiable:
   4766    return &(buf->b_p_ma);
   4767  case kOptModified:
   4768    return &(buf->b_changed);
   4769  case kOptNrformats:
   4770    return &(buf->b_p_nf);
   4771  case kOptPreserveindent:
   4772    return &(buf->b_p_pi);
   4773  case kOptQuoteescape:
   4774    return &(buf->b_p_qe);
   4775  case kOptReadonly:
   4776    return &(buf->b_p_ro);
   4777  case kOptScrollback:
   4778    return &(buf->b_p_scbk);
   4779  case kOptSmartindent:
   4780    return &(buf->b_p_si);
   4781  case kOptSofttabstop:
   4782    return &(buf->b_p_sts);
   4783  case kOptSuffixesadd:
   4784    return &(buf->b_p_sua);
   4785  case kOptSwapfile:
   4786    return &(buf->b_p_swf);
   4787  case kOptSynmaxcol:
   4788    return &(buf->b_p_smc);
   4789  case kOptSyntax:
   4790    return &(buf->b_p_syn);
   4791  case kOptSpellcapcheck:
   4792    return &(win->w_s->b_p_spc);
   4793  case kOptSpellfile:
   4794    return &(win->w_s->b_p_spf);
   4795  case kOptSpelllang:
   4796    return &(win->w_s->b_p_spl);
   4797  case kOptSpelloptions:
   4798    return &(win->w_s->b_p_spo);
   4799  case kOptShiftwidth:
   4800    return &(buf->b_p_sw);
   4801  case kOptTagfunc:
   4802    return &(buf->b_p_tfu);
   4803  case kOptTabstop:
   4804    return &(buf->b_p_ts);
   4805  case kOptTextwidth:
   4806    return &(buf->b_p_tw);
   4807  case kOptUndofile:
   4808    return &(buf->b_p_udf);
   4809  case kOptWrapmargin:
   4810    return &(buf->b_p_wm);
   4811  case kOptVarsofttabstop:
   4812    return &(buf->b_p_vsts);
   4813  case kOptVartabstop:
   4814    return &(buf->b_p_vts);
   4815  case kOptKeymap:
   4816    return &(buf->b_p_keymap);
   4817  case kOptSigncolumn:
   4818    return &(win->w_p_scl);
   4819  case kOptWinhighlight:
   4820    return &(win->w_p_winhl);
   4821  case kOptWinblend:
   4822    return &(win->w_p_winbl);
   4823  case kOptStatuscolumn:
   4824    return &(win->w_p_stc);
   4825  default:
   4826    iemsg(_("E356: get_varp ERROR"));
   4827  }
   4828  // always return a valid pointer to avoid a crash!
   4829  return &(buf->b_p_wm);
   4830 }
   4831 
   4832 /// Get option index from option pointer
   4833 static inline OptIndex get_opt_idx(vimoption_T *opt)
   4834  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
   4835 {
   4836  return (OptIndex)(opt - options);
   4837 }
   4838 
   4839 /// Get pointer to option variable.
   4840 static inline void *get_varp(vimoption_T *p)
   4841 {
   4842  return get_varp_from(p, curbuf, curwin);
   4843 }
   4844 
   4845 /// Get the value of 'equalprg', either the buffer-local one or the global one.
   4846 char *get_equalprg(void)
   4847 {
   4848  if (*curbuf->b_p_ep == NUL) {
   4849    return p_ep;
   4850  }
   4851  return curbuf->b_p_ep;
   4852 }
   4853 
   4854 /// Get the value of 'findfunc', either the buffer-local one or the global one.
   4855 char *get_findfunc(void)
   4856 {
   4857  if (*curbuf->b_p_ffu == NUL) {
   4858    return p_ffu;
   4859  }
   4860  return curbuf->b_p_ffu;
   4861 }
   4862 
   4863 /// Copy options from one window to another.
   4864 /// Used when splitting a window.
   4865 void win_copy_options(win_T *wp_from, win_T *wp_to)
   4866 {
   4867  copy_winopt(&wp_from->w_onebuf_opt, &wp_to->w_onebuf_opt);
   4868  copy_winopt(&wp_from->w_allbuf_opt, &wp_to->w_allbuf_opt);
   4869  didset_window_options(wp_to, true);
   4870 }
   4871 
   4872 static char *copy_option_val(const char *val)
   4873 {
   4874  if (val == empty_string_option) {
   4875    return empty_string_option;  // no need to allocate memory
   4876  }
   4877  return xstrdup(val);
   4878 }
   4879 
   4880 /// Copy the options from one winopt_T to another.
   4881 /// Doesn't free the old option values in "to", use clear_winopt() for that.
   4882 /// The 'scroll' option is not copied, because it depends on the window height.
   4883 /// The 'previewwindow' option is reset, there can be only one preview window.
   4884 void copy_winopt(winopt_T *from, winopt_T *to)
   4885 {
   4886  to->wo_arab = from->wo_arab;
   4887  to->wo_list = from->wo_list;
   4888  to->wo_lcs = copy_option_val(from->wo_lcs);
   4889  to->wo_fcs = copy_option_val(from->wo_fcs);
   4890  to->wo_nu = from->wo_nu;
   4891  to->wo_rnu = from->wo_rnu;
   4892  to->wo_ve = copy_option_val(from->wo_ve);
   4893  to->wo_ve_flags = from->wo_ve_flags;
   4894  to->wo_nuw = from->wo_nuw;
   4895  to->wo_rl = from->wo_rl;
   4896  to->wo_rlc = copy_option_val(from->wo_rlc);
   4897  to->wo_sbr = copy_option_val(from->wo_sbr);
   4898  to->wo_stl = copy_option_val(from->wo_stl);
   4899  to->wo_wbr = copy_option_val(from->wo_wbr);
   4900  to->wo_wrap = from->wo_wrap;
   4901  to->wo_wrap_save = from->wo_wrap_save;
   4902  to->wo_lbr = from->wo_lbr;
   4903  to->wo_bri = from->wo_bri;
   4904  to->wo_briopt = copy_option_val(from->wo_briopt);
   4905  to->wo_scb = from->wo_scb;
   4906  to->wo_scb_save = from->wo_scb_save;
   4907  to->wo_sms = from->wo_sms;
   4908  to->wo_crb = from->wo_crb;
   4909  to->wo_crb_save = from->wo_crb_save;
   4910  to->wo_siso = from->wo_siso;
   4911  to->wo_so = from->wo_so;
   4912  to->wo_spell = from->wo_spell;
   4913  to->wo_cuc = from->wo_cuc;
   4914  to->wo_cul = from->wo_cul;
   4915  to->wo_culopt = copy_option_val(from->wo_culopt);
   4916  to->wo_cc = copy_option_val(from->wo_cc);
   4917  to->wo_diff = from->wo_diff;
   4918  to->wo_diff_saved = from->wo_diff_saved;
   4919  to->wo_eiw = copy_option_val(from->wo_eiw);
   4920  to->wo_cocu = copy_option_val(from->wo_cocu);
   4921  to->wo_cole = from->wo_cole;
   4922  to->wo_fdc = copy_option_val(from->wo_fdc);
   4923  to->wo_fdc_save = from->wo_diff_saved ? xstrdup(from->wo_fdc_save) : empty_string_option;
   4924  to->wo_fen = from->wo_fen;
   4925  to->wo_fen_save = from->wo_fen_save;
   4926  to->wo_fdi = copy_option_val(from->wo_fdi);
   4927  to->wo_fml = from->wo_fml;
   4928  to->wo_fdl = from->wo_fdl;
   4929  to->wo_fdl_save = from->wo_fdl_save;
   4930  to->wo_fdm = copy_option_val(from->wo_fdm);
   4931  to->wo_fdm_save = from->wo_diff_saved ? xstrdup(from->wo_fdm_save) : empty_string_option;
   4932  to->wo_fdn = from->wo_fdn;
   4933  to->wo_fde = copy_option_val(from->wo_fde);
   4934  to->wo_fdt = copy_option_val(from->wo_fdt);
   4935  to->wo_fmr = copy_option_val(from->wo_fmr);
   4936  to->wo_scl = copy_option_val(from->wo_scl);
   4937  to->wo_lhi = from->wo_lhi;
   4938  to->wo_winhl = copy_option_val(from->wo_winhl);
   4939  to->wo_winbl = from->wo_winbl;
   4940  to->wo_stc = copy_option_val(from->wo_stc);
   4941 
   4942  to->wo_wrap_flags = from->wo_wrap_flags;
   4943  to->wo_stl_flags = from->wo_stl_flags;
   4944  to->wo_wbr_flags = from->wo_wbr_flags;
   4945  to->wo_fde_flags = from->wo_fde_flags;
   4946  to->wo_fdt_flags = from->wo_fdt_flags;
   4947 
   4948  // Copy the script context so that we know were the value was last set.
   4949  memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx));
   4950  check_winopt(to);             // don't want NULL pointers
   4951 }
   4952 
   4953 /// Check string options in a window for a NULL value.
   4954 static void check_win_options(win_T *win)
   4955 {
   4956  check_winopt(&win->w_onebuf_opt);
   4957  check_winopt(&win->w_allbuf_opt);
   4958 }
   4959 
   4960 /// Check for NULL pointers in a winopt_T and replace them with empty_string_option.
   4961 static void check_winopt(winopt_T *wop)
   4962 {
   4963  check_string_option(&wop->wo_fdc);
   4964  check_string_option(&wop->wo_fdc_save);
   4965  check_string_option(&wop->wo_fdi);
   4966  check_string_option(&wop->wo_fdm);
   4967  check_string_option(&wop->wo_fdm_save);
   4968  check_string_option(&wop->wo_fde);
   4969  check_string_option(&wop->wo_fdt);
   4970  check_string_option(&wop->wo_fmr);
   4971  check_string_option(&wop->wo_eiw);
   4972  check_string_option(&wop->wo_scl);
   4973  check_string_option(&wop->wo_rlc);
   4974  check_string_option(&wop->wo_sbr);
   4975  check_string_option(&wop->wo_stl);
   4976  check_string_option(&wop->wo_culopt);
   4977  check_string_option(&wop->wo_cc);
   4978  check_string_option(&wop->wo_cocu);
   4979  check_string_option(&wop->wo_briopt);
   4980  check_string_option(&wop->wo_winhl);
   4981  check_string_option(&wop->wo_lcs);
   4982  check_string_option(&wop->wo_fcs);
   4983  check_string_option(&wop->wo_ve);
   4984  check_string_option(&wop->wo_wbr);
   4985  check_string_option(&wop->wo_stc);
   4986 }
   4987 
   4988 /// Free the allocated memory inside a winopt_T.
   4989 void clear_winopt(winopt_T *wop)
   4990 {
   4991  clear_string_option(&wop->wo_fdc);
   4992  clear_string_option(&wop->wo_fdc_save);
   4993  clear_string_option(&wop->wo_fdi);
   4994  clear_string_option(&wop->wo_fdm);
   4995  clear_string_option(&wop->wo_fdm_save);
   4996  clear_string_option(&wop->wo_fde);
   4997  clear_string_option(&wop->wo_fdt);
   4998  clear_string_option(&wop->wo_fmr);
   4999  clear_string_option(&wop->wo_eiw);
   5000  clear_string_option(&wop->wo_scl);
   5001  clear_string_option(&wop->wo_rlc);
   5002  clear_string_option(&wop->wo_sbr);
   5003  clear_string_option(&wop->wo_stl);
   5004  clear_string_option(&wop->wo_culopt);
   5005  clear_string_option(&wop->wo_cc);
   5006  clear_string_option(&wop->wo_cocu);
   5007  clear_string_option(&wop->wo_briopt);
   5008  clear_string_option(&wop->wo_winhl);
   5009  clear_string_option(&wop->wo_lcs);
   5010  clear_string_option(&wop->wo_fcs);
   5011  clear_string_option(&wop->wo_ve);
   5012  clear_string_option(&wop->wo_wbr);
   5013  clear_string_option(&wop->wo_stc);
   5014 }
   5015 
   5016 void didset_window_options(win_T *wp, bool valid_cursor)
   5017 {
   5018  // Set w_leftcol or w_skipcol to zero.
   5019  if (wp->w_p_wrap) {
   5020    wp->w_leftcol = 0;
   5021  } else {
   5022    wp->w_skipcol = 0;
   5023  }
   5024  check_colorcolumn(NULL, wp);
   5025  briopt_check(NULL, wp);
   5026  fill_culopt_flags(NULL, wp);
   5027  set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0);
   5028  set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0);
   5029  parse_winhl_opt(NULL, wp);  // sets w_hl_needs_update also for w_p_winbl
   5030  check_blending(wp);
   5031  set_winbar_win(wp, false, valid_cursor);
   5032  check_signcolumn(NULL, wp);
   5033  wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
   5034 }
   5035 
   5036 #define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].script_ctx
   5037 
   5038 /// Copy global option values to local options for one buffer.
   5039 /// Used when creating a new buffer and sometimes when entering a buffer.
   5040 /// flags:
   5041 /// BCO_ENTER    We will enter the buffer "buf".
   5042 /// BCO_ALWAYS   Always copy the options, but only set b_p_initialized when
   5043 ///      appropriate.
   5044 /// BCO_NOHELP   Don't copy the values to a help buffer.
   5045 void buf_copy_options(buf_T *buf, int flags)
   5046 {
   5047  bool should_copy = true;
   5048  char *save_p_isk = NULL;           // init for GCC
   5049  bool did_isk = false;
   5050 
   5051  // Skip this when the option defaults have not been set yet.  Happens when
   5052  // main() allocates the first buffer.
   5053  if (p_cpo != NULL) {
   5054    //
   5055    // Always copy when entering and 'cpo' contains 'S'.
   5056    // Don't copy when already initialized.
   5057    // Don't copy when 'cpo' contains 's' and not entering.
   5058    //    'S'      BCO_ENTER  initialized  's'  should_copy
   5059    //    yes        yes          X         X      true
   5060    //    yes        no          yes        X      false
   5061    //    no          X          yes        X      false
   5062    //     X         no          no        yes     false
   5063    //     X         no          no        no      true
   5064    //    no         yes         no         X      true
   5065    ///
   5066    if ((vim_strchr(p_cpo, CPO_BUFOPTGLOB) == NULL || !(flags & BCO_ENTER))
   5067        && (buf->b_p_initialized
   5068            || (!(flags & BCO_ENTER)
   5069                && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) {
   5070      should_copy = false;
   5071    }
   5072 
   5073    if (should_copy || (flags & BCO_ALWAYS)) {
   5074      CLEAR_FIELD(buf->b_p_script_ctx);
   5075      // Don't copy the options specific to a help buffer when
   5076      // BCO_NOHELP is given or the options were initialized already
   5077      // (jumping back to a help file with CTRL-T or CTRL-O)
   5078      bool dont_do_help = ((flags & BCO_NOHELP) && buf->b_help) || buf->b_p_initialized;
   5079      if (dont_do_help) {               // don't free b_p_isk
   5080        save_p_isk = buf->b_p_isk;
   5081        buf->b_p_isk = NULL;
   5082      }
   5083      // Always free the allocated strings.  If not already initialized,
   5084      // reset 'readonly' and copy 'fileformat'.
   5085      if (!buf->b_p_initialized) {
   5086        free_buf_options(buf, true);
   5087        buf->b_p_ro = false;                    // don't copy readonly
   5088        buf->b_p_fenc = xstrdup(p_fenc);
   5089        switch (*p_ffs) {
   5090        case 'm':
   5091          buf->b_p_ff = xstrdup("mac");
   5092          break;
   5093        case 'd':
   5094          buf->b_p_ff = xstrdup("dos");
   5095          break;
   5096        case 'u':
   5097          buf->b_p_ff = xstrdup("unix");
   5098          break;
   5099        default:
   5100          buf->b_p_ff = xstrdup(p_ff);
   5101          break;
   5102        }
   5103        buf->b_p_bh = empty_string_option;
   5104        buf->b_p_bt = empty_string_option;
   5105      } else {
   5106        free_buf_options(buf, false);
   5107      }
   5108 
   5109      buf->b_p_ai = p_ai;
   5110      COPY_OPT_SCTX(buf, kBufOptAutoindent);
   5111      buf->b_p_ai_nopaste = p_ai_nopaste;
   5112      buf->b_p_sw = p_sw;
   5113      COPY_OPT_SCTX(buf, kBufOptShiftwidth);
   5114      buf->b_p_scbk = p_scbk;
   5115      COPY_OPT_SCTX(buf, kBufOptScrollback);
   5116      buf->b_p_tw = p_tw;
   5117      COPY_OPT_SCTX(buf, kBufOptTextwidth);
   5118      buf->b_p_tw_nopaste = p_tw_nopaste;
   5119      buf->b_p_tw_nobin = p_tw_nobin;
   5120      buf->b_p_wm = p_wm;
   5121      COPY_OPT_SCTX(buf, kBufOptWrapmargin);
   5122      buf->b_p_wm_nopaste = p_wm_nopaste;
   5123      buf->b_p_wm_nobin = p_wm_nobin;
   5124      buf->b_p_bin = p_bin;
   5125      COPY_OPT_SCTX(buf, kBufOptBinary);
   5126      buf->b_p_bomb = p_bomb;
   5127      COPY_OPT_SCTX(buf, kBufOptBomb);
   5128      buf->b_p_et = p_et;
   5129      COPY_OPT_SCTX(buf, kBufOptExpandtab);
   5130      buf->b_p_fixeol = p_fixeol;
   5131      COPY_OPT_SCTX(buf, kBufOptFixendofline);
   5132      buf->b_p_et_nobin = p_et_nobin;
   5133      buf->b_p_et_nopaste = p_et_nopaste;
   5134      buf->b_p_ml = p_ml;
   5135      COPY_OPT_SCTX(buf, kBufOptModeline);
   5136      buf->b_p_ml_nobin = p_ml_nobin;
   5137      buf->b_p_inf = p_inf;
   5138      COPY_OPT_SCTX(buf, kBufOptInfercase);
   5139      if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) {
   5140        buf->b_p_swf = false;
   5141      } else {
   5142        buf->b_p_swf = p_swf;
   5143        COPY_OPT_SCTX(buf, kBufOptSwapfile);
   5144      }
   5145      buf->b_p_cpt = xstrdup(p_cpt);
   5146      COPY_OPT_SCTX(buf, kBufOptComplete);
   5147      set_buflocal_cpt_callbacks(buf);
   5148 #ifdef BACKSLASH_IN_FILENAME
   5149      buf->b_p_csl = xstrdup(p_csl);
   5150      COPY_OPT_SCTX(buf, kBufOptCompleteslash);
   5151 #endif
   5152      buf->b_p_cfu = xstrdup(p_cfu);
   5153      COPY_OPT_SCTX(buf, kBufOptCompletefunc);
   5154      set_buflocal_cfu_callback(buf);
   5155      buf->b_p_ofu = xstrdup(p_ofu);
   5156      COPY_OPT_SCTX(buf, kBufOptOmnifunc);
   5157      set_buflocal_ofu_callback(buf);
   5158      buf->b_p_tfu = xstrdup(p_tfu);
   5159      COPY_OPT_SCTX(buf, kBufOptTagfunc);
   5160      set_buflocal_tfu_callback(buf);
   5161      buf->b_p_sts = p_sts;
   5162      COPY_OPT_SCTX(buf, kBufOptSofttabstop);
   5163      buf->b_p_sts_nopaste = p_sts_nopaste;
   5164      buf->b_p_vsts = xstrdup(p_vsts);
   5165      COPY_OPT_SCTX(buf, kBufOptVarsofttabstop);
   5166      if (p_vsts && p_vsts != empty_string_option) {
   5167        tabstop_set(p_vsts, &buf->b_p_vsts_array);
   5168      } else {
   5169        buf->b_p_vsts_array = NULL;
   5170      }
   5171      buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL;
   5172      buf->b_p_com = xstrdup(p_com);
   5173      COPY_OPT_SCTX(buf, kBufOptComments);
   5174      buf->b_p_cms = xstrdup(p_cms);
   5175      COPY_OPT_SCTX(buf, kBufOptCommentstring);
   5176      buf->b_p_fo = xstrdup(p_fo);
   5177      COPY_OPT_SCTX(buf, kBufOptFormatoptions);
   5178      buf->b_p_flp = xstrdup(p_flp);
   5179      COPY_OPT_SCTX(buf, kBufOptFormatlistpat);
   5180      buf->b_p_nf = xstrdup(p_nf);
   5181      COPY_OPT_SCTX(buf, kBufOptNrformats);
   5182      buf->b_p_mps = xstrdup(p_mps);
   5183      COPY_OPT_SCTX(buf, kBufOptMatchpairs);
   5184      buf->b_p_si = p_si;
   5185      COPY_OPT_SCTX(buf, kBufOptSmartindent);
   5186      buf->b_p_channel = 0;
   5187      buf->b_p_ci = p_ci;
   5188 
   5189      COPY_OPT_SCTX(buf, kBufOptCopyindent);
   5190      buf->b_p_cin = p_cin;
   5191      COPY_OPT_SCTX(buf, kBufOptCindent);
   5192      buf->b_p_cink = xstrdup(p_cink);
   5193      COPY_OPT_SCTX(buf, kBufOptCinkeys);
   5194      buf->b_p_cino = xstrdup(p_cino);
   5195      COPY_OPT_SCTX(buf, kBufOptCinoptions);
   5196      buf->b_p_cinsd = xstrdup(p_cinsd);
   5197      COPY_OPT_SCTX(buf, kBufOptCinscopedecls);
   5198      buf->b_p_lop = xstrdup(p_lop);
   5199      COPY_OPT_SCTX(buf, kBufOptLispoptions);
   5200 
   5201      // Don't copy 'filetype', it must be detected
   5202      buf->b_p_ft = empty_string_option;
   5203      buf->b_p_pi = p_pi;
   5204      COPY_OPT_SCTX(buf, kBufOptPreserveindent);
   5205      buf->b_p_cinw = xstrdup(p_cinw);
   5206      COPY_OPT_SCTX(buf, kBufOptCinwords);
   5207      buf->b_p_lisp = p_lisp;
   5208      COPY_OPT_SCTX(buf, kBufOptLisp);
   5209      // Don't copy 'syntax', it must be set
   5210      buf->b_p_syn = empty_string_option;
   5211      buf->b_p_smc = p_smc;
   5212      COPY_OPT_SCTX(buf, kBufOptSynmaxcol);
   5213      buf->b_s.b_syn_isk = empty_string_option;
   5214      buf->b_s.b_p_spc = xstrdup(p_spc);
   5215      COPY_OPT_SCTX(buf, kBufOptSpellcapcheck);
   5216      compile_cap_prog(&buf->b_s);
   5217      buf->b_s.b_p_spf = xstrdup(p_spf);
   5218      COPY_OPT_SCTX(buf, kBufOptSpellfile);
   5219      buf->b_s.b_p_spl = xstrdup(p_spl);
   5220      COPY_OPT_SCTX(buf, kBufOptSpelllang);
   5221      buf->b_s.b_p_spo = xstrdup(p_spo);
   5222      COPY_OPT_SCTX(buf, kBufOptSpelloptions);
   5223      buf->b_s.b_p_spo_flags = spo_flags;
   5224      buf->b_p_inde = xstrdup(p_inde);
   5225      COPY_OPT_SCTX(buf, kBufOptIndentexpr);
   5226      buf->b_p_indk = xstrdup(p_indk);
   5227      COPY_OPT_SCTX(buf, kBufOptIndentkeys);
   5228      buf->b_p_fp = empty_string_option;
   5229      buf->b_p_fex = xstrdup(p_fex);
   5230      COPY_OPT_SCTX(buf, kBufOptFormatexpr);
   5231      buf->b_p_sua = xstrdup(p_sua);
   5232      COPY_OPT_SCTX(buf, kBufOptSuffixesadd);
   5233      buf->b_p_keymap = xstrdup(p_keymap);
   5234      COPY_OPT_SCTX(buf, kBufOptKeymap);
   5235      buf->b_kmap_state |= KEYMAP_INIT;
   5236      // This isn't really an option, but copying the langmap and IME
   5237      // state from the current buffer is better than resetting it.
   5238      buf->b_p_iminsert = p_iminsert;
   5239      COPY_OPT_SCTX(buf, kBufOptIminsert);
   5240      buf->b_p_imsearch = p_imsearch;
   5241      COPY_OPT_SCTX(buf, kBufOptImsearch);
   5242 
   5243      // options that are normally global but also have a local value
   5244      // are not copied, start using the global value
   5245      buf->b_p_ac = -1;
   5246      buf->b_p_ar = -1;
   5247      buf->b_p_fs = -1;
   5248      buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
   5249      buf->b_p_bkc = empty_string_option;
   5250      buf->b_bkc_flags = 0;
   5251      buf->b_p_gefm = empty_string_option;
   5252      buf->b_p_gp = empty_string_option;
   5253      buf->b_p_mp = empty_string_option;
   5254      buf->b_p_efm = empty_string_option;
   5255      buf->b_p_ep = empty_string_option;
   5256      buf->b_p_ffu = empty_string_option;
   5257      buf->b_p_kp = empty_string_option;
   5258      buf->b_p_path = empty_string_option;
   5259      buf->b_p_tags = empty_string_option;
   5260      buf->b_p_tc = empty_string_option;
   5261      buf->b_tc_flags = 0;
   5262      buf->b_p_def = empty_string_option;
   5263      buf->b_p_inc = empty_string_option;
   5264      buf->b_p_inex = xstrdup(p_inex);
   5265      COPY_OPT_SCTX(buf, kBufOptIncludeexpr);
   5266      buf->b_p_cot = empty_string_option;
   5267      buf->b_cot_flags = 0;
   5268      buf->b_p_dict = empty_string_option;
   5269      buf->b_p_dia = empty_string_option;
   5270      buf->b_p_tsr = empty_string_option;
   5271      buf->b_p_tsrfu = empty_string_option;
   5272      buf->b_p_qe = xstrdup(p_qe);
   5273      COPY_OPT_SCTX(buf, kBufOptQuoteescape);
   5274      buf->b_p_udf = p_udf;
   5275      COPY_OPT_SCTX(buf, kBufOptUndofile);
   5276      buf->b_p_lw = empty_string_option;
   5277      buf->b_p_menc = empty_string_option;
   5278 
   5279      // Don't copy the options set by ex_help(), use the saved values,
   5280      // when going from a help buffer to a non-help buffer.
   5281      // Don't touch these at all when BCO_NOHELP is used and going from
   5282      // or to a help buffer.
   5283      if (dont_do_help) {
   5284        buf->b_p_isk = save_p_isk;
   5285        if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
   5286          tabstop_set(p_vts, &buf->b_p_vts_array);
   5287        } else {
   5288          buf->b_p_vts_array = NULL;
   5289        }
   5290      } else {
   5291        buf->b_p_isk = xstrdup(p_isk);
   5292        COPY_OPT_SCTX(buf, kBufOptIskeyword);
   5293        did_isk = true;
   5294        buf->b_p_ts = p_ts;
   5295        COPY_OPT_SCTX(buf, kBufOptTabstop);
   5296        buf->b_p_vts = xstrdup(p_vts);
   5297        COPY_OPT_SCTX(buf, kBufOptVartabstop);
   5298        if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
   5299          tabstop_set(p_vts, &buf->b_p_vts_array);
   5300        } else {
   5301          buf->b_p_vts_array = NULL;
   5302        }
   5303        buf->b_help = false;
   5304        if (buf->b_p_bt[0] == 'h') {
   5305          clear_string_option(&buf->b_p_bt);
   5306        }
   5307        buf->b_p_ma = p_ma;
   5308        COPY_OPT_SCTX(buf, kBufOptModifiable);
   5309      }
   5310    }
   5311 
   5312    // When the options should be copied (ignoring BCO_ALWAYS), set the
   5313    // flag that indicates that the options have been initialized.
   5314    if (should_copy) {
   5315      buf->b_p_initialized = true;
   5316    }
   5317  }
   5318 
   5319  check_buf_options(buf);           // make sure we don't have NULLs
   5320  if (did_isk) {
   5321    buf_init_chartab(buf, false);
   5322  }
   5323 }
   5324 
   5325 /// Reset the 'modifiable' option and its default value.
   5326 void reset_modifiable(void)
   5327 {
   5328  curbuf->b_p_ma = false;
   5329  p_ma = false;
   5330  change_option_default(kOptModifiable, BOOLEAN_OPTVAL(false));
   5331 }
   5332 
   5333 /// Set the global value for 'iminsert' to the local value.
   5334 void set_iminsert_global(buf_T *buf)
   5335 {
   5336  p_iminsert = buf->b_p_iminsert;
   5337 }
   5338 
   5339 /// Set the global value for 'imsearch' to the local value.
   5340 void set_imsearch_global(buf_T *buf)
   5341 {
   5342  p_imsearch = buf->b_p_imsearch;
   5343 }
   5344 
   5345 static OptIndex expand_option_idx = kOptInvalid;
   5346 static int expand_option_start_col = 0;
   5347 static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
   5348 static int expand_option_flags = 0;
   5349 static bool expand_option_append = false;
   5350 
   5351 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   5352 void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
   5353 {
   5354  expand_option_flags = opt_flags;
   5355 
   5356  xp->xp_context = EXPAND_SETTINGS;
   5357  if (*arg == NUL) {
   5358    xp->xp_pattern = arg;
   5359    return;
   5360  }
   5361  char *const argend = arg + strlen(arg);
   5362  char *p = argend - 1;
   5363  if (*p == ' ' && *(p - 1) != '\\') {
   5364    xp->xp_pattern = p + 1;
   5365    return;
   5366  }
   5367  while (p > arg) {
   5368    char *s = p;
   5369    // count number of backslashes before ' ' or ','
   5370    if (*p == ' ' || *p == ',') {
   5371      while (s > arg && *(s - 1) == '\\') {
   5372        s--;
   5373      }
   5374    }
   5375    // break at a space with an even number of backslashes
   5376    if (*p == ' ' && ((p - s) & 1) == 0) {
   5377      p++;
   5378      break;
   5379    }
   5380    p--;
   5381  }
   5382  if (strncmp(p, "no", 2) == 0) {
   5383    xp->xp_context = EXPAND_BOOL_SETTINGS;
   5384    xp->xp_prefix = XP_PREFIX_NO;
   5385    p += 2;
   5386  } else if (strncmp(p, "inv", 3) == 0) {
   5387    xp->xp_context = EXPAND_BOOL_SETTINGS;
   5388    xp->xp_prefix = XP_PREFIX_INV;
   5389    p += 3;
   5390  }
   5391  xp->xp_pattern = p;
   5392  arg = p;
   5393 
   5394  char nextchar;
   5395  uint32_t flags = 0;
   5396  OptIndex opt_idx = 0;
   5397  bool is_term_option = false;
   5398 
   5399  if (*arg == '<') {
   5400    while (*p != '>') {
   5401      if (*p++ == NUL) {            // expand terminal option name
   5402        return;
   5403      }
   5404    }
   5405    int key = get_special_key_code(arg + 1);
   5406    if (key == 0) {                 // unknown name
   5407      xp->xp_context = EXPAND_NOTHING;
   5408      return;
   5409    }
   5410    nextchar = *++p;
   5411    is_term_option = true;
   5412    expand_option_name[2] = (char)(uint8_t)KEY2TERMCAP0(key);
   5413    expand_option_name[3] = (char)(uint8_t)KEY2TERMCAP1(key);
   5414  } else {
   5415    if (p[0] == 't' && p[1] == '_') {
   5416      p += 2;
   5417      if (*p != NUL) {
   5418        p++;
   5419      }
   5420      if (*p == NUL) {
   5421        return;                 // expand option name
   5422      }
   5423      nextchar = *++p;
   5424      is_term_option = true;
   5425      expand_option_name[2] = p[-2];
   5426      expand_option_name[3] = p[-1];
   5427    } else {
   5428      // Allow * wildcard.
   5429      while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') {
   5430        p++;
   5431      }
   5432      if (*p == NUL) {
   5433        return;
   5434      }
   5435      nextchar = *p;
   5436      opt_idx = find_option_len(arg, (size_t)(p - arg));
   5437      if (opt_idx == kOptInvalid || is_option_hidden(opt_idx)) {
   5438        xp->xp_context = EXPAND_NOTHING;
   5439        return;
   5440      }
   5441      flags = options[opt_idx].flags;
   5442      if (option_has_type(opt_idx, kOptValTypeBoolean)) {
   5443        xp->xp_context = EXPAND_NOTHING;
   5444        return;
   5445      }
   5446    }
   5447  }
   5448  // handle "-=" and "+="
   5449  expand_option_append = false;
   5450  bool expand_option_subtract = false;
   5451  if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') {
   5452    if (nextchar == '-') {
   5453      expand_option_subtract = true;
   5454    }
   5455    if (nextchar == '+' || nextchar == '^') {
   5456      expand_option_append = true;
   5457    }
   5458    p++;
   5459    nextchar = '=';
   5460  }
   5461  if ((nextchar != '=' && nextchar != ':')
   5462      || xp->xp_context == EXPAND_BOOL_SETTINGS) {
   5463    xp->xp_context = EXPAND_UNSUCCESSFUL;
   5464    return;
   5465  }
   5466 
   5467  // Below are for handling expanding a specific option's value after the '=' or ':'
   5468 
   5469  if (is_term_option) {
   5470    expand_option_idx = kOptInvalid;
   5471  } else {
   5472    expand_option_idx = opt_idx;
   5473  }
   5474 
   5475  xp->xp_pattern = p + 1;
   5476  expand_option_start_col = (int)(p + 1 - xp->xp_line);
   5477 
   5478  // Certain options currently have special case handling to reuse the
   5479  // expansion logic with other commands.
   5480  if (options[opt_idx].var == &p_syn) {
   5481    xp->xp_context = EXPAND_OWNSYNTAX;
   5482    return;
   5483  }
   5484  if (options[opt_idx].var == &p_ft) {
   5485    xp->xp_context = EXPAND_FILETYPE;
   5486    return;
   5487  }
   5488  if (options[opt_idx].var == &p_keymap) {
   5489    xp->xp_context = EXPAND_KEYMAP;
   5490    return;
   5491  }
   5492 
   5493  // Now pick. If the option has a custom expander, use that. Otherwise, just
   5494  // fill with the existing option value.
   5495  if (expand_option_subtract) {
   5496    xp->xp_context = EXPAND_SETTING_SUBTRACT;
   5497    return;
   5498  } else if (expand_option_idx != kOptInvalid && options[expand_option_idx].opt_expand_cb != NULL) {
   5499    xp->xp_context = EXPAND_STRING_SETTING;
   5500  } else if (*xp->xp_pattern == NUL) {
   5501    xp->xp_context = EXPAND_OLD_SETTING;
   5502    return;
   5503  } else {
   5504    xp->xp_context = EXPAND_NOTHING;
   5505  }
   5506 
   5507  if (is_term_option || option_has_type(opt_idx, kOptValTypeNumber)) {
   5508    return;
   5509  }
   5510 
   5511  // Only string options below
   5512 
   5513  // Options that have kOptFlagExpand are considered to all use file/dir expansion.
   5514  if (flags & kOptFlagExpand) {
   5515    p = options[opt_idx].var;
   5516    if (p == (char *)&p_bdir
   5517        || p == (char *)&p_dir
   5518        || p == (char *)&p_path
   5519        || p == (char *)&p_pp
   5520        || p == (char *)&p_rtp
   5521        || p == (char *)&p_cdpath
   5522        || p == (char *)&p_vdir) {
   5523      xp->xp_context = EXPAND_DIRECTORIES;
   5524      if (p == (char *)&p_path || p == (char *)&p_cdpath) {
   5525        xp->xp_backslash = XP_BS_THREE;
   5526      } else {
   5527        xp->xp_backslash = XP_BS_ONE;
   5528      }
   5529    } else {
   5530      xp->xp_context = EXPAND_FILES;
   5531      // for 'tags' need three backslashes for a space
   5532      if (p == (char *)&p_tags) {
   5533        xp->xp_backslash = XP_BS_THREE;
   5534      } else {
   5535        xp->xp_backslash = XP_BS_ONE;
   5536      }
   5537    }
   5538    if (flags & kOptFlagComma) {
   5539      xp->xp_backslash |= XP_BS_COMMA;
   5540    }
   5541  }
   5542 
   5543  // For an option that is a list of file names, or comma/colon-separated
   5544  // values, split it by the delimiter and find the start of the current
   5545  // pattern, while accounting for backslash-escaped space/commas/colons.
   5546  // Triple-backslashed escaped file names (e.g. 'path') can also be
   5547  // delimited by space.
   5548  if ((flags & kOptFlagExpand) || (flags & kOptFlagComma) || (flags & kOptFlagColon)) {
   5549    for (p = argend - 1; p > xp->xp_pattern; p--) {
   5550      // count number of backslashes before ' ' or ','
   5551      if (*p == ' ' || *p == ',' || (*p == ':' && (flags & kOptFlagColon))) {
   5552        char *s = p;
   5553        while (s > xp->xp_pattern && *(s - 1) == '\\') {
   5554          s--;
   5555        }
   5556        if ((*p == ' ' && ((xp->xp_backslash & XP_BS_THREE) && (p - s) < 3))
   5557 #if defined(BACKSLASH_IN_FILENAME)
   5558            || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 1)
   5559 #else
   5560            || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 2)
   5561 #endif
   5562            || (*p == ':' && (flags & kOptFlagColon))) {
   5563          xp->xp_pattern = p + 1;
   5564          break;
   5565        }
   5566      }
   5567    }
   5568  }
   5569 
   5570  // An option that is a list of single-character flags should always start
   5571  // at the end as we don't complete words.
   5572  if (flags & kOptFlagFlagList) {
   5573    xp->xp_pattern = argend;
   5574  }
   5575 
   5576  // Some options can either be using file/dir expansions, or custom value
   5577  // expansion depending on what the user typed. Unfortunately we have to
   5578  // manually handle it here to make sure we have the correct xp_context set.
   5579  // for 'spellsuggest' start at "file:"
   5580  if (options[opt_idx].var == &p_sps) {
   5581    if (strncmp(xp->xp_pattern, "file:", 5) == 0) {
   5582      xp->xp_pattern += 5;
   5583      return;
   5584    } else if (options[expand_option_idx].opt_expand_cb != NULL) {
   5585      xp->xp_context = EXPAND_STRING_SETTING;
   5586    }
   5587  }
   5588 }
   5589 
   5590 /// Returns true if "str" either matches "regmatch" or fuzzy matches "pat".
   5591 ///
   5592 /// If "test_only" is true and "fuzzy" is false and if "str" matches the regular
   5593 /// expression "regmatch", then returns true.  Otherwise returns false.
   5594 ///
   5595 /// If "test_only" is false and "fuzzy" is false and if "str" matches the
   5596 /// regular expression "regmatch", then stores the match in matches[idx] and
   5597 /// returns true.
   5598 ///
   5599 /// If "test_only" is true and "fuzzy" is true and if "str" fuzzy matches
   5600 /// "fuzzystr", then returns true. Otherwise returns false.
   5601 ///
   5602 /// If "test_only" is false and "fuzzy" is true and if "str" fuzzy matches
   5603 /// "fuzzystr", then stores the match details in fuzmatch[idx] and returns true.
   5604 static bool match_str(char *const str, regmatch_T *const regmatch, char **const matches,
   5605                      const int idx, const bool test_only, const bool fuzzy,
   5606                      const char *const fuzzystr, fuzmatch_str_T *const fuzmatch)
   5607 {
   5608  if (!fuzzy) {
   5609    if (vim_regexec(regmatch, str, 0)) {
   5610      if (!test_only) {
   5611        matches[idx] = xstrdup(str);
   5612      }
   5613      return true;
   5614    }
   5615  } else {
   5616    const int score = fuzzy_match_str(str, fuzzystr);
   5617    if (score != FUZZY_SCORE_NONE) {
   5618      if (!test_only) {
   5619        fuzmatch[idx].idx = idx;
   5620        fuzmatch[idx].str = xstrdup(str);
   5621        fuzmatch[idx].score = score;
   5622      }
   5623      return true;
   5624    }
   5625  }
   5626  return false;
   5627 }
   5628 
   5629 int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numMatches,
   5630                   char ***matches, const bool can_fuzzy)
   5631 {
   5632  int num_normal = 0;  // Nr of matching non-term-code settings
   5633  int count = 0;
   5634  static char *(names[]) = { "all" };
   5635  int ic = regmatch->rm_ic;  // remember the ignore-case flag
   5636 
   5637  fuzmatch_str_T *fuzmatch = NULL;
   5638  const bool fuzzy = can_fuzzy && cmdline_fuzzy_complete(fuzzystr);
   5639 
   5640  // do this loop twice:
   5641  // loop == 0: count the number of matching options
   5642  // loop == 1: copy the matching options into allocated memory
   5643  for (int loop = 0; loop <= 1; loop++) {
   5644    regmatch->rm_ic = ic;
   5645    if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
   5646      for (int match = 0; match < (int)ARRAY_SIZE(names);
   5647           match++) {
   5648        if (match_str(names[match], regmatch, *matches,
   5649                      count, (loop == 0), fuzzy, fuzzystr, fuzmatch)) {
   5650          if (loop == 0) {
   5651            num_normal++;
   5652          } else {
   5653            count++;
   5654          }
   5655        }
   5656      }
   5657    }
   5658    char *str;
   5659    for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   5660      str = options[opt_idx].fullname;
   5661      if (is_option_hidden(opt_idx)) {
   5662        continue;
   5663      }
   5664      if (xp->xp_context == EXPAND_BOOL_SETTINGS
   5665          && !(option_has_type(opt_idx, kOptValTypeBoolean))) {
   5666        continue;
   5667      }
   5668 
   5669      if (match_str(str, regmatch, *matches, count, (loop == 0),
   5670                    fuzzy, fuzzystr, fuzmatch)) {
   5671        if (loop == 0) {
   5672          num_normal++;
   5673        } else {
   5674          count++;
   5675        }
   5676      } else if (!fuzzy && options[opt_idx].shortname != NULL
   5677                 && vim_regexec(regmatch, options[opt_idx].shortname, 0)) {
   5678        // Compare against the abbreviated option name (for regular
   5679        // expression match). Fuzzy matching (previous if) already
   5680        // matches against both the expanded and abbreviated names.
   5681        if (loop == 0) {
   5682          num_normal++;
   5683        } else {
   5684          (*matches)[count++] = xstrdup(str);
   5685        }
   5686      }
   5687    }
   5688 
   5689    if (loop == 0) {
   5690      if (num_normal > 0) {
   5691        *numMatches = num_normal;
   5692      } else {
   5693        return OK;
   5694      }
   5695      if (!fuzzy) {
   5696        *matches = xmalloc((size_t)(*numMatches) * sizeof(char *));
   5697      } else {
   5698        fuzmatch = xmalloc((size_t)(*numMatches) * sizeof(fuzmatch_str_T));
   5699      }
   5700    }
   5701  }
   5702 
   5703  if (fuzzy) {
   5704    fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
   5705  }
   5706 
   5707  return OK;
   5708 }
   5709 
   5710 /// Escape an option value that can be used on the command-line with :set.
   5711 /// Caller needs to free the returned string, unless NULL is returned.
   5712 static char *escape_option_str_cmdline(char *var)
   5713 {
   5714  // A backslash is required before some characters.  This is the reverse of
   5715  // what happens in do_set().
   5716  char *buf = vim_strsave_escaped(var, escape_chars);
   5717 
   5718 #ifdef BACKSLASH_IN_FILENAME
   5719  // For MS-Windows et al. we don't double backslashes at the start and
   5720  // before a file name character.
   5721  // The reverse is found at stropt_copy_value().
   5722  for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
   5723    if (var[0] == '\\' && var[1] == '\\'
   5724        && expand_option_idx != kOptInvalid
   5725        && (options[expand_option_idx].flags & kOptFlagExpand)
   5726        && vim_isfilec((uint8_t)var[2])
   5727        && (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
   5728      STRMOVE(var, var + 1);
   5729    }
   5730  }
   5731 #endif
   5732  return buf;
   5733 }
   5734 
   5735 /// Expansion handler for :set= when we just want to fill in with the existing value.
   5736 int ExpandOldSetting(int *numMatches, char ***matches)
   5737 {
   5738  char *var = NULL;
   5739 
   5740  *numMatches = 0;
   5741  *matches = xmalloc(sizeof(char *));
   5742 
   5743  // For a terminal key code expand_option_idx is kOptInvalid.
   5744  if (expand_option_idx == kOptInvalid) {
   5745    expand_option_idx = find_option(expand_option_name);
   5746  }
   5747 
   5748  if (expand_option_idx != kOptInvalid) {
   5749    // Put string of option value in NameBuff.
   5750    option_value2string(&options[expand_option_idx], expand_option_flags);
   5751    var = NameBuff;
   5752  } else {
   5753    var = "";
   5754  }
   5755 
   5756  char *buf = escape_option_str_cmdline(var);
   5757 
   5758  (*matches)[0] = buf;
   5759  *numMatches = 1;
   5760  return OK;
   5761 }
   5762 
   5763 /// Expansion handler for :set=/:set+= when the option has a custom expansion handler.
   5764 int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches)
   5765 {
   5766  if (expand_option_idx == kOptInvalid || options[expand_option_idx].opt_expand_cb == NULL) {
   5767    // Not supposed to reach this. This function is only for options with
   5768    // custom expansion callbacks.
   5769    return FAIL;
   5770  }
   5771 
   5772  optexpand_T args = {
   5773    .oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
   5774    .oe_idx = expand_option_idx,
   5775    .oe_append = expand_option_append,
   5776    .oe_regmatch = regmatch,
   5777    .oe_xp = xp,
   5778    .oe_set_arg = xp->xp_line + expand_option_start_col,
   5779  };
   5780  args.oe_include_orig_val = !expand_option_append && (*args.oe_set_arg == NUL);
   5781 
   5782  // Retrieve the existing value, but escape it as a reverse of setting it.
   5783  // We technically only need to do this when oe_append or
   5784  // oe_include_orig_val is true.
   5785  option_value2string(&options[expand_option_idx], expand_option_flags);
   5786  char *var = NameBuff;
   5787  char *buf = escape_option_str_cmdline(var);
   5788  args.oe_opt_value = buf;
   5789 
   5790  int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches);
   5791 
   5792  xfree(buf);
   5793  return num_ret;
   5794 }
   5795 
   5796 /// Expansion handler for :set-=
   5797 int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches)
   5798 {
   5799  if (expand_option_idx == kOptInvalid) {
   5800    // term option
   5801    return ExpandOldSetting(numMatches, matches);
   5802  }
   5803 
   5804  char *option_val = *(char **)get_option_varp_scope_from(expand_option_idx,
   5805                                                          expand_option_flags,
   5806                                                          curbuf, curwin);
   5807 
   5808  uint32_t option_flags = options[expand_option_idx].flags;
   5809 
   5810  if (option_has_type(expand_option_idx, kOptValTypeNumber)) {
   5811    return ExpandOldSetting(numMatches, matches);
   5812  } else if (option_flags & kOptFlagComma) {
   5813    // Split the option by comma, then present each option to the user if
   5814    // it matches the pattern.
   5815    // This condition needs to go first, because 'whichwrap' has both
   5816    // kOptFlagComma and kOptFlagFlagList.
   5817 
   5818    if (*option_val == NUL) {
   5819      return FAIL;
   5820    }
   5821 
   5822    // Make a copy as we need to inject null characters destructively.
   5823    char *option_copy = xstrdup(option_val);
   5824    char *next_val = option_copy;
   5825 
   5826    garray_T ga;
   5827    ga_init(&ga, sizeof(char *), 10);
   5828 
   5829    do {
   5830      char *item = next_val;
   5831      char *comma = vim_strchr(next_val, ',');
   5832      while (comma != NULL && comma != next_val && *(comma - 1) == '\\') {
   5833        // "\," is interpreted as a literal comma rather than option
   5834        // separator when reading options in copy_option_part(). Skip
   5835        // it.
   5836        comma = vim_strchr(comma + 1, ',');
   5837      }
   5838      if (comma != NULL) {
   5839        *comma = NUL;  // null-terminate this value, required by later functions
   5840        next_val = comma + 1;
   5841      } else {
   5842        next_val = NULL;
   5843      }
   5844 
   5845      if (*item == NUL) {
   5846        // empty value, don't add to list
   5847        continue;
   5848      }
   5849 
   5850      if (!vim_regexec(regmatch, item, 0)) {
   5851        continue;
   5852      }
   5853 
   5854      char *buf = escape_option_str_cmdline(item);
   5855      GA_APPEND(char *, &ga, buf);
   5856    } while (next_val != NULL);
   5857 
   5858    xfree(option_copy);
   5859 
   5860    *matches = ga.ga_data;
   5861    *numMatches = ga.ga_len;
   5862    return OK;
   5863  } else if (option_flags & kOptFlagFlagList) {
   5864    // Only present the flags that are set on the option as the other flags
   5865    // are not meaningful to do set-= on.
   5866 
   5867    if (*xp->xp_pattern != NUL) {
   5868      // Don't suggest anything if cmdline is non-empty. Vim's set-=
   5869      // behavior requires consecutive strings and it's usually
   5870      // unintuitive to users if they try to subtract multiple flags at
   5871      // once.
   5872      return FAIL;
   5873    }
   5874 
   5875    size_t num_flags = strlen(option_val);
   5876    if (num_flags == 0) {
   5877      return FAIL;
   5878    }
   5879 
   5880    *matches = xmalloc(sizeof(char *) * (num_flags + 1));
   5881 
   5882    int count = 0;
   5883 
   5884    (*matches)[count++] = xmemdupz(option_val, num_flags);
   5885 
   5886    if (num_flags > 1) {
   5887      // If more than one flags, split the flags up and expose each
   5888      // character as individual choice.
   5889      for (char *flag = option_val; *flag != NUL; flag++) {
   5890        (*matches)[count++] = xmemdupz(flag, 1);
   5891      }
   5892    }
   5893 
   5894    *numMatches = count;
   5895    return OK;
   5896  }
   5897 
   5898  return ExpandOldSetting(numMatches, matches);
   5899 }
   5900 
   5901 /// Get the value for the numeric or string option///opp in a nice format into
   5902 /// NameBuff[].  Must not be called with a hidden option!
   5903 ///
   5904 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   5905 ///
   5906 /// TODO(famiu): Replace this with optval_to_cstr() if possible.
   5907 static void option_value2string(vimoption_T *opt, int opt_flags)
   5908 {
   5909  void *varp = get_varp_scope(opt, opt_flags);
   5910  assert(varp != NULL);
   5911 
   5912  if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
   5913    OptInt wc = 0;
   5914 
   5915    if (wc_use_keyname(varp, &wc)) {
   5916      xstrlcpy(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
   5917    } else if (wc != 0) {
   5918      xstrlcpy(NameBuff, transchar((int)wc), sizeof(NameBuff));
   5919    } else {
   5920      snprintf(NameBuff,
   5921               sizeof(NameBuff),
   5922               "%" PRId64,
   5923               (int64_t)(*(OptInt *)varp));
   5924    }
   5925  } else {  // string
   5926    varp = *(char **)(varp);
   5927 
   5928    if (opt->flags & kOptFlagExpand) {
   5929      home_replace(NULL, varp, NameBuff, MAXPATHL, false);
   5930    } else {
   5931      xstrlcpy(NameBuff, varp, MAXPATHL);
   5932    }
   5933  }
   5934 }
   5935 
   5936 /// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be
   5937 /// printed as a keyname.
   5938 /// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
   5939 static int wc_use_keyname(const void *varp, OptInt *wcp)
   5940 {
   5941  if (((OptInt *)varp == &p_wc) || ((OptInt *)varp == &p_wcm)) {
   5942    *wcp = *(OptInt *)varp;
   5943    if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)(*wcp)) >= 0) {
   5944      return true;
   5945    }
   5946  }
   5947  return false;
   5948 }
   5949 
   5950 /// @returns true if "x" is present in 'shortmess' option, or
   5951 /// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
   5952 bool shortmess(int x)
   5953 {
   5954  return (p_shm != NULL
   5955          && (vim_strchr(p_shm, x) != NULL
   5956              || (vim_strchr(p_shm, 'a') != NULL
   5957                  && vim_strchr(SHM_ALL_ABBREVIATIONS, x) != NULL)));
   5958 }
   5959 
   5960 /// vimrc_found() - Called when a vimrc or "VIMINIT" has been found.
   5961 ///
   5962 /// Set the values for options that didn't get set yet to the defaults.
   5963 /// When "fname" is not NULL, use it to set $"envname" when it wasn't set yet.
   5964 void vimrc_found(char *fname, char *envname)
   5965 {
   5966  if (fname != NULL && envname != NULL) {
   5967    char *p = vim_getenv(envname);
   5968    if (p == NULL) {
   5969      // Set $MYVIMRC to the first vimrc file found.
   5970      p = FullName_save(fname, false);
   5971      if (p != NULL) {
   5972        os_setenv(envname, p, 1);
   5973        xfree(p);
   5974      }
   5975    } else {
   5976      xfree(p);
   5977    }
   5978  }
   5979 }
   5980 
   5981 /// Check whether global option has been set.
   5982 ///
   5983 /// @param[in]  name  Option name.
   5984 ///
   5985 /// @return True if option was set.
   5986 bool option_was_set(OptIndex opt_idx)
   5987 {
   5988  assert(opt_idx != kOptInvalid);
   5989  return options[opt_idx].flags & kOptFlagWasSet;
   5990 }
   5991 
   5992 /// Reset the flag indicating option "name" was set.
   5993 ///
   5994 /// @param[in]  name  Option name.
   5995 void reset_option_was_set(OptIndex opt_idx)
   5996 {
   5997  assert(opt_idx != kOptInvalid);
   5998  options[opt_idx].flags &= ~(unsigned)kOptFlagWasSet;
   5999 }
   6000 
   6001 /// fill_culopt_flags() -- called when 'culopt' changes value
   6002 int fill_culopt_flags(char *val, win_T *wp)
   6003 {
   6004  char *p;
   6005  uint8_t culopt_flags_new = 0;
   6006 
   6007  if (val == NULL) {
   6008    p = wp->w_p_culopt;
   6009  } else {
   6010    p = val;
   6011  }
   6012  while (*p != NUL) {
   6013    // Note: Keep this in sync with opt_culopt_values.
   6014    if (strncmp(p, "line", 4) == 0) {
   6015      p += 4;
   6016      culopt_flags_new |= kOptCuloptFlagLine;
   6017    } else if (strncmp(p, "both", 4) == 0) {
   6018      p += 4;
   6019      culopt_flags_new |= kOptCuloptFlagLine | kOptCuloptFlagNumber;
   6020    } else if (strncmp(p, "number", 6) == 0) {
   6021      p += 6;
   6022      culopt_flags_new |= kOptCuloptFlagNumber;
   6023    } else if (strncmp(p, "screenline", 10) == 0) {
   6024      p += 10;
   6025      culopt_flags_new |= kOptCuloptFlagScreenline;
   6026    }
   6027 
   6028    if (*p != ',' && *p != NUL) {
   6029      return FAIL;
   6030    }
   6031    if (*p == ',') {
   6032      p++;
   6033    }
   6034  }
   6035 
   6036  // Can't have both "line" and "screenline".
   6037  if ((culopt_flags_new & kOptCuloptFlagLine) && (culopt_flags_new & kOptCuloptFlagScreenline)) {
   6038    return FAIL;
   6039  }
   6040  wp->w_p_culopt_flags = culopt_flags_new;
   6041 
   6042  return OK;
   6043 }
   6044 
   6045 /// Get the value of 'magic' taking "magic_overruled" into account.
   6046 bool magic_isset(void)
   6047 {
   6048  switch (magic_overruled) {
   6049  case OPTION_MAGIC_ON:
   6050    return true;
   6051  case OPTION_MAGIC_OFF:
   6052    return false;
   6053  case OPTION_MAGIC_NOT_SET:
   6054    break;
   6055  }
   6056  return p_magic;
   6057 }
   6058 
   6059 /// Set the callback function value for an option that accepts a function name,
   6060 /// lambda, et al. (e.g. 'operatorfunc', 'tagfunc', etc.)
   6061 /// @return  OK if the option is successfully set to a function, otherwise FAIL
   6062 int option_set_callback_func(char *optval, Callback *optcb)
   6063 {
   6064  if (optval == NULL || *optval == NUL) {
   6065    callback_free(optcb);
   6066    return OK;
   6067  }
   6068 
   6069  typval_T *tv;
   6070  if (*optval == '{'
   6071      || (strncmp(optval, "function(", 9) == 0)
   6072      || (strncmp(optval, "funcref(", 8) == 0)) {
   6073    // Lambda expression or a funcref
   6074    tv = eval_expr(optval, NULL);
   6075    if (tv == NULL) {
   6076      return FAIL;
   6077    }
   6078  } else {
   6079    // treat everything else as a function name string
   6080    tv = xcalloc(1, sizeof(*tv));
   6081    tv->v_type = VAR_STRING;
   6082    tv->vval.v_string = xstrdup(optval);
   6083  }
   6084 
   6085  Callback cb;
   6086  if (!callback_from_typval(&cb, tv) || cb.type == kCallbackNone) {
   6087    tv_free(tv);
   6088    return FAIL;
   6089  }
   6090 
   6091  callback_free(optcb);
   6092  *optcb = cb;
   6093  tv_free(tv);
   6094  return OK;
   6095 }
   6096 
   6097 static void didset_options_sctx(int opt_flags, int *buf)
   6098 {
   6099  for (int i = 0;; i++) {
   6100    if (buf[i] == kOptInvalid) {
   6101      break;
   6102    }
   6103 
   6104    set_option_sctx(buf[i], opt_flags, current_sctx);
   6105  }
   6106 }
   6107 
   6108 /// Check if backspacing over something is allowed.
   6109 /// @param  what  BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP
   6110 bool can_bs(int what)
   6111 {
   6112  if (what == BS_START && bt_prompt(curbuf)) {
   6113    return false;
   6114  }
   6115 
   6116  // support for number values was removed but we keep '2' since it is used in
   6117  // legacy tests
   6118  if (*p_bs == '2') {
   6119    return what != BS_NOSTOP;
   6120  }
   6121 
   6122  return vim_strchr(p_bs, what) != NULL;
   6123 }
   6124 
   6125 /// Get the local or global value of 'backupcopy' flags.
   6126 ///
   6127 /// @param buf The buffer.
   6128 unsigned get_bkc_flags(buf_T *buf)
   6129 {
   6130  return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
   6131 }
   6132 
   6133 /// Get the local or global value of 'formatlistpat'.
   6134 ///
   6135 /// @param buf The buffer.
   6136 char *get_flp_value(buf_T *buf)
   6137 {
   6138  if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) {
   6139    return p_flp;
   6140  }
   6141  return buf->b_p_flp;
   6142 }
   6143 
   6144 /// Get the local or global value of 'virtualedit' flags.
   6145 unsigned get_ve_flags(win_T *wp)
   6146 {
   6147  return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags)
   6148         & ~(unsigned)(kOptVeFlagNone | kOptVeFlagNoneU);
   6149 }
   6150 
   6151 /// Get the local or global value of 'showbreak'.
   6152 ///
   6153 /// @param win  If not NULL, the window to get the local option from; global
   6154 ///             otherwise.
   6155 char *get_showbreak_value(win_T *const win)
   6156  FUNC_ATTR_WARN_UNUSED_RESULT
   6157 {
   6158  if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) {
   6159    return p_sbr;
   6160  }
   6161  if (strcmp(win->w_p_sbr, "NONE") == 0) {
   6162    return empty_string_option;
   6163  }
   6164  return win->w_p_sbr;
   6165 }
   6166 
   6167 /// Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC.
   6168 int get_fileformat(const buf_T *buf)
   6169  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   6170 {
   6171  int c = (unsigned char)(*buf->b_p_ff);
   6172 
   6173  if (buf->b_p_bin || c == 'u') {
   6174    return EOL_UNIX;
   6175  }
   6176  if (c == 'm') {
   6177    return EOL_MAC;
   6178  }
   6179  return EOL_DOS;
   6180 }
   6181 
   6182 /// Like get_fileformat(), but override 'fileformat' with "p" for "++opt=val"
   6183 /// argument.
   6184 ///
   6185 /// @param eap  can be NULL!
   6186 int get_fileformat_force(const buf_T *buf, const exarg_T *eap)
   6187  FUNC_ATTR_NONNULL_ARG(1)
   6188 {
   6189  int c;
   6190 
   6191  if (eap != NULL && eap->force_ff != 0) {
   6192    c = eap->force_ff;
   6193  } else {
   6194    if ((eap != NULL && eap->force_bin != 0)
   6195        ? (eap->force_bin == FORCE_BIN) : buf->b_p_bin) {
   6196      return EOL_UNIX;
   6197    }
   6198    c = (unsigned char)(*buf->b_p_ff);
   6199  }
   6200  if (c == 'u') {
   6201    return EOL_UNIX;
   6202  }
   6203  if (c == 'm') {
   6204    return EOL_MAC;
   6205  }
   6206  return EOL_DOS;
   6207 }
   6208 
   6209 /// Return the default fileformat from 'fileformats'.
   6210 int default_fileformat(void)
   6211 {
   6212  switch (*p_ffs) {
   6213  case 'm':
   6214    return EOL_MAC;
   6215  case 'd':
   6216    return EOL_DOS;
   6217  }
   6218  return EOL_UNIX;
   6219 }
   6220 
   6221 /// Set the current end-of-line type to EOL_UNIX, EOL_MAC, or EOL_DOS.
   6222 ///
   6223 /// Sets 'fileformat'.
   6224 ///
   6225 /// @param eol_style End-of-line style.
   6226 /// @param  opt_flags  Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
   6227 void set_fileformat(int eol_style, int opt_flags)
   6228 {
   6229  char *p = NULL;
   6230 
   6231  switch (eol_style) {
   6232  case EOL_UNIX:
   6233    p = "unix";
   6234    break;
   6235  case EOL_MAC:
   6236    p = "mac";
   6237    break;
   6238  case EOL_DOS:
   6239    p = "dos";
   6240    break;
   6241  }
   6242 
   6243  // p is NULL if "eol_style" is EOL_UNKNOWN.
   6244  if (p != NULL) {
   6245    set_option_direct(kOptFileformat, CSTR_AS_OPTVAL(p), opt_flags, 0);
   6246  }
   6247 
   6248  // This may cause the buffer to become (un)modified.
   6249  redraw_buf_status_later(curbuf);
   6250  redraw_tabline = true;
   6251  need_maketitle = true;  // Set window title later.
   6252 }
   6253 
   6254 /// Skip to next part of an option argument: skip space and comma
   6255 char *skip_to_option_part(const char *p)
   6256 {
   6257  if (*p == ',') {
   6258    p++;
   6259  }
   6260  while (*p == ' ') {
   6261    p++;
   6262  }
   6263  return (char *)p;
   6264 }
   6265 
   6266 /// Isolate one part of a string option separated by `sep_chars`.
   6267 ///
   6268 /// @param[in,out]  option    advanced to the next part
   6269 /// @param[in,out]  buf       copy of the isolated part
   6270 /// @param[in]      maxlen    length of `buf`
   6271 /// @param[in]      sep_chars chars that separate the option parts
   6272 ///
   6273 /// @return length of `*option`
   6274 size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars)
   6275 {
   6276  size_t len = 0;
   6277  char *p = *option;
   6278 
   6279  // skip '.' at start of option part, for 'suffixes'
   6280  if (*p == '.') {
   6281    buf[len++] = *p++;
   6282  }
   6283  while (*p != NUL && vim_strchr(sep_chars, (uint8_t)(*p)) == NULL) {
   6284    // Skip backslash before a separator character and space.
   6285    if (p[0] == '\\' && vim_strchr(sep_chars, (uint8_t)p[1]) != NULL) {
   6286      p++;
   6287    }
   6288    if (len < maxlen - 1) {
   6289      buf[len++] = *p;
   6290    }
   6291    p++;
   6292  }
   6293  buf[len] = NUL;
   6294 
   6295  if (*p != NUL && *p != ',') {  // skip non-standard separator
   6296    p++;
   6297  }
   6298  p = skip_to_option_part(p);    // p points to next file name
   6299 
   6300  *option = p;
   6301  return len;
   6302 }
   6303 
   6304 /// Return true when 'shell' has "csh" in the tail.
   6305 int csh_like_shell(void)
   6306 {
   6307  return strstr(path_tail(p_sh), "csh") != NULL;
   6308 }
   6309 
   6310 /// Return true when 'shell' has "fish" in the tail.
   6311 bool fish_like_shell(void)
   6312 {
   6313  return strstr(path_tail(p_sh), "fish") != NULL;
   6314 }
   6315 
   6316 /// Get window or buffer local options
   6317 dict_T *get_winbuf_options(const int bufopt)
   6318  FUNC_ATTR_WARN_UNUSED_RESULT
   6319 {
   6320  dict_T *const d = tv_dict_alloc();
   6321 
   6322  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   6323    vimoption_T *opt = &options[opt_idx];
   6324 
   6325    if ((bufopt && (option_has_scope(opt_idx, kOptScopeBuf)))
   6326        || (!bufopt && (option_has_scope(opt_idx, kOptScopeWin)))) {
   6327      void *varp = get_varp(opt);
   6328 
   6329      if (varp != NULL) {
   6330        typval_T opt_tv = optval_as_tv(optval_from_varp(opt_idx, varp), true);
   6331        tv_dict_add_tv(d, opt->fullname, strlen(opt->fullname), &opt_tv);
   6332      }
   6333    }
   6334  }
   6335 
   6336  return d;
   6337 }
   6338 
   6339 /// Return the effective 'scrolloff' value for the current window, using the
   6340 /// global value when appropriate.
   6341 int get_scrolloff_value(win_T *wp)
   6342 {
   6343  // Disallow scrolloff in terminal-mode. #11915
   6344  // Still allow 'scrolloff' for non-terminal buffers. #34447
   6345  if ((State & MODE_TERMINAL) && wp->w_buffer->terminal) {
   6346    return 0;
   6347  }
   6348  return (int)(wp->w_p_so < 0 ? p_so : wp->w_p_so);
   6349 }
   6350 
   6351 /// Return the effective 'sidescrolloff' value for the current window, using the
   6352 /// global value when appropriate.
   6353 int get_sidescrolloff_value(win_T *wp)
   6354 {
   6355  return (int)(wp->w_p_siso < 0 ? p_siso : wp->w_p_siso);
   6356 }
   6357 
   6358 Dict get_vimoption(String name, int opt_flags, buf_T *buf, win_T *win, Arena *arena, Error *err)
   6359 {
   6360  OptIndex opt_idx = find_option_len(name.data, name.size);
   6361  VALIDATE_S(opt_idx != kOptInvalid, "option (not found)", name.data, {
   6362    return (Dict)ARRAY_DICT_INIT;
   6363  });
   6364 
   6365  return vimoption2dict(&options[opt_idx], opt_flags, buf, win, arena);
   6366 }
   6367 
   6368 Dict get_all_vimoptions(Arena *arena)
   6369 {
   6370  Dict retval = arena_dict(arena, kOptCount);
   6371  for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
   6372    Dict opt_dict = vimoption2dict(&options[opt_idx], OPT_GLOBAL, curbuf, curwin, arena);
   6373    PUT_C(retval, options[opt_idx].fullname, DICT_OBJ(opt_dict));
   6374  }
   6375  return retval;
   6376 }
   6377 
   6378 static Dict vimoption2dict(vimoption_T *opt, int opt_flags, buf_T *buf, win_T *win, Arena *arena)
   6379 {
   6380  OptIndex opt_idx = get_opt_idx(opt);
   6381  Dict dict = arena_dict(arena, 13);
   6382 
   6383  PUT_C(dict, "name", CSTR_AS_OBJ(opt->fullname));
   6384  PUT_C(dict, "shortname", CSTR_AS_OBJ(opt->shortname));
   6385 
   6386  const char *scope;
   6387  if (option_has_scope(opt_idx, kOptScopeBuf)) {
   6388    scope = "buf";
   6389  } else if (option_has_scope(opt_idx, kOptScopeWin)) {
   6390    scope = "win";
   6391  } else {
   6392    scope = "global";
   6393  }
   6394 
   6395  PUT_C(dict, "scope", CSTR_AS_OBJ(scope));
   6396 
   6397  // welcome to the jungle
   6398  PUT_C(dict, "global_local", BOOLEAN_OBJ(option_is_global_local(opt_idx)));
   6399  PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & kOptFlagComma));
   6400  PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & kOptFlagFlagList));
   6401 
   6402  PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet));
   6403 
   6404  sctx_T script_ctx = { .sc_sid = 0 };
   6405  if (opt_flags == OPT_GLOBAL) {
   6406    script_ctx = opt->script_ctx;
   6407  } else {
   6408    // Scope is either OPT_LOCAL or a fallback mode was requested.
   6409    if (option_has_scope(opt_idx, kOptScopeBuf)) {
   6410      script_ctx = buf->b_p_script_ctx[opt->scope_idx[kOptScopeBuf]];
   6411    }
   6412    if (option_has_scope(opt_idx, kOptScopeWin)) {
   6413      script_ctx = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]];
   6414    }
   6415    if (opt_flags != OPT_LOCAL && script_ctx.sc_sid == 0) {
   6416      script_ctx = opt->script_ctx;
   6417    }
   6418  }
   6419 
   6420  PUT_C(dict, "last_set_sid", INTEGER_OBJ(script_ctx.sc_sid));
   6421  PUT_C(dict, "last_set_linenr", INTEGER_OBJ(script_ctx.sc_lnum));
   6422  PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)script_ctx.sc_chan));
   6423 
   6424  PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(option_get_type(get_opt_idx(opt)))));
   6425  PUT_C(dict, "default", optval_as_object(opt->def_val));
   6426  PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & kOptFlagNoDup)));
   6427 
   6428  return dict;
   6429 }