1 // This is an open source non-commercial project. Dear PVS-Studio, please check
2 // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3 
4 // User-settable options. Checklist for adding a new option:
5 // - Put it in options.lua
6 // - For a global option: Add a variable for it in option_defs.h.
7 // - For a buffer or window local option:
8 //   - Add a BV_XX or WV_XX entry to option_defs.h
9 //   - Add a variable to the window or buffer struct in buffer_defs.h.
10 //   - For a window option, add some code to copy_winopt().
11 //   - For a window string option, add code to check_winopt()
12 //     and clear_winopt(). If setting the option needs parsing,
13 //     add some code to didset_window_options().
14 //   - For a buffer option, add some code to buf_copy_options().
15 //   - For a buffer string option, add code to check_buf_options().
16 // - If it's a numeric option, add any necessary bounds checks to
17 //   set_num_option().
18 // - If it's a list of flags, add some code in do_set(), search for WW_ALL.
19 // - Add documentation! doc/options.txt, and any other related places.
20 // - Add an entry in runtime/optwin.vim.
21 
22 #define IN_OPTION_C
23 #include <assert.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "nvim/ascii.h"
31 #include "nvim/buffer.h"
32 #include "nvim/charset.h"
33 #include "nvim/cursor.h"
34 #include "nvim/cursor_shape.h"
35 #include "nvim/diff.h"
36 #include "nvim/digraph.h"
37 #include "nvim/edit.h"
38 #include "nvim/eval.h"
39 #include "nvim/eval/typval.h"
40 #include "nvim/ex_cmds2.h"
41 #include "nvim/ex_docmd.h"
42 #include "nvim/ex_getln.h"
43 #include "nvim/ex_session.h"
44 #include "nvim/fileio.h"
45 #include "nvim/fold.h"
46 #include "nvim/garray.h"
47 #include "nvim/getchar.h"
48 #include "nvim/hardcopy.h"
49 #include "nvim/highlight.h"
50 #include "nvim/indent_c.h"
51 #include "nvim/keymap.h"
52 #include "nvim/macros.h"
53 #include "nvim/mbyte.h"
54 #include "nvim/memfile.h"
55 #include "nvim/memline.h"
56 #include "nvim/memory.h"
57 #include "nvim/message.h"
58 #include "nvim/misc1.h"
59 #include "nvim/mouse.h"
60 #include "nvim/move.h"
61 #include "nvim/normal.h"
62 #include "nvim/option.h"
63 #include "nvim/os/os.h"
64 #include "nvim/os_unix.h"
65 #include "nvim/path.h"
66 #include "nvim/popupmnu.h"
67 #include "nvim/regexp.h"
68 #include "nvim/runtime.h"
69 #include "nvim/screen.h"
70 #include "nvim/spell.h"
71 #include "nvim/spellfile.h"
72 #include "nvim/strings.h"
73 #include "nvim/syntax.h"
74 #include "nvim/ui.h"
75 #include "nvim/ui_compositor.h"
76 #include "nvim/undo.h"
77 #include "nvim/vim.h"
78 #include "nvim/window.h"
79 #ifdef WIN32
80 # include "nvim/os/pty_conpty_win.h"
81 #endif
82 #include "nvim/api/private/helpers.h"
83 #include "nvim/lua/executor.h"
84 #include "nvim/os/input.h"
85 #include "nvim/os/lang.h"
86 #include "nvim/quickfix.h"
87 
88 /*
89  * The options that are local to a window or buffer have "indir" set to one of
90  * these values.  Special values:
91  * PV_NONE: global option.
92  * PV_WIN is added: window-local option
93  * PV_BUF is added: buffer-local option
94  * PV_BOTH is added: global option which also has a local value.
95  */
96 #define PV_BOTH 0x1000
97 #define PV_WIN  0x2000
98 #define PV_BUF  0x4000
99 #define PV_MASK 0x0fff
100 #define OPT_WIN(x)  (idopt_T)(PV_WIN + (int)(x))
101 #define OPT_BUF(x)  (idopt_T)(PV_BUF + (int)(x))
102 #define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
103 
104 
105 // WV_ and BV_ values get typecasted to this for the "indir" field
106 typedef enum {
107   PV_NONE = 0,
108   PV_MAXVAL = 0xffff,  // to avoid warnings for value out of range
109 } idopt_T;
110 
111 /*
112  * Options local to a window have a value local to a buffer and global to all
113  * buffers.  Indicate this by setting "var" to VAR_WIN.
114  */
115 #define VAR_WIN ((char_u *)-1)
116 
117 static char *p_term = NULL;
118 static char *p_ttytype = NULL;
119 
120 /*
121  * These are the global values for options which are also local to a buffer.
122  * Only to be used in option.c!
123  */
124 static int p_ai;
125 static int p_bin;
126 static int p_bomb;
127 static char_u *p_bh;
128 static char_u *p_bt;
129 static int p_bl;
130 static long p_channel;
131 static int p_ci;
132 static int p_cin;
133 static char_u *p_cink;
134 static char_u *p_cino;
135 static char_u *p_cinw;
136 static char_u *p_com;
137 static char_u *p_cms;
138 static char_u *p_cpt;
139 static char_u *p_cfu;
140 static char_u *p_ofu;
141 static char_u *p_tfu;
142 static int p_eol;
143 static int p_fixeol;
144 static int p_et;
145 static char_u *p_fenc;
146 static char_u *p_ff;
147 static char_u *p_fo;
148 static char_u *p_flp;
149 static char_u *p_ft;
150 static long p_iminsert;
151 static long p_imsearch;
152 static char_u *p_inex;
153 static char_u *p_inde;
154 static char_u *p_indk;
155 static char_u *p_fex;
156 static int p_inf;
157 static char_u *p_isk;
158 static int p_lisp;
159 static int p_ml;
160 static int p_ma;
161 static int p_mod;
162 static char_u *p_mps;
163 static char_u *p_nf;
164 static int p_pi;
165 static char_u *p_qe;
166 static int p_ro;
167 static int p_si;
168 static long p_sts;
169 static char_u *p_sua;
170 static long p_sw;
171 static int p_swf;
172 static long p_smc;
173 static char_u *p_syn;
174 static char_u *p_spc;
175 static char_u *p_spf;
176 static char_u *p_spl;
177 static char_u *p_spo;
178 static long p_ts;
179 static long p_tw;
180 static int p_udf;
181 static long p_wm;
182 static char_u *p_vsts;
183 static char_u *p_vts;
184 static char_u *p_keymap;
185 
186 // Saved values for when 'bin' is set.
187 static int p_et_nobin;
188 static int p_ml_nobin;
189 static long p_tw_nobin;
190 static long p_wm_nobin;
191 
192 // Saved values for when 'paste' is set.
193 static int p_ai_nopaste;
194 static int p_et_nopaste;
195 static long p_sts_nopaste;
196 static long p_tw_nopaste;
197 static long p_wm_nopaste;
198 static char_u *p_vsts_nopaste;
199 
200 typedef struct vimoption {
201   char *fullname;        // full option name
202   char *shortname;       // permissible abbreviation
203   uint32_t flags;               // see below
204   char_u *var;             // global option: pointer to variable;
205                            // window-local option: VAR_WIN;
206                            // buffer-local option: global value
207   idopt_T indir;                // global option: PV_NONE;
208                                 // local option: indirect option index
209   char_u *def_val;         // default values for variable (neovim!!)
210   LastSet last_set;             // script in which the option was last set
211 } vimoption_T;
212 
213 
214 /*
215  * Flags
216  */
217 #define P_BOOL          0x01U    // the option is boolean
218 #define P_NUM           0x02U    // the option is numeric
219 #define P_STRING        0x04U    // the option is a string
220 #define P_ALLOCED       0x08U    // the string option is in allocated memory,
221                                  // must use free_string_option() when
222                                  // assigning new value. Not set if default is
223                                  // the same.
224 #define P_EXPAND        0x10U    // environment expansion.  NOTE: P_EXPAND can
225                                  // never be used for local or hidden options
226 #define P_NODEFAULT     0x40U    // don't set to default value
227 #define P_DEF_ALLOCED   0x80U    // default value is in allocated memory, must
228                                  // use free() when assigning new value
229 #define P_WAS_SET       0x100U   // option has been set/reset
230 #define P_NO_MKRC       0x200U   // don't include in :mkvimrc output
231 
232 // when option changed, what to display:
233 #define P_RSTAT         0x1000U  ///< redraw status lines
234 #define P_RWIN          0x2000U  ///< redraw current window and recompute text
235 #define P_RBUF          0x4000U  ///< redraw current buffer and recompute text
236 #define P_RALL          0x6000U  ///< redraw all windows
237 #define P_RCLR          0x7000U  ///< clear and redraw all
238 
239 #define P_COMMA         0x8000U    ///< comma separated list
240 #define P_ONECOMMA      0x18000U   ///< P_COMMA and cannot have two consecutive
241                                    ///< commas
242 #define P_NODUP         0x20000U   ///< don't allow duplicate strings
243 #define P_FLAGLIST      0x40000U   ///< list of single-char flags
244 
245 #define P_SECURE        0x80000U   ///< cannot change in modeline or secure mode
246 #define P_GETTEXT       0x100000U  ///< expand default value with _()
247 #define P_NOGLOB        0x200000U  ///< do not use local value for global vimrc
248 #define P_NFNAME        0x400000U  ///< only normal file name chars allowed
249 #define P_INSECURE      0x800000U  ///< option was set from a modeline
250 #define P_PRI_MKRC     0x1000000U  ///< priority for :mkvimrc (setting option
251                                    ///< has side effects)
252 #define P_NO_ML        0x2000000U  ///< not allowed in modeline
253 #define P_CURSWANT     0x4000000U  ///< update curswant required; not needed
254                                    ///< when there is a redraw flag
255 #define P_NO_DEF_EXP   0x8000000U  ///< Do not expand default value.
256 
257 #define P_RWINONLY     0x10000000U  ///< only redraw current window
258 #define P_NDNAME       0x20000000U  ///< only normal dir name chars allowed
259 #define P_UI_OPTION    0x40000000U  ///< send option to remote ui
260 #define P_MLE          0x80000000U  ///< under control of 'modelineexpr'
261 
262 #define HIGHLIGHT_INIT \
263   "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
264   "i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \
265   "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
266   "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
267   "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
268   "X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \
269   "q:QuickFixLine,0:Whitespace,I:NormalNC"
270 
271 /*
272  * options[] is initialized here.
273  * The order of the options MUST be alphabetic for ":set all" and findoption().
274  * All option names MUST start with a lowercase letter (for findoption()).
275  * Exception: "t_" options are at the end.
276  * The options with a NULL variable are 'hidden': a set command for them is
277  * ignored and they are not printed.
278  */
279 
280 #ifdef INCLUDE_GENERATED_DECLARATIONS
281 # include "options.generated.h"
282 #endif
283 
284 #define PARAM_COUNT ARRAY_SIZE(options)
285 
286 static char *(p_ambw_values[]) =      { "single", "double", NULL };
287 static char *(p_bg_values[]) =        { "light", "dark", NULL };
288 static char *(p_nf_values[]) =        { "bin", "octal", "hex", "alpha",
289                                         "unsigned", NULL };
290 static char *(p_ff_values[]) =        { FF_UNIX, FF_DOS, FF_MAC, NULL };
291 static char *(p_wak_values[]) =       { "yes", "menu", "no", NULL };
292 static char *(p_mousem_values[]) =    { "extend", "popup", "popup_setpos",
293                                         "mac", NULL };
294 static char *(p_sel_values[]) =       { "inclusive", "exclusive", "old", NULL };
295 static char *(p_slm_values[]) =       { "mouse", "key", "cmd", NULL };
296 static char *(p_km_values[]) =        { "startsel", "stopsel", NULL };
297 static char *(p_scbopt_values[]) =    { "ver", "hor", "jump", NULL };
298 static char *(p_debug_values[]) =     { "msg", "throw", "beep", NULL };
299 static char *(p_ead_values[]) =       { "both", "ver", "hor", NULL };
300 static char *(p_buftype_values[]) =   { "nofile", "nowrite", "quickfix",
301                                         "help", "acwrite", "terminal",
302                                         "prompt", NULL };
303 
304 static char *(p_bufhidden_values[]) = { "hide", "unload", "delete",
305                                         "wipe", NULL };
306 static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL };
307 static char *(p_fdm_values[]) =       { "manual", "expr", "marker", "indent",
308                                         "syntax",  "diff", NULL };
309 static char *(p_fcl_values[]) =       { "all", NULL };
310 static char *(p_cot_values[]) =       { "menu", "menuone", "longest", "preview",
311                                         "noinsert", "noselect", NULL };
312 #ifdef BACKSLASH_IN_FILENAME
313 static char *(p_csl_values[]) =       { "slash", "backslash", NULL };
314 #endif
315 static char *(p_icm_values[]) =       { "nosplit", "split", NULL };
316 static char *(p_scl_values[]) =       { "yes", "no", "auto", "auto:1", "auto:2",
317                                         "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8",
318                                         "auto:9",
319                                         "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6",
320                                         "yes:7", "yes:8",
321                                         "yes:9", "number", NULL };
322 static char *(p_fdc_values[]) =       { "auto", "auto:1", "auto:2",
323                                         "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8",
324                                         "auto:9",
325                                         "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL };
326 
327 /// All possible flags for 'shm'.
328 static char_u SHM_ALL[] = {
329   SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI,
330   SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER,
331   SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU,
332   SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT,
333   0,
334 };
335 
336 #ifdef INCLUDE_GENERATED_DECLARATIONS
337 # include "option.c.generated.h"
338 #endif
339 
340 /// Initialize the options, first part.
341 ///
342 /// Called only once from main(), just after creating the first buffer.
343 /// If "clean_arg" is true, Nvim was started with --clean.
344 ///
345 /// NOTE: ELOG() etc calls are not allowed here, as log location depends on
346 /// env var expansion which depends on expression evaluation and other
347 /// editor state initialized here. Do logging in set_init_2 or later.
set_init_1(bool clean_arg)348 void set_init_1(bool clean_arg)
349 {
350   int opt_idx;
351 
352   langmap_init();
353 
354   /*
355    * Find default value for 'shell' option.
356    * Don't use it if it is empty.
357    */
358   {
359     const char *shell = os_getenv("SHELL");
360     if (shell != NULL) {
361       if (vim_strchr((const char_u *)shell, ' ') != NULL) {
362         const size_t len = strlen(shell) + 3;  // two quotes and a trailing NUL
363         char *const cmd = xmalloc(len);
364         snprintf(cmd, len, "\"%s\"", shell);
365         set_string_default("sh", cmd, true);
366       } else {
367         set_string_default("sh", (char *)shell, false);
368       }
369     }
370   }
371 
372   /*
373    * Set the default for 'backupskip' to include environment variables for
374    * temp files.
375    */
376   {
377 #ifdef UNIX
378     static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" };
379 #else
380     static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" };
381 #endif
382     garray_T ga;
383     opt_idx = findoption("backupskip");
384 
385     ga_init(&ga, 1, 100);
386     for (size_t n = 0; n < ARRAY_SIZE(names); n++) {
387       bool mustfree = true;
388       char *p;
389 #ifdef UNIX
390       if (*names[n] == NUL) {
391 # ifdef __APPLE__
392         p = "/private/tmp";
393 # else
394         p = "/tmp";
395 # endif
396         mustfree = false;
397       } else
398 #endif
399       {
400         p = vim_getenv(names[n]);
401       }
402       if (p != NULL && *p != NUL) {
403         // First time count the NUL, otherwise count the ','.
404         const size_t len = strlen(p) + 3;
405         char *item = xmalloc(len);
406         xstrlcpy(item, p, len);
407         add_pathsep(item);
408         xstrlcat(item, "*", len);
409         if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags)
410             == NULL) {
411           ga_grow(&ga, (int)len);
412           if (!GA_EMPTY(&ga)) {
413             STRCAT(ga.ga_data, ",");
414           }
415           STRCAT(ga.ga_data, p);
416           add_pathsep(ga.ga_data);
417           STRCAT(ga.ga_data, "*");
418           ga.ga_len += (int)len;
419         }
420         xfree(item);
421       }
422       if (mustfree) {
423         xfree(p);
424       }
425     }
426     if (ga.ga_data != NULL) {
427       set_string_default("bsk", ga.ga_data, true);
428     }
429   }
430 
431   {
432     char_u *cdpath;
433     char_u *buf;
434     int i;
435     int j;
436 
437     // Initialize the 'cdpath' option's default value.
438     cdpath = (char_u *)vim_getenv("CDPATH");
439     if (cdpath != NULL) {
440       buf = xmalloc(2 * STRLEN(cdpath) + 2);
441       {
442         buf[0] = ',';               // start with ",", current dir first
443         j = 1;
444         for (i = 0; cdpath[i] != NUL; i++) {
445           if (vim_ispathlistsep(cdpath[i])) {
446             buf[j++] = ',';
447           } else {
448             if (cdpath[i] == ' ' || cdpath[i] == ',') {
449               buf[j++] = '\\';
450             }
451             buf[j++] = cdpath[i];
452           }
453         }
454         buf[j] = NUL;
455         opt_idx = findoption("cdpath");
456         if (opt_idx >= 0) {
457           options[opt_idx].def_val = buf;
458           options[opt_idx].flags |= P_DEF_ALLOCED;
459         } else {
460           xfree(buf);           // cannot happen
461         }
462       }
463       xfree(cdpath);
464     }
465   }
466 
467 #if defined(MSWIN) || defined(MAC)
468   // Set print encoding on platforms that don't default to latin1
469   set_string_default("printencoding", "hp-roman8", false);
470 #endif
471 
472   // 'printexpr' must be allocated to be able to evaluate it.
473   set_string_default("printexpr",
474 #ifdef UNIX
475                      "system(['lpr'] "
476                      "+ (empty(&printdevice)?[]:['-P', &printdevice]) "
477                      "+ [v:fname_in])"
478                      ". delete(v:fname_in)"
479                      "+ v:shell_error",
480 #elif defined(MSWIN)
481                      "system(['copy', v:fname_in, "
482                      "empty(&printdevice)?'LPT1':&printdevice])"
483                      ". delete(v:fname_in)",
484 #else
485                      "",
486 #endif
487                      false);
488 
489   char *backupdir = stdpaths_user_data_subpath("backup", 2, true);
490   const size_t backupdir_len = strlen(backupdir);
491   backupdir = xrealloc(backupdir, backupdir_len + 3);
492   memmove(backupdir + 2, backupdir, backupdir_len + 1);
493   memmove(backupdir, ".,", 2);
494   set_string_default("backupdir", backupdir, true);
495   set_string_default("viewdir", stdpaths_user_data_subpath("view", 2, true),
496                      true);
497   set_string_default("directory", stdpaths_user_data_subpath("swap", 2, true),
498                      true);
499   set_string_default("undodir", stdpaths_user_data_subpath("undo", 2, true),
500                      true);
501   // Set default for &runtimepath. All necessary expansions are performed in
502   // this function.
503   char *rtp = runtimepath_default(clean_arg);
504   if (rtp) {
505     set_string_default("runtimepath", rtp, true);
506     // Make a copy of 'rtp' for 'packpath'
507     set_string_default("packpath", rtp, false);
508     rtp = NULL;  // ownership taken
509   }
510 
511   /*
512    * Set all the options (except the terminal options) to their default
513    * value.  Also set the global value for local options.
514    */
515   set_options_default(0);
516 
517 
518   curbuf->b_p_initialized = true;
519   curbuf->b_p_ar = -1;          // no local 'autoread' value
520   curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL;
521   check_buf_options(curbuf);
522   check_win_options(curwin);
523   check_options();
524 
525   // Set all options to their default value
526   set_options_default(OPT_FREE);
527 
528   // set 'laststatus'
529   last_status(false);
530 
531   // Must be before option_expand(), because that one needs vim_isIDc()
532   didset_options();
533 
534   // Use the current chartab for the generic chartab. This is not in
535   // didset_options() because it only depends on 'encoding'.
536   init_spell_chartab();
537 
538   /*
539    * Expand environment variables and things like "~" for the defaults.
540    * If option_expand() returns non-NULL the variable is expanded.  This can
541    * only happen for non-indirect options.
542    * Also set the default to the expanded value, so ":set" does not list
543    * them.
544    * Don't set the P_ALLOCED flag, because we don't want to free the
545    * default.
546    */
547   for (opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
548     if (options[opt_idx].flags & P_NO_DEF_EXP) {
549       continue;
550     }
551     char *p;
552     if ((options[opt_idx].flags & P_GETTEXT)
553         && options[opt_idx].var != NULL) {
554       p = _(*(char **)options[opt_idx].var);
555     } else {
556       p = (char *)option_expand(opt_idx, NULL);
557     }
558     if (p != NULL) {
559       p = xstrdup(p);
560       *(char **)options[opt_idx].var = p;
561       if (options[opt_idx].flags & P_DEF_ALLOCED) {
562         xfree(options[opt_idx].def_val);
563       }
564       options[opt_idx].def_val = (char_u *)p;
565       options[opt_idx].flags |= P_DEF_ALLOCED;
566     }
567   }
568 
569   save_file_ff(curbuf);         // Buffer is unchanged
570 
571   // Detect use of mlterm.
572   // Mlterm is a terminal emulator akin to xterm that has some special
573   // abilities (bidi namely).
574   // NOTE: mlterm's author is being asked to 'set' a variable
575   //       instead of an environment variable due to inheritance.
576   if (os_env_exists("MLTERM")) {
577     set_option_value("tbidi", 1L, NULL, 0);
578   }
579 
580   didset_options2();
581 
582   lang_init();
583 
584   // enc_locale() will try to find the encoding of the current locale.
585   // This will be used when 'default' is used as encoding specifier
586   // in 'fileencodings'
587   char_u *p = enc_locale();
588   if (p == NULL) {
589     // use utf-8 as 'default' if locale encoding can't be detected.
590     p = (char_u *)xmemdupz(S_LEN("utf-8"));
591   }
592   fenc_default = p;
593 
594 #ifdef HAVE_WORKING_LIBINTL
595   // GNU gettext 0.10.37 supports this feature: set the codeset used for
596   // translated messages independently from the current locale.
597   (void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
598 #endif
599 
600   // Set the default for 'helplang'.
601   set_helplang_default(get_mess_lang());
602 }
603 
604 /// Set an option to its default value.
605 /// This does not take care of side effects!
606 ///
607 /// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
set_option_default(int opt_idx,int opt_flags)608 static void set_option_default(int opt_idx, int opt_flags)
609 {
610   char_u *varp;            // pointer to variable for current option
611   int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
612 
613   varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
614   uint32_t flags = options[opt_idx].flags;
615   if (varp != NULL) {       // skip hidden option, nothing to do for it
616     if (flags & P_STRING) {
617       // Use set_string_option_direct() for local options to handle
618       // freeing and allocating the value.
619       if (options[opt_idx].indir != PV_NONE) {
620         set_string_option_direct(NULL, opt_idx,
621                                  options[opt_idx].def_val, opt_flags, 0);
622       } else {
623         if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
624           free_string_option(*(char_u **)(varp));
625         }
626         *(char_u **)varp = options[opt_idx].def_val;
627         options[opt_idx].flags &= ~P_ALLOCED;
628       }
629     } else if (flags & P_NUM) {
630       if (options[opt_idx].indir == PV_SCROLL) {
631         win_comp_scroll(curwin);
632       } else {
633         long def_val = (long)options[opt_idx].def_val;
634         if ((long *)varp == &curwin->w_p_so
635             || (long *)varp == &curwin->w_p_siso) {
636           // 'scrolloff' and 'sidescrolloff' local values have a
637           // different default value than the global default.
638           *(long *)varp = -1;
639         } else {
640           *(long *)varp = def_val;
641         }
642         // May also set global value for local option.
643         if (both) {
644           *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
645             def_val;
646         }
647       }
648     } else {  // P_BOOL
649       *(int *)varp = (int)(intptr_t)options[opt_idx].def_val;
650 #ifdef UNIX
651       // 'modeline' defaults to off for root
652       if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) {
653         *(int *)varp = false;
654       }
655 #endif
656       // May also set global value for local option.
657       if (both) {
658         *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
659           *(int *)varp;
660       }
661     }
662 
663     // The default value is not insecure.
664     uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
665     *flagsp = *flagsp & ~P_INSECURE;
666   }
667 
668   set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
669 }
670 
671 /// Set all options (except terminal options) to their default value.
672 ///
673 /// @param opt_flags  OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
set_options_default(int opt_flags)674 static void set_options_default(int opt_flags)
675 {
676   for (int i = 0; options[i].fullname; i++) {
677     if (!(options[i].flags & P_NODEFAULT)) {
678       set_option_default(i, opt_flags);
679     }
680   }
681 
682   // The 'scroll' option must be computed for all windows.
683   FOR_ALL_TAB_WINDOWS(tp, wp) {
684     win_comp_scroll(wp);
685   }
686 
687   parse_cino(curbuf);
688 }
689 
690 /// Set the Vi-default value of a string option.
691 /// Used for 'sh', 'backupskip' and 'term'.
692 ///
693 /// @param name The name of the option
694 /// @param val The value of the option
695 /// @param allocated If true, do not copy default as it was already allocated.
set_string_default(const char * name,char * val,bool allocated)696 static void set_string_default(const char *name, char *val, bool allocated)
697   FUNC_ATTR_NONNULL_ALL
698 {
699   int opt_idx = findoption(name);
700   if (opt_idx >= 0) {
701     if (options[opt_idx].flags & P_DEF_ALLOCED) {
702       xfree(options[opt_idx].def_val);
703     }
704 
705     options[opt_idx].def_val = allocated
706         ? (char_u *)val
707         : (char_u *)xstrdup(val);
708     options[opt_idx].flags |= P_DEF_ALLOCED;
709   }
710 }
711 
712 // For an option value that contains comma separated items, find "newval" in
713 // "origval".  Return NULL if not found.
find_dup_item(char_u * origval,const char_u * newval,uint32_t flags)714 static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t flags)
715   FUNC_ATTR_NONNULL_ARG(2)
716 {
717   int bs = 0;
718 
719   if (origval == NULL) {
720     return NULL;
721   }
722 
723   const size_t newlen = STRLEN(newval);
724   for (char_u *s = origval; *s != NUL; s++) {
725     if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1)))
726         && STRNCMP(s, newval, newlen) == 0
727         && (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) {
728       return s;
729     }
730     // Count backslashes.  Only a comma with an even number of backslashes
731     // or a single backslash preceded by a comma before it is recognized as
732     // a separator.
733     if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',')
734         || (s == origval + 1 && s[-1] == '\\')) {
735       bs++;
736     } else {
737       bs = 0;
738     }
739   }
740   return NULL;
741 }
742 
743 /// Set the Vi-default value of a number option.
744 /// Used for 'lines' and 'columns'.
set_number_default(char * name,long val)745 void set_number_default(char *name, long val)
746 {
747   int opt_idx;
748 
749   opt_idx = findoption(name);
750   if (opt_idx >= 0) {
751     options[opt_idx].def_val = (char_u *)(intptr_t)val;
752   }
753 }
754 
755 #if defined(EXITFREE)
756 /// Free all options.
free_all_options(void)757 void free_all_options(void)
758 {
759   for (int i = 0; options[i].fullname; i++) {
760     if (options[i].indir == PV_NONE) {
761       // global option: free value and default value.
762       if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) {
763         free_string_option(*(char_u **)options[i].var);
764       }
765       if (options[i].flags & P_DEF_ALLOCED) {
766         free_string_option(options[i].def_val);
767       }
768     } else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) {
769       // buffer-local option: free global value
770       clear_string_option((char_u **)options[i].var);
771     }
772   }
773 }
774 #endif
775 
776 
777 /// Initialize the options, part two: After getting Rows and Columns.
set_init_2(bool headless)778 void set_init_2(bool headless)
779 {
780   // set in set_init_1 but logging is not allowed there
781   ILOG("startup runtimepath/packpath value: %s", p_rtp);
782 
783   int idx;
784 
785   // 'scroll' defaults to half the window height. The stored default is zero,
786   // which results in the actual value computed from the window height.
787   idx = findoption("scroll");
788   if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
789     set_option_default(idx, OPT_LOCAL);
790   }
791   comp_col();
792 
793   /*
794    * 'window' is only for backwards compatibility with Vi.
795    * Default is Rows - 1.
796    */
797   if (!option_was_set("window")) {
798     p_window = Rows - 1;
799   }
800   set_number_default("window", Rows - 1);
801   (void)parse_printoptions();      // parse 'printoptions' default value
802 }
803 
804 /// Initialize the options, part three: After reading the .vimrc
set_init_3(void)805 void set_init_3(void)
806 {
807   parse_shape_opt(SHAPE_CURSOR);   // set cursor shapes from 'guicursor'
808 
809   // Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
810   // This is done after other initializations, where 'shell' might have been
811   // set, but only if they have not been set before.
812   int idx_srr = findoption("srr");
813   int do_srr = (idx_srr < 0)
814     ? false
815     : !(options[idx_srr].flags & P_WAS_SET);
816   int idx_sp = findoption("sp");
817   int do_sp = (idx_sp < 0)
818     ? false
819     : !(options[idx_sp].flags & P_WAS_SET);
820 
821   size_t len = 0;
822   char_u *p = (char_u *)invocation_path_tail(p_sh, &len);
823   p = vim_strnsave(p, len);
824 
825   {
826     //
827     // Default for p_sp is "| tee", for p_srr is ">".
828     // For known shells it is changed here to include stderr.
829     //
830     if (fnamecmp(p, "csh") == 0
831         || fnamecmp(p, "tcsh") == 0) {
832       if (do_sp) {
833         p_sp = (char_u *)"|& tee";
834         options[idx_sp].def_val = p_sp;
835       }
836       if (do_srr) {
837         p_srr = (char_u *)">&";
838         options[idx_srr].def_val = p_srr;
839       }
840     } else if (fnamecmp(p, "sh") == 0
841                || fnamecmp(p, "ksh") == 0
842                || fnamecmp(p, "mksh") == 0
843                || fnamecmp(p, "pdksh") == 0
844                || fnamecmp(p, "zsh") == 0
845                || fnamecmp(p, "zsh-beta") == 0
846                || fnamecmp(p, "bash") == 0
847                || fnamecmp(p, "fish") == 0
848                || fnamecmp(p, "ash") == 0
849                || fnamecmp(p, "dash") == 0) {
850       // Always use POSIX shell style redirection if we reach this
851       if (do_sp) {
852         p_sp = (char_u *)"2>&1| tee";
853         options[idx_sp].def_val = p_sp;
854       }
855       if (do_srr) {
856         p_srr = (char_u *)">%s 2>&1";
857         options[idx_srr].def_val = p_srr;
858       }
859     }
860     xfree(p);
861   }
862 
863   if (buf_is_empty(curbuf)) {
864     int idx_ffs = findoption_len(S_LEN("ffs"));
865 
866     // Apply the first entry of 'fileformats' to the initial buffer.
867     if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) {
868       set_fileformat(default_fileformat(), OPT_LOCAL);
869     }
870   }
871 
872   set_title_defaults();  // 'title', 'icon'
873 }
874 
875 /// When 'helplang' is still at its default value, set it to "lang".
876 /// Only the first two characters of "lang" are used.
set_helplang_default(const char * lang)877 void set_helplang_default(const char *lang)
878 {
879   if (lang == NULL) {
880     return;
881   }
882 
883   const size_t lang_len = strlen(lang);
884   if (lang_len < 2) {  // safety check
885     return;
886   }
887   int idx = findoption("hlg");
888   if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
889     if (options[idx].flags & P_ALLOCED) {
890       free_string_option(p_hlg);
891     }
892     p_hlg = (char_u *)xmemdupz(lang, lang_len);
893     // zh_CN becomes "cn", zh_TW becomes "tw".
894     if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) {
895       p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]);
896       p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]);
897     } else if (STRLEN(p_hlg) >= 1 && *p_hlg == 'C') {
898       // any C like setting, such as C.UTF-8, becomes "en"
899       p_hlg[0] = 'e';
900       p_hlg[1] = 'n';
901     }
902     p_hlg[2] = NUL;
903     options[idx].flags |= P_ALLOCED;
904   }
905 }
906 
907 
908 /// 'title' and 'icon' only default to true if they have not been set or reset
909 /// in .vimrc and we can read the old value.
910 /// When 'title' and 'icon' have been reset in .vimrc, we won't even check if
911 /// they can be reset.  This reduces startup time when using X on a remote
912 /// machine.
set_title_defaults(void)913 void set_title_defaults(void)
914 {
915   int idx1;
916 
917   /*
918    * If GUI is (going to be) used, we can always set the window title and
919    * icon name.  Saves a bit of time, because the X11 display server does
920    * not need to be contacted.
921    */
922   idx1 = findoption("title");
923   if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
924     options[idx1].def_val = (char_u *)(intptr_t)0;
925     p_title = 0;
926   }
927   idx1 = findoption("icon");
928   if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
929     options[idx1].def_val = (char_u *)(intptr_t)0;
930     p_icon = 0;
931   }
932 }
933 
934 /// Parse 'arg' for option settings.
935 ///
936 /// 'arg' may be IObuff, but only when no errors can be present and option
937 /// does not need to be expanded with option_expand().
938 /// "opt_flags":
939 /// 0 for ":set"
940 /// OPT_GLOBAL   for ":setglobal"
941 /// OPT_LOCAL    for ":setlocal" and a modeline
942 /// OPT_MODELINE for a modeline
943 /// OPT_WINONLY  to only set window-local options
944 /// OPT_NOWIN    to skip setting window-local options
945 ///
946 /// @param arg  option string (may be written to!)
947 ///
948 /// @return  FAIL if an error is detected, OK otherwise
do_set(char_u * arg,int opt_flags)949 int do_set(char_u *arg, int opt_flags)
950 {
951   int opt_idx;
952   char *errmsg;
953   char errbuf[80];
954   char_u *startarg;
955   int prefix;           // 1: nothing, 0: "no", 2: "inv" in front of name
956   char_u nextchar;                  // next non-white char after option name
957   int afterchar;                    // character just after option name
958   int len;
959   int i;
960   varnumber_T value;
961   int key;
962   uint32_t flags;                   // flags for current option
963   char_u *varp = NULL;         // pointer to variable for current option
964   int did_show = false;             // already showed one value
965   int adding;                       // "opt+=arg"
966   int prepending;                   // "opt^=arg"
967   int removing;                     // "opt-=arg"
968 
969   if (*arg == NUL) {
970     showoptions(0, opt_flags);
971     did_show = true;
972     goto theend;
973   }
974 
975   while (*arg != NUL) {         // loop to process all options
976     errmsg = NULL;
977     startarg = arg;             // remember for error message
978 
979     if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
980         && !(opt_flags & OPT_MODELINE)) {
981       /*
982        * ":set all"  show all options.
983        * ":set all&" set all options to their default value.
984        */
985       arg += 3;
986       if (*arg == '&') {
987         arg++;
988         // Only for :set command set global value of local options.
989         set_options_default(OPT_FREE | opt_flags);
990         didset_options();
991         didset_options2();
992         ui_refresh_options();
993         redraw_all_later(CLEAR);
994       } else {
995         showoptions(1, opt_flags);
996         did_show = true;
997       }
998     } else {
999       prefix = 1;
1000       if (STRNCMP(arg, "no", 2) == 0) {
1001         prefix = 0;
1002         arg += 2;
1003       } else if (STRNCMP(arg, "inv", 3) == 0) {
1004         prefix = 2;
1005         arg += 3;
1006       }
1007 
1008       // find end of name
1009       key = 0;
1010       if (*arg == '<') {
1011         opt_idx = -1;
1012         // look out for <t_>;>
1013         if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) {
1014           len = 5;
1015         } else {
1016           len = 1;
1017           while (arg[len] != NUL && arg[len] != '>') {
1018             len++;
1019           }
1020         }
1021         if (arg[len] != '>') {
1022           errmsg = e_invarg;
1023           goto skip;
1024         }
1025         if (arg[1] == 't' && arg[2] == '_') {  // could be term code
1026           opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1));
1027         }
1028         len++;
1029         if (opt_idx == -1) {
1030           key = find_key_option(arg + 1, true);
1031         }
1032       } else {
1033         len = 0;
1034         // The two characters after "t_" may not be alphanumeric.
1035         if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) {
1036           len = 4;
1037         } else {
1038           while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') {
1039             len++;
1040           }
1041         }
1042         opt_idx = findoption_len((const char *)arg, (size_t)len);
1043         if (opt_idx == -1) {
1044           key = find_key_option(arg, false);
1045         }
1046       }
1047 
1048       // remember character after option name
1049       afterchar = arg[len];
1050 
1051       // skip white space, allow ":set ai  ?"
1052       while (ascii_iswhite(arg[len])) {
1053         len++;
1054       }
1055 
1056       adding = false;
1057       prepending = false;
1058       removing = false;
1059       if (arg[len] != NUL && arg[len + 1] == '=') {
1060         if (arg[len] == '+') {
1061           adding = true;                        // "+="
1062           len++;
1063         } else if (arg[len] == '^') {
1064           prepending = true;                    // "^="
1065           len++;
1066         } else if (arg[len] == '-') {
1067           removing = true;                      // "-="
1068           len++;
1069         }
1070       }
1071       nextchar = arg[len];
1072 
1073       if (opt_idx == -1 && key == 0) {          // found a mismatch: skip
1074         errmsg = N_("E518: Unknown option");
1075         goto skip;
1076       }
1077 
1078       if (opt_idx >= 0) {
1079         if (options[opt_idx].var == NULL) {         // hidden option: skip
1080           // Only give an error message when requesting the value of
1081           // a hidden option, ignore setting it.
1082           if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL
1083               && (!(options[opt_idx].flags & P_BOOL)
1084                   || nextchar == '?')) {
1085             errmsg = _(e_unsupportedoption);
1086           }
1087           goto skip;
1088         }
1089 
1090         flags = options[opt_idx].flags;
1091         varp = get_varp_scope(&(options[opt_idx]), opt_flags);
1092       } else {
1093         flags = P_STRING;
1094       }
1095 
1096       // Skip all options that are not window-local (used when showing
1097       // an already loaded buffer in a window).
1098       if ((opt_flags & OPT_WINONLY)
1099           && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) {
1100         goto skip;
1101       }
1102 
1103       // Skip all options that are window-local (used for :vimgrep).
1104       if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
1105           && options[opt_idx].var == VAR_WIN) {
1106         goto skip;
1107       }
1108 
1109       // Disallow changing some options from modelines.
1110       if (opt_flags & OPT_MODELINE) {
1111         if (flags & (P_SECURE | P_NO_ML)) {
1112           errmsg = N_("E520: Not allowed in a modeline");
1113           goto skip;
1114         }
1115         if ((flags & P_MLE) && !p_mle) {
1116           errmsg = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
1117           goto skip;
1118         }
1119         // In diff mode some options are overruled.  This avoids that
1120         // 'foldmethod' becomes "marker" instead of "diff" and that
1121         // "wrap" gets set.
1122         if (curwin->w_p_diff
1123             && opt_idx >= 0              // shut up coverity warning
1124             && (options[opt_idx].indir == PV_FDM
1125                 || options[opt_idx].indir == PV_WRAP)) {
1126           goto skip;
1127         }
1128       }
1129 
1130       // Disallow changing some options in the sandbox
1131       if (sandbox != 0 && (flags & P_SECURE)) {
1132         errmsg = e_sandbox;
1133         goto skip;
1134       }
1135 
1136       if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) {
1137         arg += len;
1138         if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') {
1139           if (arg[3] == 'm') {  // "opt&vim": set to Vim default
1140             arg += 3;
1141           } else {  // "opt&vi": set to Vi default
1142             arg += 2;
1143           }
1144         }
1145         if (vim_strchr((char_u *)"?!&<", nextchar) != NULL
1146             && arg[1] != NUL && !ascii_iswhite(arg[1])) {
1147           errmsg = e_trailing;
1148           goto skip;
1149         }
1150       }
1151 
1152       //
1153       // allow '=' and ':' as MS-DOS command.com allows only one
1154       // '=' character per "set" command line. grrr. (jw)
1155       //
1156       if (nextchar == '?'
1157           || (prefix == 1
1158               && vim_strchr((char_u *)"=:&<", nextchar) == NULL
1159               && !(flags & P_BOOL))) {
1160         /*
1161          * print value
1162          */
1163         if (did_show) {
1164           msg_putchar('\n');                // cursor below last one
1165         } else {
1166           gotocmdline(true);                // cursor at status line
1167           did_show = true;                  // remember that we did a line
1168         }
1169         if (opt_idx >= 0) {
1170           showoneopt(&options[opt_idx], opt_flags);
1171           if (p_verbose > 0) {
1172             // Mention where the option was last set.
1173             if (varp == options[opt_idx].var) {
1174               option_last_set_msg(options[opt_idx].last_set);
1175             } else if ((int)options[opt_idx].indir & PV_WIN) {
1176               option_last_set_msg(curwin->w_p_script_ctx[
1177                                                          (int)options[opt_idx].indir & PV_MASK]);
1178             } else if ((int)options[opt_idx].indir & PV_BUF) {
1179               option_last_set_msg(curbuf->b_p_script_ctx[
1180                                                          (int)options[opt_idx].indir & PV_MASK]);
1181             }
1182           }
1183         } else {
1184           errmsg = N_("E846: Key code not set");
1185           goto skip;
1186         }
1187         if (nextchar != '?'
1188             && nextchar != NUL && !ascii_iswhite(afterchar)) {
1189           errmsg = e_trailing;
1190         }
1191       } else {
1192         int value_is_replaced = !prepending && !adding && !removing;
1193         int value_checked = false;
1194 
1195         if (flags & P_BOOL) {                       // boolean
1196           if (nextchar == '=' || nextchar == ':') {
1197             errmsg = e_invarg;
1198             goto skip;
1199           }
1200 
1201           /*
1202            * ":set opt!": invert
1203            * ":set opt&": reset to default value
1204            * ":set opt<": reset to global value
1205            */
1206           if (nextchar == '!') {
1207             value = *(int *)(varp) ^ 1;
1208           } else if (nextchar == '&') {
1209             value = (int)(intptr_t)options[opt_idx].def_val;
1210           } else if (nextchar == '<') {
1211             // For 'autoread' -1 means to use global value.
1212             if ((int *)varp == &curbuf->b_p_ar
1213                 && opt_flags == OPT_LOCAL) {
1214               value = -1;
1215             } else {
1216               value = *(int *)get_varp_scope(&(options[opt_idx]),
1217                                              OPT_GLOBAL);
1218             }
1219           } else {
1220             /*
1221              * ":set invopt": invert
1222              * ":set opt" or ":set noopt": set or reset
1223              */
1224             if (nextchar != NUL && !ascii_iswhite(afterchar)) {
1225               errmsg = e_trailing;
1226               goto skip;
1227             }
1228             if (prefix == 2) {                  // inv
1229               value = *(int *)(varp) ^ 1;
1230             } else {
1231               value = prefix;
1232             }
1233           }
1234 
1235           errmsg = set_bool_option(opt_idx, varp, (int)value,
1236                                    opt_flags);
1237         } else {  // Numeric or string.
1238           if (vim_strchr((const char_u *)"=:&<", nextchar) == NULL
1239               || prefix != 1) {
1240             errmsg = e_invarg;
1241             goto skip;
1242           }
1243 
1244           if (flags & P_NUM) {                      // numeric
1245             // Different ways to set a number option:
1246             // &            set to default value
1247             // <            set to global value
1248             // <xx>         accept special key codes for 'wildchar'
1249             // c            accept any non-digit for 'wildchar'
1250             // [-]0-9       set number
1251             // other        error
1252             arg++;
1253             if (nextchar == '&') {
1254               value = (long)(intptr_t)options[opt_idx].def_val;
1255             } else if (nextchar == '<') {
1256               // For 'undolevels' NO_LOCAL_UNDOLEVEL means to
1257               // use the global value.
1258               if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
1259                 value = NO_LOCAL_UNDOLEVEL;
1260               } else {
1261                 value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
1262               }
1263             } else if (((long *)varp == &p_wc
1264                         || (long *)varp == &p_wcm)
1265                        && (*arg == '<'
1266                            || *arg == '^'
1267                            || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
1268                                && !ascii_isdigit(*arg)))) {
1269               value = string_to_key(arg);
1270               if (value == 0 && (long *)varp != &p_wcm) {
1271                 errmsg = e_invarg;
1272                 goto skip;
1273               }
1274             } else if (*arg == '-' || ascii_isdigit(*arg)) {
1275               // Allow negative, octal and hex numbers.
1276               vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
1277               if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
1278                 errmsg = N_("E521: Number required after =");
1279                 goto skip;
1280               }
1281             } else {
1282               errmsg = N_("E521: Number required after =");
1283               goto skip;
1284             }
1285 
1286             if (adding) {
1287               value = *(long *)varp + value;
1288             }
1289             if (prepending) {
1290               value = *(long *)varp * value;
1291             }
1292             if (removing) {
1293               value = *(long *)varp - value;
1294             }
1295             errmsg = set_num_option(opt_idx, varp, (long)value,
1296                                     errbuf, sizeof(errbuf),
1297                                     opt_flags);
1298           } else if (opt_idx >= 0) {  // String.
1299             char_u *save_arg = NULL;
1300             char_u *s = NULL;
1301             char_u *oldval = NULL;         // previous value if *varp
1302             char_u *newval;
1303             char_u *origval = NULL;
1304             char *saved_origval = NULL;
1305             char *saved_newval = NULL;
1306             unsigned newlen;
1307             int comma;
1308             bool new_value_alloced = false;  // new string option was allocated
1309 
1310             // When using ":set opt=val" for a global option
1311             // with a local value the local value will be
1312             // reset, use the global value here.
1313             if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
1314                 && ((int)options[opt_idx].indir & PV_BOTH)) {
1315               varp = options[opt_idx].var;
1316             }
1317 
1318             // The old value is kept until we are sure that the
1319             // new value is valid.
1320             oldval = *(char_u **)varp;
1321 
1322             // When setting the local value of a global
1323             // option, the old value may be the global value.
1324             if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags
1325                                                             & OPT_LOCAL)) {
1326               origval = *(char_u **)get_varp(&options[opt_idx]);
1327             } else {
1328               origval = oldval;
1329             }
1330 
1331             if (nextchar == '&') {  // set to default val
1332               newval = options[opt_idx].def_val;
1333               // expand environment variables and ~ (since the
1334               // default value was already expanded, only
1335               // required when an environment variable was set
1336               // later
1337               new_value_alloced = true;
1338               if (newval == NULL) {
1339                 newval = empty_option;
1340               } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
1341                 s = option_expand(opt_idx, newval);
1342                 if (s == NULL) {
1343                   s = newval;
1344                 }
1345                 newval = vim_strsave(s);
1346               } else {
1347                 newval = (char_u *)xstrdup((char *)newval);
1348               }
1349             } else if (nextchar == '<') {  // set to global val
1350               newval = vim_strsave(*(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
1351               new_value_alloced = true;
1352             } else {
1353               arg++;                    // jump to after the '=' or ':'
1354 
1355               /*
1356                * Set 'keywordprg' to ":help" if an empty
1357                * value was passed to :set by the user.
1358                * Misuse errbuf[] for the resulting string.
1359                */
1360               if (varp == (char_u *)&p_kp
1361                   && (*arg == NUL || *arg == ' ')) {
1362                 STRCPY(errbuf, ":help");
1363                 save_arg = arg;
1364                 arg = (char_u *)errbuf;
1365               }
1366               /*
1367                * Convert 'backspace' number to string, for
1368                * adding, prepending and removing string.
1369                */
1370               else if (varp == (char_u *)&p_bs
1371                        && ascii_isdigit(**(char_u **)varp)) {
1372                 i = getdigits_int((char_u **)varp, true, 0);
1373                 switch (i) {
1374                 case 0:
1375                   *(char_u **)varp = empty_option;
1376                   break;
1377                 case 1:
1378                   *(char_u **)varp = vim_strsave((char_u *)"indent,eol");
1379                   break;
1380                 case 2:
1381                   *(char_u **)varp = vim_strsave((char_u *)"indent,eol,start");
1382                   break;
1383                 case 3:
1384                   *(char_u **)varp = vim_strsave((char_u *)"indent,eol,nostop");
1385                   break;
1386                 }
1387                 xfree(oldval);
1388                 if (origval == oldval) {
1389                   origval = *(char_u **)varp;
1390                 }
1391                 oldval = *(char_u **)varp;
1392               }
1393               /*
1394                * Convert 'whichwrap' number to string, for
1395                * backwards compatibility with Vim 3.0.
1396                * Misuse errbuf[] for the resulting string.
1397                */
1398               else if (varp == (char_u *)&p_ww
1399                        && ascii_isdigit(*arg)) {
1400                 *errbuf = NUL;
1401                 i = getdigits_int(&arg, true, 0);
1402                 if (i & 1) {
1403                   STRLCAT(errbuf, "b,", sizeof(errbuf));
1404                 }
1405                 if (i & 2) {
1406                   STRLCAT(errbuf, "s,", sizeof(errbuf));
1407                 }
1408                 if (i & 4) {
1409                   STRLCAT(errbuf, "h,l,", sizeof(errbuf));
1410                 }
1411                 if (i & 8) {
1412                   STRLCAT(errbuf, "<,>,", sizeof(errbuf));
1413                 }
1414                 if (i & 16) {
1415                   STRLCAT(errbuf, "[,],", sizeof(errbuf));
1416                 }
1417                 save_arg = arg;
1418                 arg = (char_u *)errbuf;
1419               }
1420               /*
1421                * Remove '>' before 'dir' and 'bdir', for
1422                * backwards compatibility with version 3.0
1423                */
1424               else if (*arg == '>'
1425                        && (varp == (char_u *)&p_dir
1426                            || varp == (char_u *)&p_bdir)) {
1427                 arg++;
1428               }
1429 
1430               /*
1431                * Copy the new string into allocated memory.
1432                * Can't use set_string_option_direct(), because
1433                * we need to remove the backslashes.
1434                */
1435               // get a bit too much
1436               newlen = (unsigned)STRLEN(arg) + 1;
1437               if (adding || prepending || removing) {
1438                 newlen += (unsigned)STRLEN(origval) + 1;
1439               }
1440               newval = xmalloc(newlen);
1441               s = newval;
1442 
1443               /*
1444                * Copy the string, skip over escaped chars.
1445                * For WIN32 backslashes before normal
1446                * file name characters are not removed, and keep
1447                * backslash at start, for "\\machine\path", but
1448                * do remove it for "\\\\machine\\path".
1449                * The reverse is found in ExpandOldSetting().
1450                */
1451               while (*arg && !ascii_iswhite(*arg)) {
1452                 if (*arg == '\\' && arg[1] != NUL
1453 #ifdef BACKSLASH_IN_FILENAME
1454                     && !((flags & P_EXPAND)
1455                          && vim_isfilec(arg[1])
1456                          && !ascii_iswhite(arg[1])
1457                          && (arg[1] != '\\'
1458                              || (s == newval
1459                                  && arg[2] != '\\')))
1460 #endif
1461                     ) {
1462                   arg++;                        // remove backslash
1463                 }
1464                 i = utfc_ptr2len(arg);
1465                 if (i > 1) {
1466                   // copy multibyte char
1467                   memmove(s, arg, (size_t)i);
1468                   arg += i;
1469                   s += i;
1470                 } else {
1471                   *s++ = *arg++;
1472                 }
1473               }
1474               *s = NUL;
1475 
1476               /*
1477                * Expand environment variables and ~.
1478                * Don't do it when adding without inserting a
1479                * comma.
1480                */
1481               if (!(adding || prepending || removing)
1482                   || (flags & P_COMMA)) {
1483                 s = option_expand(opt_idx, newval);
1484                 if (s != NULL) {
1485                   xfree(newval);
1486                   newlen = (unsigned)STRLEN(s) + 1;
1487                   if (adding || prepending || removing) {
1488                     newlen += (unsigned)STRLEN(origval) + 1;
1489                   }
1490                   newval = xmalloc(newlen);
1491                   STRCPY(newval, s);
1492                 }
1493               }
1494 
1495               // locate newval[] in origval[] when removing it
1496               // and when adding to avoid duplicates
1497               i = 0;                    // init for GCC
1498               if (removing || (flags & P_NODUP)) {
1499                 i = (int)STRLEN(newval);
1500                 s = find_dup_item(origval, newval, flags);
1501 
1502                 // do not add if already there
1503                 if ((adding || prepending) && s != NULL) {
1504                   prepending = false;
1505                   adding = false;
1506                   STRCPY(newval, origval);
1507                 }
1508 
1509                 // if no duplicate, move pointer to end of
1510                 // original value
1511                 if (s == NULL) {
1512                   s = origval + (int)STRLEN(origval);
1513                 }
1514               }
1515 
1516               // concatenate the two strings; add a ',' if
1517               // needed
1518               if (adding || prepending) {
1519                 comma = ((flags & P_COMMA) && *origval != NUL
1520                          && *newval != NUL);
1521                 if (adding) {
1522                   i = (int)STRLEN(origval);
1523                   // Strip a trailing comma, would get 2.
1524                   if (comma && i > 1
1525                       && (flags & P_ONECOMMA) == P_ONECOMMA
1526                       && origval[i - 1] == ','
1527                       && origval[i - 2] != '\\') {
1528                     i--;
1529                   }
1530                   memmove(newval + i + comma, newval,
1531                           STRLEN(newval) + 1);
1532                   memmove(newval, origval, (size_t)i);
1533                 } else {
1534                   i = (int)STRLEN(newval);
1535                   STRMOVE(newval + i + comma, origval);
1536                 }
1537                 if (comma) {
1538                   newval[i] = ',';
1539                 }
1540               }
1541 
1542               // Remove newval[] from origval[]. (Note: "i" has
1543               // been set above and is used here).
1544               if (removing) {
1545                 STRCPY(newval, origval);
1546                 if (*s) {
1547                   // may need to remove a comma
1548                   if (flags & P_COMMA) {
1549                     if (s == origval) {
1550                       // include comma after string
1551                       if (s[i] == ',') {
1552                         i++;
1553                       }
1554                     } else {
1555                       // include comma before string
1556                       s--;
1557                       i++;
1558                     }
1559                   }
1560                   STRMOVE(newval + (s - origval), s + i);
1561                 }
1562               }
1563 
1564               if (flags & P_FLAGLIST) {
1565                 // Remove flags that appear twice.
1566                 for (s = newval; *s;) {
1567                   // if options have P_FLAGLIST and P_ONECOMMA such as
1568                   // 'whichwrap'
1569                   if (flags & P_ONECOMMA) {
1570                     if (*s != ',' && *(s + 1) == ','
1571                         && vim_strchr(s + 2, *s) != NULL) {
1572                       // Remove the duplicated value and the next comma.
1573                       STRMOVE(s, s + 2);
1574                       continue;
1575                     }
1576                   } else {
1577                     if ((!(flags & P_COMMA) || *s != ',')
1578                         && vim_strchr(s + 1, *s) != NULL) {
1579                       STRMOVE(s, s + 1);
1580                       continue;
1581                     }
1582                   }
1583                   s++;
1584                 }
1585               }
1586 
1587               if (save_arg != NULL) {               // number for 'whichwrap'
1588                 arg = save_arg;
1589               }
1590               new_value_alloced = true;
1591             }
1592 
1593             // Set the new value.
1594             *(char_u **)(varp) = newval;
1595 
1596             // origval may be freed by
1597             // did_set_string_option(), make a copy.
1598             saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0;
1599 
1600             // newval (and varp) may become invalid if the
1601             // buffer is closed by autocommands.
1602             saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0;
1603 
1604             {
1605               uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
1606               const int secure_saved = secure;
1607 
1608               // When an option is set in the sandbox, from a
1609               // modeline or in secure mode, then deal with side
1610               // effects in secure mode.  Also when the value was
1611               // set with the P_INSECURE flag and is not
1612               // completely replaced.
1613               if ((opt_flags & OPT_MODELINE)
1614                   || sandbox != 0
1615                   || (!value_is_replaced && (*p & P_INSECURE))) {
1616                 secure = 1;
1617               }
1618 
1619               // Handle side effects, and set the global value
1620               // for ":set" on local options. Note: when setting
1621               // 'syntax' or 'filetype' autocommands may be
1622               // triggered that can cause havoc.
1623               errmsg = did_set_string_option(opt_idx, (char_u **)varp,
1624                                              new_value_alloced, oldval,
1625                                              errbuf, sizeof(errbuf),
1626                                              opt_flags, &value_checked);
1627 
1628               secure = secure_saved;
1629             }
1630 
1631             if (errmsg == NULL) {
1632               if (!starting) {
1633                 trigger_optionsset_string(opt_idx, opt_flags, saved_origval,
1634                                           saved_newval);
1635               }
1636               if (options[opt_idx].flags & P_UI_OPTION) {
1637                 ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
1638                                    STRING_OBJ(cstr_as_string(saved_newval)));
1639               }
1640             }
1641             xfree(saved_origval);
1642             xfree(saved_newval);
1643 
1644             // If error detected, print the error message.
1645             if (errmsg != NULL) {
1646               goto skip;
1647             }
1648           } else {
1649             // key code option(FIXME(tarruda): Show a warning or something
1650             // similar)
1651           }
1652         }
1653 
1654         if (opt_idx >= 0) {
1655           did_set_option(opt_idx, opt_flags, value_is_replaced, value_checked);
1656         }
1657       }
1658 
1659 skip:
1660       /*
1661        * Advance to next argument.
1662        * - skip until a blank found, taking care of backslashes
1663        * - skip blanks
1664        * - skip one "=val" argument (for hidden options ":set gfn =xx")
1665        */
1666       for (i = 0; i < 2; i++) {
1667         while (*arg != NUL && !ascii_iswhite(*arg)) {
1668           if (*arg++ == '\\' && *arg != NUL) {
1669             arg++;
1670           }
1671         }
1672         arg = skipwhite(arg);
1673         if (*arg != '=') {
1674           break;
1675         }
1676       }
1677     }
1678 
1679     if (errmsg != NULL) {
1680       STRLCPY(IObuff, _(errmsg), IOSIZE);
1681       i = (int)STRLEN(IObuff) + 2;
1682       if (i + (arg - startarg) < IOSIZE) {
1683         // append the argument with the error
1684         STRCAT(IObuff, ": ");
1685         assert(arg >= startarg);
1686         memmove(IObuff + i, startarg, (size_t)(arg - startarg));
1687         IObuff[i + (arg - startarg)] = NUL;
1688       }
1689       // make sure all characters are printable
1690       trans_characters(IObuff, IOSIZE);
1691 
1692       no_wait_return++;         // wait_return done later
1693       emsg((char *)IObuff);     // show error highlighted
1694       no_wait_return--;
1695 
1696       return FAIL;
1697     }
1698 
1699     arg = skipwhite(arg);
1700   }
1701 
1702 theend:
1703   if (silent_mode && did_show) {
1704     // After displaying option values in silent mode.
1705     silent_mode = false;
1706     info_message = true;        // use mch_msg(), not mch_errmsg()
1707     msg_putchar('\n');
1708     ui_flush();
1709     silent_mode = true;
1710     info_message = false;       // use mch_msg(), not mch_errmsg()
1711   }
1712 
1713   return OK;
1714 }
1715 
1716 /// Call this when an option has been given a new value through a user command.
1717 /// Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
1718 ///
1719 /// @param opt_flags  possibly with OPT_MODELINE
1720 /// @param new_value  value was replaced completely
1721 /// @param value_checked  value was checked to be safe, no need to set P_INSECURE
did_set_option(int opt_idx,int opt_flags,int new_value,int value_checked)1722 static void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked)
1723 {
1724   options[opt_idx].flags |= P_WAS_SET;
1725 
1726   // When an option is set in the sandbox, from a modeline or in secure mode
1727   // set the P_INSECURE flag.  Otherwise, if a new value is stored reset the
1728   // flag.
1729   uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
1730   if (!value_checked && (secure
1731                          || sandbox != 0
1732                          || (opt_flags & OPT_MODELINE))) {
1733     *p = *p | P_INSECURE;
1734   } else if (new_value) {
1735     *p = *p & ~P_INSECURE;
1736   }
1737 }
1738 
illegal_char(char * errbuf,size_t errbuflen,int c)1739 static char *illegal_char(char *errbuf, size_t errbuflen, int c)
1740 {
1741   if (errbuf == NULL) {
1742     return "";
1743   }
1744   vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"),
1745                (char *)transchar(c));
1746   return errbuf;
1747 }
1748 
1749 /// Convert a key name or string into a key value.
1750 /// Used for 'wildchar' and 'cedit' options.
string_to_key(char_u * arg)1751 static int string_to_key(char_u *arg)
1752 {
1753   if (*arg == '<') {
1754     return find_key_option(arg + 1, true);
1755   }
1756   if (*arg == '^') {
1757     return Ctrl_chr(arg[1]);
1758   }
1759   return *arg;
1760 }
1761 
1762 /// Check value of 'cedit' and set cedit_key.
1763 /// Returns NULL if value is OK, error message otherwise.
check_cedit(void)1764 static char *check_cedit(void)
1765 {
1766   int n;
1767 
1768   if (*p_cedit == NUL) {
1769     cedit_key = -1;
1770   } else {
1771     n = string_to_key(p_cedit);
1772     if (vim_isprintc(n)) {
1773       return e_invarg;
1774     }
1775     cedit_key = n;
1776   }
1777   return NULL;
1778 }
1779 
1780 // When changing 'title', 'titlestring', 'icon' or 'iconstring', call
1781 // maketitle() to create and display it.
1782 // When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
1783 // the old value back.
did_set_title(void)1784 static void did_set_title(void)
1785 {
1786   if (starting != NO_SCREEN) {
1787     maketitle();
1788   }
1789 }
1790 
1791 /// set_options_bin -  called when 'bin' changes value.
1792 ///
1793 /// @param opt_flags  OPT_LOCAL and/or OPT_GLOBAL
set_options_bin(int oldval,int newval,int opt_flags)1794 void set_options_bin(int oldval, int newval, int opt_flags)
1795 {
1796   /*
1797    * The option values that are changed when 'bin' changes are
1798    * copied when 'bin is set and restored when 'bin' is reset.
1799    */
1800   if (newval) {
1801     if (!oldval) {              // switched on
1802       if (!(opt_flags & OPT_GLOBAL)) {
1803         curbuf->b_p_tw_nobin = curbuf->b_p_tw;
1804         curbuf->b_p_wm_nobin = curbuf->b_p_wm;
1805         curbuf->b_p_ml_nobin = curbuf->b_p_ml;
1806         curbuf->b_p_et_nobin = curbuf->b_p_et;
1807       }
1808       if (!(opt_flags & OPT_LOCAL)) {
1809         p_tw_nobin = p_tw;
1810         p_wm_nobin = p_wm;
1811         p_ml_nobin = p_ml;
1812         p_et_nobin = p_et;
1813       }
1814     }
1815 
1816     if (!(opt_flags & OPT_GLOBAL)) {
1817       curbuf->b_p_tw = 0;       // no automatic line wrap
1818       curbuf->b_p_wm = 0;       // no automatic line wrap
1819       curbuf->b_p_ml = 0;       // no modelines
1820       curbuf->b_p_et = 0;       // no expandtab
1821     }
1822     if (!(opt_flags & OPT_LOCAL)) {
1823       p_tw = 0;
1824       p_wm = 0;
1825       p_ml = false;
1826       p_et = false;
1827       p_bin = true;             // needed when called for the "-b" argument
1828     }
1829   } else if (oldval) {        // switched off
1830     if (!(opt_flags & OPT_GLOBAL)) {
1831       curbuf->b_p_tw = curbuf->b_p_tw_nobin;
1832       curbuf->b_p_wm = curbuf->b_p_wm_nobin;
1833       curbuf->b_p_ml = curbuf->b_p_ml_nobin;
1834       curbuf->b_p_et = curbuf->b_p_et_nobin;
1835     }
1836     if (!(opt_flags & OPT_LOCAL)) {
1837       p_tw = p_tw_nobin;
1838       p_wm = p_wm_nobin;
1839       p_ml = p_ml_nobin;
1840       p_et = p_et_nobin;
1841     }
1842   }
1843 }
1844 
1845 /// Find the parameter represented by the given character (eg ', :, ", or /),
1846 /// and return its associated value in the 'shada' string.
1847 /// Only works for number parameters, not for 'r' or 'n'.
1848 /// If the parameter is not specified in the string or there is no following
1849 /// number, return -1.
get_shada_parameter(int type)1850 int get_shada_parameter(int type)
1851 {
1852   char_u *p;
1853 
1854   p = find_shada_parameter(type);
1855   if (p != NULL && ascii_isdigit(*p)) {
1856     return atoi((char *)p);
1857   }
1858   return -1;
1859 }
1860 
1861 /// Find the parameter represented by the given character (eg ''', ':', '"', or
1862 /// '/') in the 'shada' option and return a pointer to the string after it.
1863 /// Return NULL if the parameter is not specified in the string.
find_shada_parameter(int type)1864 char_u *find_shada_parameter(int type)
1865 {
1866   char_u *p;
1867 
1868   for (p = p_shada; *p; p++) {
1869     if (*p == type) {
1870       return p + 1;
1871     }
1872     if (*p == 'n') {                // 'n' is always the last one
1873       break;
1874     }
1875     p = vim_strchr(p, ',');         // skip until next ','
1876     if (p == NULL) {                // hit the end without finding parameter
1877       break;
1878     }
1879   }
1880   return NULL;
1881 }
1882 
1883 /// Expand environment variables for some string options.
1884 /// These string options cannot be indirect!
1885 /// If "val" is NULL expand the current value of the option.
1886 /// Return pointer to NameBuff, or NULL when not expanded.
option_expand(int opt_idx,char_u * val)1887 static char_u *option_expand(int opt_idx, char_u *val)
1888 {
1889   // if option doesn't need expansion nothing to do
1890   if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) {
1891     return NULL;
1892   }
1893 
1894   if (val == NULL) {
1895     val = *(char_u **)options[opt_idx].var;
1896   }
1897 
1898   // If val is longer than MAXPATHL no meaningful expansion can be done,
1899   // expand_env() would truncate the string.
1900   if (val == NULL || STRLEN(val) > MAXPATHL) {
1901     return NULL;
1902   }
1903 
1904   /*
1905    * Expanding this with NameBuff, expand_env() must not be passed IObuff.
1906    * Escape spaces when expanding 'tags', they are used to separate file
1907    * names.
1908    * For 'spellsuggest' expand after "file:".
1909    */
1910   expand_env_esc(val, NameBuff, MAXPATHL,
1911                  (char_u **)options[opt_idx].var == &p_tags, false,
1912                  (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
1913                  NULL);
1914   if (STRCMP(NameBuff, val) == 0) {   // they are the same
1915     return NULL;
1916   }
1917 
1918   return NameBuff;
1919 }
1920 
1921 // After setting various option values: recompute variables that depend on
1922 // option values.
didset_options(void)1923 static void didset_options(void)
1924 {
1925   // initialize the table for 'iskeyword' et.al.
1926   (void)init_chartab();
1927 
1928   (void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
1929   (void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
1930   (void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
1931   (void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true);
1932   (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
1933   (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
1934   (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
1935   (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
1936   (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
1937   (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
1938   (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
1939   (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
1940   (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
1941   (void)spell_check_msm();
1942   (void)spell_check_sps();
1943   (void)compile_cap_prog(curwin->w_s);
1944   (void)did_set_spell_option(true);
1945   // set cedit_key
1946   (void)check_cedit();
1947   briopt_check(curwin);
1948   // initialize the table for 'breakat'.
1949   fill_breakat_flags();
1950   fill_culopt_flags(NULL, curwin);
1951 }
1952 
1953 // More side effects of setting options.
didset_options2(void)1954 static void didset_options2(void)
1955 {
1956   // Initialize the highlight_attr[] table.
1957   highlight_changed();
1958 
1959   // Parse default for 'clipboard'.
1960   (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
1961 
1962   // Parse default for 'fillchars'.
1963   (void)set_chars_option(curwin, &curwin->w_p_fcs, true);
1964 
1965   // Parse default for 'listchars'.
1966   (void)set_chars_option(curwin, &curwin->w_p_lcs, true);
1967 
1968   // Parse default for 'wildmode'.
1969   check_opt_wim();
1970   xfree(curbuf->b_p_vsts_array);
1971   tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
1972   xfree(curbuf->b_p_vts_array);
1973   tabstop_set(curbuf->b_p_vts,  &curbuf->b_p_vts_array);
1974 }
1975 
1976 /// Check for string options that are NULL (normally only termcap options).
check_options(void)1977 void check_options(void)
1978 {
1979   int opt_idx;
1980 
1981   for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
1982     if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) {
1983       check_string_option((char_u **)get_varp(&(options[opt_idx])));
1984     }
1985   }
1986 }
1987 
1988 /// Check string options in a buffer for NULL value.
check_buf_options(buf_T * buf)1989 void check_buf_options(buf_T *buf)
1990 {
1991   check_string_option(&buf->b_p_bh);
1992   check_string_option(&buf->b_p_bt);
1993   check_string_option(&buf->b_p_fenc);
1994   check_string_option(&buf->b_p_ff);
1995   check_string_option(&buf->b_p_def);
1996   check_string_option(&buf->b_p_inc);
1997   check_string_option(&buf->b_p_inex);
1998   check_string_option(&buf->b_p_inde);
1999   check_string_option(&buf->b_p_indk);
2000   check_string_option(&buf->b_p_fp);
2001   check_string_option(&buf->b_p_fex);
2002   check_string_option(&buf->b_p_kp);
2003   check_string_option(&buf->b_p_mps);
2004   check_string_option(&buf->b_p_fo);
2005   check_string_option(&buf->b_p_flp);
2006   check_string_option(&buf->b_p_isk);
2007   check_string_option(&buf->b_p_com);
2008   check_string_option(&buf->b_p_cms);
2009   check_string_option(&buf->b_p_nf);
2010   check_string_option(&buf->b_p_qe);
2011   check_string_option(&buf->b_p_syn);
2012   check_string_option(&buf->b_s.b_syn_isk);
2013   check_string_option(&buf->b_s.b_p_spc);
2014   check_string_option(&buf->b_s.b_p_spf);
2015   check_string_option(&buf->b_s.b_p_spl);
2016   check_string_option(&buf->b_s.b_p_spo);
2017   check_string_option(&buf->b_p_sua);
2018   check_string_option(&buf->b_p_cink);
2019   check_string_option(&buf->b_p_cino);
2020   parse_cino(buf);
2021   check_string_option(&buf->b_p_ft);
2022   check_string_option(&buf->b_p_cinw);
2023   check_string_option(&buf->b_p_cpt);
2024   check_string_option(&buf->b_p_cfu);
2025   check_string_option(&buf->b_p_ofu);
2026   check_string_option(&buf->b_p_keymap);
2027   check_string_option(&buf->b_p_gp);
2028   check_string_option(&buf->b_p_mp);
2029   check_string_option(&buf->b_p_efm);
2030   check_string_option(&buf->b_p_ep);
2031   check_string_option(&buf->b_p_path);
2032   check_string_option(&buf->b_p_tags);
2033   check_string_option(&buf->b_p_tfu);
2034   check_string_option(&buf->b_p_tc);
2035   check_string_option(&buf->b_p_dict);
2036   check_string_option(&buf->b_p_tsr);
2037   check_string_option(&buf->b_p_tsrfu);
2038   check_string_option(&buf->b_p_lw);
2039   check_string_option(&buf->b_p_bkc);
2040   check_string_option(&buf->b_p_menc);
2041   check_string_option(&buf->b_p_vsts);
2042   check_string_option(&buf->b_p_vts);
2043 }
2044 
2045 /// Free the string allocated for an option.
2046 /// Checks for the string being empty_option. This may happen if we're out of
2047 /// memory, vim_strsave() returned NULL, which was replaced by empty_option by
2048 /// check_options().
2049 /// Does NOT check for P_ALLOCED flag!
free_string_option(char_u * p)2050 void free_string_option(char_u *p)
2051 {
2052   if (p != empty_option) {
2053     xfree(p);
2054   }
2055 }
2056 
clear_string_option(char_u ** pp)2057 void clear_string_option(char_u **pp)
2058 {
2059   if (*pp != empty_option) {
2060     xfree(*pp);
2061   }
2062   *pp = empty_option;
2063 }
2064 
check_string_option(char_u ** pp)2065 static void check_string_option(char_u **pp)
2066 {
2067   if (*pp == NULL) {
2068     *pp = empty_option;
2069   }
2070 }
2071 
2072 /// Return true when option "opt" was set from a modeline or in secure mode.
2073 /// Return false when it wasn't.
2074 /// Return -1 for an unknown option.
was_set_insecurely(win_T * const wp,char * opt,int opt_flags)2075 int was_set_insecurely(win_T *const wp, char *opt, int opt_flags)
2076 {
2077   int idx = findoption(opt);
2078 
2079   if (idx >= 0) {
2080     uint32_t *flagp = insecure_flag(wp, idx, opt_flags);
2081     return (*flagp & P_INSECURE) != 0;
2082   }
2083   internal_error("was_set_insecurely()");
2084   return -1;
2085 }
2086 
2087 /// Get a pointer to the flags used for the P_INSECURE flag of option
2088 /// "opt_idx".  For some local options a local flags field is used.
2089 /// NOTE: Caller must make sure that "wp" is set to the window from which
2090 /// the option is used.
insecure_flag(win_T * const wp,int opt_idx,int opt_flags)2091 static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
2092 {
2093   if (opt_flags & OPT_LOCAL) {
2094     assert(wp != NULL);
2095     switch ((int)options[opt_idx].indir) {
2096     case PV_STL:
2097       return &wp->w_p_stl_flags;
2098     case PV_FDE:
2099       return &wp->w_p_fde_flags;
2100     case PV_FDT:
2101       return &wp->w_p_fdt_flags;
2102     case PV_INDE:
2103       return &wp->w_buffer->b_p_inde_flags;
2104     case PV_FEX:
2105       return &wp->w_buffer->b_p_fex_flags;
2106     case PV_INEX:
2107       return &wp->w_buffer->b_p_inex_flags;
2108     }
2109   }
2110 
2111   // Nothing special, return global flags field.
2112   return &options[opt_idx].flags;
2113 }
2114 
2115 
2116 /// Redraw the window title and/or tab page text later.
redraw_titles(void)2117 static void redraw_titles(void)
2118 {
2119   need_maketitle = true;
2120   redraw_tabline = true;
2121 }
2122 
2123 static int shada_idx = -1;
2124 
2125 /// Set a string option to a new value (without checking the effect).
2126 /// The string is copied into allocated memory.
2127 /// if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used.
2128 /// When "set_sid" is zero set the scriptID to current_sctx.sc_sid.  When
2129 /// "set_sid" is SID_NONE don't set the scriptID.  Otherwise set the scriptID to
2130 /// "set_sid".
2131 ///
2132 /// @param opt_flags  OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
set_string_option_direct(const char * name,int opt_idx,const char_u * val,int opt_flags,int set_sid)2133 void set_string_option_direct(const char *name, int opt_idx, const char_u *val, int opt_flags,
2134                               int set_sid)
2135 {
2136   char_u *s;
2137   char_u **varp;
2138   int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
2139   int idx = opt_idx;
2140 
2141   if (idx == -1) {  // Use name.
2142     idx = findoption(name);
2143     if (idx < 0) {  // Not found (should not happen).
2144       internal_error("set_string_option_direct()");
2145       siemsg(_("For option %s"), name);
2146       return;
2147     }
2148   }
2149 
2150   if (options[idx].var == NULL) {       // can't set hidden option
2151     return;
2152   }
2153 
2154   assert((void *)options[idx].var != (void *)&p_shada);
2155 
2156   s = vim_strsave(val);
2157   {
2158     varp = (char_u **)get_varp_scope(&(options[idx]),
2159                                      both ? OPT_LOCAL : opt_flags);
2160     if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED)) {
2161       free_string_option(*varp);
2162     }
2163     *varp = s;
2164 
2165     // For buffer/window local option may also set the global value.
2166     if (both) {
2167       set_string_option_global(idx, varp);
2168     }
2169 
2170     options[idx].flags |= P_ALLOCED;
2171 
2172     /* When setting both values of a global option with a local value,
2173     * make the local value empty, so that the global value is used. */
2174     if (((int)options[idx].indir & PV_BOTH) && both) {
2175       free_string_option(*varp);
2176       *varp = empty_option;
2177     }
2178     if (set_sid != SID_NONE) {
2179       sctx_T script_ctx;
2180 
2181       if (set_sid == 0) {
2182         script_ctx = current_sctx;
2183       } else {
2184         script_ctx.sc_sid = set_sid;
2185         script_ctx.sc_seq = 0;
2186         script_ctx.sc_lnum = 0;
2187       }
2188       set_option_sctx_idx(idx, opt_flags, script_ctx);
2189     }
2190   }
2191 }
2192 
2193 /// Set global value for string option when it's a local option.
2194 ///
2195 /// @param opt_idx  option index
2196 /// @param varp  pointer to option variable
set_string_option_global(int opt_idx,char_u ** varp)2197 static void set_string_option_global(int opt_idx, char_u **varp)
2198 {
2199   char_u **p, *s;
2200 
2201   // the global value is always allocated
2202   if (options[opt_idx].var == VAR_WIN) {
2203     p = (char_u **)GLOBAL_WO(varp);
2204   } else {
2205     p = (char_u **)options[opt_idx].var;
2206   }
2207   if (options[opt_idx].indir != PV_NONE && p != varp) {
2208     s = vim_strsave(*varp);
2209     free_string_option(*p);
2210     *p = s;
2211   }
2212 }
2213 
2214 /// Set a string option to a new value, handling the effects
2215 ///
2216 /// @param[in]  opt_idx  Option to set.
2217 /// @param[in]  value  New value.
2218 /// @param[in]  opt_flags  Option flags: expected to contain #OPT_LOCAL and/or
2219 ///                        #OPT_GLOBAL.
2220 ///
2221 /// @return NULL on success, error message on error.
set_string_option(const int opt_idx,const char * const value,const int opt_flags)2222 static char *set_string_option(const int opt_idx, const char *const value, const int opt_flags)
2223   FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
2224 {
2225   if (options[opt_idx].var == NULL) {  // don't set hidden option
2226     return NULL;
2227   }
2228 
2229   char *const s = xstrdup(value);
2230   char **const varp = (char **)get_varp_scope(&(options[opt_idx]),
2231                                               ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
2232        ? (((int)options[opt_idx].indir & PV_BOTH)
2233           ? OPT_GLOBAL : OPT_LOCAL)
2234           : opt_flags));
2235   char *const oldval = *varp;
2236   *varp = s;
2237 
2238   char *const saved_oldval = xstrdup(oldval);
2239   char *const saved_newval = xstrdup(s);
2240 
2241   int value_checked = false;
2242   char *const r = did_set_string_option(opt_idx, (char_u **)varp, (int)true,
2243                                         (char_u *)oldval,
2244                                         NULL, 0, opt_flags, &value_checked);
2245   if (r == NULL) {
2246     did_set_option(opt_idx, opt_flags, true, value_checked);
2247   }
2248 
2249   // call autocommand after handling side effects
2250   if (r == NULL) {
2251     if (!starting) {
2252       trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval);
2253     }
2254     if (options[opt_idx].flags & P_UI_OPTION) {
2255       ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
2256                          STRING_OBJ(cstr_as_string(saved_newval)));
2257     }
2258   }
2259   xfree(saved_oldval);
2260   xfree(saved_newval);
2261 
2262   return r;
2263 }
2264 
2265 /// Return true if "val" is a valid name: only consists of alphanumeric ASCII
2266 /// characters or characters in "allowed".
valid_name(const char_u * val,const char * allowed)2267 static bool valid_name(const char_u *val, const char *allowed)
2268   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2269 {
2270   for (const char_u *s = val; *s != NUL; s++) {
2271     if (!ASCII_ISALNUM(*s)
2272         && vim_strchr((const char_u *)allowed, *s) == NULL) {
2273       return false;
2274     }
2275   }
2276   return true;
2277 }
2278 
2279 /// Return true if "val" is a valid 'filetype' name.
2280 /// Also used for 'syntax' and 'keymap'.
valid_filetype(const char_u * val)2281 static bool valid_filetype(const char_u *val)
2282   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2283 {
2284   return valid_name(val, ".-_");
2285 }
2286 
2287 /// Return true if "val" is a valid 'spelllang' value.
valid_spelllang(const char_u * val)2288 bool valid_spelllang(const char_u *val)
2289   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2290 {
2291   return valid_name(val, ".-_,@");
2292 }
2293 
2294 /// Return true if "val" is a valid 'spellfile' value.
valid_spellfile(const char_u * val)2295 static bool valid_spellfile(const char_u *val)
2296   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2297 {
2298   for (const char_u *s = val; *s != NUL; s++) {
2299     if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
2300       return false;
2301     }
2302   }
2303   return true;
2304 }
2305 
2306 /// Handle string options that need some action to perform when changed.
2307 /// Returns NULL for success, or an error message for an error.
2308 ///
2309 /// @param opt_idx  index in options[] table
2310 /// @param varp  pointer to the option variable
2311 /// @param new_value_alloced  new value was allocated
2312 /// @param oldval  previous value of the option
2313 /// @param errbuf  buffer for errors, or NULL
2314 /// @param errbuflen  length of errors buffer
2315 /// @param opt_flags  OPT_LOCAL and/or OPT_GLOBAL
2316 /// @param value_checked  value was checked to be safe, no need to set P_INSECURE
did_set_string_option(int opt_idx,char_u ** varp,bool new_value_alloced,char_u * oldval,char * errbuf,size_t errbuflen,int opt_flags,int * value_checked)2317 static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_alloced,
2318                                    char_u *oldval, char *errbuf, size_t errbuflen, int opt_flags,
2319                                    int *value_checked)
2320 {
2321   char *errmsg = NULL;
2322   char_u *s, *p;
2323   int did_chartab = false;
2324   char_u **gvarp;
2325   bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
2326   bool value_changed = false;
2327 
2328   /* Get the global option to compare with, otherwise we would have to check
2329    * two values for all local options. */
2330   gvarp = (char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
2331 
2332   // Disallow changing some options from secure mode
2333   if ((secure || sandbox != 0)
2334       && (options[opt_idx].flags & P_SECURE)) {
2335     errmsg = e_secure;
2336   } else if (((options[opt_idx].flags & P_NFNAME)
2337               && vim_strpbrk(*varp, (char_u *)(secure ? "/\\*?[|;&<>\r\n"
2338                                                       : "/\\*?[<>\r\n")) != NULL)
2339              || ((options[opt_idx].flags & P_NDNAME)
2340                  && vim_strpbrk(*varp, (char_u *)"*?[|;&<>\r\n") != NULL)) {
2341     // Check for a "normal" directory or file name in some options.  Disallow a
2342     // path separator (slash and/or backslash), wildcards and characters that
2343     // are often illegal in a file name. Be more permissive if "secure" is off.
2344     errmsg = e_invarg;
2345   } else if (gvarp == &p_bkc) {  // 'backupcopy'
2346     char_u *bkc   = p_bkc;
2347     unsigned int *flags = &bkc_flags;
2348 
2349     if (opt_flags & OPT_LOCAL) {
2350       bkc   = curbuf->b_p_bkc;
2351       flags = &curbuf->b_bkc_flags;
2352     }
2353 
2354     if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
2355       // make the local value empty: use the global value
2356       *flags = 0;
2357     } else {
2358       if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
2359         errmsg = e_invarg;
2360       }
2361 
2362       if (((*flags & BKC_AUTO) != 0)
2363           + ((*flags & BKC_YES) != 0)
2364           + ((*flags & BKC_NO) != 0) != 1) {
2365         // Must have exactly one of "auto", "yes"  and "no".
2366         (void)opt_strings_flags(oldval, p_bkc_values, flags, true);
2367         errmsg = e_invarg;
2368       }
2369     }
2370   } else if (varp == &p_bex || varp == &p_pm) {  // 'backupext' and 'patchmode'
2371     if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
2372                *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
2373       errmsg = N_("E589: 'backupext' and 'patchmode' are equal");
2374     }
2375   } else if (varp == &curwin->w_p_briopt) {  // 'breakindentopt'
2376     if (briopt_check(curwin) == FAIL) {
2377       errmsg = e_invarg;
2378     }
2379   } else if (varp == &p_isi
2380              || varp == &(curbuf->b_p_isk)
2381              || varp == &p_isp
2382              || varp == &p_isf) {
2383     // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
2384     // If the new option is invalid, use old value.  'lisp' option: refill
2385     // g_chartab[] for '-' char
2386     if (init_chartab() == FAIL) {
2387       did_chartab = true;           // need to restore it below
2388       errmsg = e_invarg;            // error in value
2389     }
2390   } else if (varp == &p_hf) {  // 'helpfile'
2391     // May compute new values for $VIM and $VIMRUNTIME
2392     if (didset_vim) {
2393       os_setenv("VIM", "", 1);
2394       didset_vim = false;
2395     }
2396     if (didset_vimruntime) {
2397       os_setenv("VIMRUNTIME", "", 1);
2398       didset_vimruntime = false;
2399     }
2400   } else if (varp == &p_rtp || varp == &p_pp) {  // 'runtimepath' 'packpath'
2401     runtime_search_path_invalidate();
2402   } else if (varp == &curwin->w_p_culopt
2403              || gvarp == &curwin->w_allbuf_opt.wo_culopt) {  // 'cursorlineopt'
2404     if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) {
2405       errmsg = e_invarg;
2406     }
2407   } else if (varp == &curwin->w_p_cc) {  // 'colorcolumn'
2408     errmsg = check_colorcolumn(curwin);
2409   } else if (varp == &p_hlg) {  // 'helplang'
2410     // Check for "", "ab", "ab,cd", etc.
2411     for (s = p_hlg; *s != NUL; s += 3) {
2412       if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
2413         errmsg = e_invarg;
2414         break;
2415       }
2416       if (s[2] == NUL) {
2417         break;
2418       }
2419     }
2420   } else if (varp == &p_hl) {
2421     // 'highlight'
2422     if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) {
2423       errmsg = e_unsupportedoption;
2424     }
2425   } else if (varp == &p_jop) {  // 'jumpoptions'
2426     if (opt_strings_flags(p_jop, p_jop_values, &jop_flags, true) != OK) {
2427       errmsg = e_invarg;
2428     }
2429   } else if (gvarp == &p_nf) {  // 'nrformats'
2430     if (check_opt_strings(*varp, p_nf_values, true) != OK) {
2431       errmsg = e_invarg;
2432     }
2433   } else if (varp == &p_ssop) {  // 'sessionoptions'
2434     if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
2435       errmsg = e_invarg;
2436     }
2437     if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
2438       // Don't allow both "sesdir" and "curdir".
2439       (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
2440       errmsg = e_invarg;
2441     }
2442   } else if (varp == &p_vop) {  // 'viewoptions'
2443     if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
2444       errmsg = e_invarg;
2445     }
2446   } else if (varp == &p_rdb) {  // 'redrawdebug'
2447     if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) {
2448       errmsg = e_invarg;
2449     }
2450   } else if (varp == &p_sbo) {  // 'scrollopt'
2451     if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) {
2452       errmsg = e_invarg;
2453     }
2454   } else if (varp == &p_ambw || (int *)varp == &p_emoji) {
2455     // 'ambiwidth'
2456     if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
2457       errmsg = e_invarg;
2458     } else {
2459       FOR_ALL_TAB_WINDOWS(tp, wp) {
2460         if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
2461           errmsg = _("E834: Conflicts with value of 'listchars'");
2462           goto ambw_end;
2463         }
2464         if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
2465           errmsg = _("E835: Conflicts with value of 'fillchars'");
2466           goto ambw_end;
2467         }
2468       }
2469 ambw_end:
2470       {}  // clint prefers {} over ; as an empty statement
2471     }
2472   } else if (varp == &p_bg) {  // 'background'
2473     if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
2474       int dark = (*p_bg == 'd');
2475 
2476       init_highlight(false, false);
2477 
2478       if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
2479         // The color scheme must have set 'background' back to another
2480         // value, that's not what we want here.  Disable the color
2481         // scheme and set the colors again.
2482         do_unlet(S_LEN("g:colors_name"), true);
2483         free_string_option(p_bg);
2484         p_bg = vim_strsave((char_u *)(dark ? "dark" : "light"));
2485         check_string_option(&p_bg);
2486         init_highlight(false, false);
2487       }
2488     } else {
2489       errmsg = e_invarg;
2490     }
2491   } else if (varp == &p_wim) {  // 'wildmode'
2492     if (check_opt_wim() == FAIL) {
2493       errmsg = e_invarg;
2494     }
2495     // 'wildoptions'
2496   } else if (varp == &p_wop) {
2497     if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
2498       errmsg = e_invarg;
2499     }
2500   } else if (varp == &p_wak) {  // 'winaltkeys'
2501     if (*p_wak == NUL
2502         || check_opt_strings(p_wak, p_wak_values, false) != OK) {
2503       errmsg = e_invarg;
2504     }
2505   } else if (varp == &p_ei) {  // 'eventignore'
2506     if (check_ei() == FAIL) {
2507       errmsg = e_invarg;
2508     }
2509     // 'encoding', 'fileencoding' and 'makeencoding'
2510   } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
2511     if (gvarp == &p_fenc) {
2512       if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
2513         errmsg = e_modifiable;
2514       } else if (vim_strchr(*varp, ',') != NULL) {
2515         // No comma allowed in 'fileencoding'; catches confusing it
2516         // with 'fileencodings'.
2517         errmsg = e_invarg;
2518       } else {
2519         // May show a "+" in the title now.
2520         redraw_titles();
2521         // Add 'fileencoding' to the swap file.
2522         ml_setflags(curbuf);
2523       }
2524     }
2525 
2526     if (errmsg == NULL) {
2527       // canonize the value, so that STRCMP() can be used on it
2528       p = enc_canonize(*varp);
2529       xfree(*varp);
2530       *varp = p;
2531       if (varp == &p_enc) {
2532         // only encoding=utf-8 allowed
2533         if (STRCMP(p_enc, "utf-8") != 0) {
2534           errmsg = e_unsupportedoption;
2535         }
2536       }
2537     }
2538   } else if (varp == &p_penc) {
2539     // Canonize printencoding if VIM standard one
2540     p = enc_canonize(p_penc);
2541     xfree(p_penc);
2542     p_penc = p;
2543   } else if (varp == &curbuf->b_p_keymap) {
2544     if (!valid_filetype(*varp)) {
2545       errmsg = e_invarg;
2546     } else {
2547       int secure_save = secure;
2548 
2549       // Reset the secure flag, since the value of 'keymap' has
2550       // been checked to be safe.
2551       secure = 0;
2552 
2553       // load or unload key mapping tables
2554       errmsg = keymap_init();
2555 
2556       secure = secure_save;
2557 
2558       // Since we check the value, there is no need to set P_INSECURE,
2559       // even when the value comes from a modeline.
2560       *value_checked = true;
2561     }
2562 
2563     if (errmsg == NULL) {
2564       if (*curbuf->b_p_keymap != NUL) {
2565         // Installed a new keymap, switch on using it.
2566         curbuf->b_p_iminsert = B_IMODE_LMAP;
2567         if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
2568           curbuf->b_p_imsearch = B_IMODE_LMAP;
2569         }
2570       } else {
2571         // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
2572         if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
2573           curbuf->b_p_iminsert = B_IMODE_NONE;
2574         }
2575         if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
2576           curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
2577         }
2578       }
2579       if ((opt_flags & OPT_LOCAL) == 0) {
2580         set_iminsert_global();
2581         set_imsearch_global();
2582       }
2583       status_redraw_curbuf();
2584     }
2585   } else if (gvarp == &p_ff) {  // 'fileformat'
2586     if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
2587       errmsg = e_modifiable;
2588     } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
2589       errmsg = e_invarg;
2590     } else {
2591       redraw_titles();
2592       // update flag in swap file
2593       ml_setflags(curbuf);
2594       /* Redraw needed when switching to/from "mac": a CR in the text
2595        * will be displayed differently. */
2596       if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
2597         redraw_curbuf_later(NOT_VALID);
2598       }
2599     }
2600   } else if (varp == &p_ffs) {  // 'fileformats'
2601     if (check_opt_strings(p_ffs, p_ff_values, true) != OK) {
2602       errmsg = e_invarg;
2603     }
2604   } else if (gvarp == &p_mps) {  // 'matchpairs'
2605     for (p = *varp; *p != NUL; p++) {
2606       int x2 = -1;
2607       int x3 = -1;
2608 
2609       if (*p != NUL) {
2610         p += utfc_ptr2len(p);
2611       }
2612       if (*p != NUL) {
2613         x2 = *p++;
2614       }
2615       if (*p != NUL) {
2616         x3 = utf_ptr2char(p);
2617         p += utfc_ptr2len(p);
2618       }
2619       if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
2620         errmsg = e_invarg;
2621         break;
2622       }
2623       if (*p == NUL) {
2624         break;
2625       }
2626     }
2627   } else if (gvarp == &p_com) {  // 'comments'
2628     for (s = *varp; *s;) {
2629       while (*s && *s != ':') {
2630         if (vim_strchr((char_u *)COM_ALL, *s) == NULL
2631             && !ascii_isdigit(*s) && *s != '-') {
2632           errmsg = illegal_char(errbuf, errbuflen, *s);
2633           break;
2634         }
2635         s++;
2636       }
2637       if (*s++ == NUL) {
2638         errmsg = N_("E524: Missing colon");
2639       } else if (*s == ',' || *s == NUL) {
2640         errmsg = N_("E525: Zero length string");
2641       }
2642       if (errmsg != NULL) {
2643         break;
2644       }
2645       while (*s && *s != ',') {
2646         if (*s == '\\' && s[1] != NUL) {
2647           s++;
2648         }
2649         s++;
2650       }
2651       s = skip_to_option_part(s);
2652     }
2653   } else if (varp == &p_lcs) {  // global 'listchars'
2654     errmsg = set_chars_option(curwin, varp, false);
2655     if (errmsg == NULL) {
2656       // The current window is set to use the global 'listchars' value.
2657       // So clear the window-local value.
2658       if (!(opt_flags & OPT_GLOBAL)) {
2659         clear_string_option(&curwin->w_p_lcs);
2660       }
2661       FOR_ALL_TAB_WINDOWS(tp, wp) {
2662         // If no error was returned above, we don't expect an error
2663         // here, so ignore the return value.
2664         (void)set_chars_option(wp, &wp->w_p_lcs, true);
2665       }
2666       redraw_all_later(NOT_VALID);
2667     }
2668   } else if (varp == &curwin->w_p_lcs) {  // local 'listchars'
2669     errmsg = set_chars_option(curwin, varp, true);
2670   } else if (varp == &p_fcs) {  // global 'fillchars'
2671     errmsg = set_chars_option(curwin, varp, false);
2672     if (errmsg == NULL) {
2673       // The current window is set to use the global 'fillchars' value.
2674       // So clear the window-local value.
2675       if (!(opt_flags & OPT_GLOBAL)) {
2676         clear_string_option(&curwin->w_p_fcs);
2677       }
2678       FOR_ALL_TAB_WINDOWS(tp, wp) {
2679         // If no error was returned above, we don't expect an error
2680         // here, so ignore the return value.
2681         (void)set_chars_option(wp, &wp->w_p_fcs, true);
2682       }
2683       redraw_all_later(NOT_VALID);
2684     }
2685   } else if (varp == &curwin->w_p_fcs) {  // local 'fillchars'
2686     errmsg = set_chars_option(curwin, varp, true);
2687   } else if (varp == &p_cedit) {  // 'cedit'
2688     errmsg = check_cedit();
2689   } else if (varp == &p_vfile) {  // 'verbosefile'
2690     verbose_stop();
2691     if (*p_vfile != NUL && verbose_open() == FAIL) {
2692       errmsg = e_invarg;
2693     }
2694     // 'shada'
2695   } else if (varp == &p_shada) {
2696     // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
2697     //              option.
2698     opt_idx = ((options[opt_idx].fullname[0] == 'v')
2699                ? (shada_idx == -1
2700                   ? ((shada_idx = findoption("shada")))
2701                   : shada_idx)
2702                   : opt_idx);
2703     // Update free_oldval now that we have the opt_idx for 'shada', otherwise
2704     // there would be a disconnect between the check for P_ALLOCED at the start
2705     // of the function and the set of P_ALLOCED at the end of the function.
2706     free_oldval = (options[opt_idx].flags & P_ALLOCED);
2707     for (s = p_shada; *s;) {
2708       // Check it's a valid character
2709       if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
2710         errmsg = illegal_char(errbuf, errbuflen, *s);
2711         break;
2712       }
2713       if (*s == 'n') {          // name is always last one
2714         break;
2715       } else if (*s == 'r') {  // skip until next ','
2716         while (*++s && *s != ',') {}
2717       } else if (*s == '%') {
2718         // optional number
2719         while (ascii_isdigit(*++s)) {}
2720       } else if (*s == '!' || *s == 'h' || *s == 'c') {
2721         s++;                    // no extra chars
2722       } else {                    // must have a number
2723         while (ascii_isdigit(*++s)) {}
2724 
2725         if (!ascii_isdigit(*(s - 1))) {
2726           if (errbuf != NULL) {
2727             vim_snprintf((char *)errbuf, errbuflen,
2728                          _("E526: Missing number after <%s>"),
2729                          transchar_byte(*(s - 1)));
2730             errmsg = errbuf;
2731           } else {
2732             errmsg = "";
2733           }
2734           break;
2735         }
2736       }
2737       if (*s == ',') {
2738         s++;
2739       } else if (*s) {
2740         if (errbuf != NULL) {
2741           errmsg = N_("E527: Missing comma");
2742         } else {
2743           errmsg = "";
2744         }
2745         break;
2746       }
2747     }
2748     if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
2749       errmsg = N_("E528: Must specify a ' value");
2750     }
2751   } else if (gvarp == &p_sbr) {  // 'showbreak'
2752     for (s = *varp; *s;) {
2753       if (ptr2cells(s) != 1) {
2754         errmsg = N_("E595: 'showbreak' contains unprintable or wide character");
2755       }
2756       MB_PTR_ADV(s);
2757     }
2758   } else if (varp == &p_guicursor) {  // 'guicursor'
2759     errmsg = parse_shape_opt(SHAPE_CURSOR);
2760   } else if (varp == &p_popt) {
2761     errmsg = parse_printoptions();
2762   } else if (varp == &p_pmfn) {
2763     errmsg = parse_printmbfont();
2764   } else if (varp == &p_langmap) {  // 'langmap'
2765     langmap_set();
2766   } else if (varp == &p_breakat) {  // 'breakat'
2767     fill_breakat_flags();
2768   } else if (varp == &p_titlestring || varp == &p_iconstring) {
2769     // 'titlestring' and 'iconstring'
2770     int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
2771 
2772     // NULL => statusline syntax
2773     if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
2774       stl_syntax |= flagval;
2775     } else {
2776       stl_syntax &= ~flagval;
2777     }
2778     did_set_title();
2779   } else if (varp == &p_sel) {  // 'selection'
2780     if (*p_sel == NUL
2781         || check_opt_strings(p_sel, p_sel_values, false) != OK) {
2782       errmsg = e_invarg;
2783     }
2784   } else if (varp == &p_slm) {  // 'selectmode'
2785     if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
2786       errmsg = e_invarg;
2787     }
2788   } else if (varp == &p_km) {  // 'keymodel'
2789     if (check_opt_strings(p_km, p_km_values, true) != OK) {
2790       errmsg = e_invarg;
2791     } else {
2792       km_stopsel = (vim_strchr(p_km, 'o') != NULL);
2793       km_startsel = (vim_strchr(p_km, 'a') != NULL);
2794     }
2795   } else if (varp == &p_mousem) {  // 'mousemodel'
2796     if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
2797       errmsg = e_invarg;
2798     }
2799   } else if (varp == &p_swb) {  // 'switchbuf'
2800     if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
2801       errmsg = e_invarg;
2802     }
2803   } else if (varp == &p_debug) {  // 'debug'
2804     if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
2805       errmsg = e_invarg;
2806     }
2807   } else if (varp == &p_dy) {  // 'display'
2808     if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
2809       errmsg = e_invarg;
2810     } else {
2811       (void)init_chartab();
2812       msg_grid_validate();
2813     }
2814   } else if (varp == &p_ead) {  // 'eadirection'
2815     if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
2816       errmsg = e_invarg;
2817     }
2818   } else if (varp == &p_cb) {  // 'clipboard'
2819     if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
2820       errmsg = e_invarg;
2821     }
2822   } else if (varp == &(curwin->w_s->b_p_spl)  // 'spell'
2823              || varp == &(curwin->w_s->b_p_spf)) {
2824     // When 'spelllang' or 'spellfile' is set and there is a window for this
2825     // buffer in which 'spell' is set load the wordlists.
2826     const bool is_spellfile = varp == &(curwin->w_s->b_p_spf);
2827 
2828     if ((is_spellfile && !valid_spellfile(*varp))
2829         || (!is_spellfile && !valid_spelllang(*varp))) {
2830       errmsg = e_invarg;
2831     } else {
2832       errmsg = did_set_spell_option(is_spellfile);
2833     }
2834   } else if (varp == &(curwin->w_s->b_p_spc)) {
2835     // When 'spellcapcheck' is set compile the regexp program.
2836     errmsg = compile_cap_prog(curwin->w_s);
2837   } else if (varp == &(curwin->w_s->b_p_spo)) {  // 'spelloptions'
2838     if (**varp != NUL && STRCMP("camel", *varp) != 0) {
2839       errmsg = e_invarg;
2840     }
2841   } else if (varp == &p_sps) {  // 'spellsuggest'
2842     if (spell_check_sps() != OK) {
2843       errmsg = e_invarg;
2844     }
2845   } else if (varp == &p_msm) {  // 'mkspellmem'
2846     if (spell_check_msm() != OK) {
2847       errmsg = e_invarg;
2848     }
2849   } else if (gvarp == &p_bh) {
2850     // When 'bufhidden' is set, check for valid value.
2851     if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
2852       errmsg = e_invarg;
2853     }
2854   } else if (gvarp == &p_bt) {
2855     // When 'buftype' is set, check for valid value.
2856     if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
2857         || (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
2858         || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
2859       errmsg = e_invarg;
2860     } else {
2861       if (curwin->w_status_height) {
2862         curwin->w_redr_status = true;
2863         redraw_later(curwin, VALID);
2864       }
2865       curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
2866       redraw_titles();
2867     }
2868   } else if (gvarp == &p_stl || varp == &p_ruf) {
2869     // 'statusline' or 'rulerformat'
2870     int wid;
2871 
2872     if (varp == &p_ruf) {       // reset ru_wid first
2873       ru_wid = 0;
2874     }
2875     s = *varp;
2876     if (varp == &p_ruf && *s == '%') {
2877       // set ru_wid if 'ruf' starts with "%99("
2878       if (*++s == '-') {        // ignore a '-'
2879         s++;
2880       }
2881       wid = getdigits_int(&s, true, 0);
2882       if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
2883         ru_wid = wid;
2884       } else {
2885         errmsg = check_stl_option(p_ruf);
2886       }
2887     } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
2888       // check 'statusline' only if it doesn't start with "%!"
2889       errmsg = check_stl_option(s);
2890     }
2891     if (varp == &p_ruf && errmsg == NULL) {
2892       comp_col();
2893     }
2894   } else if (gvarp == &p_cpt) {
2895     // check if it is a valid value for 'complete' -- Acevedo
2896     for (s = *varp; *s;) {
2897       while (*s == ',' || *s == ' ') {
2898         s++;
2899       }
2900       if (!*s) {
2901         break;
2902       }
2903       if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) {
2904         errmsg = illegal_char(errbuf, errbuflen, *s);
2905         break;
2906       }
2907       if (*++s != NUL && *s != ',' && *s != ' ') {
2908         if (s[-1] == 'k' || s[-1] == 's') {
2909           // skip optional filename after 'k' and 's'
2910           while (*s && *s != ',' && *s != ' ') {
2911             if (*s == '\\' && s[1] != NUL) {
2912               s++;
2913             }
2914             s++;
2915           }
2916         } else {
2917           if (errbuf != NULL) {
2918             vim_snprintf((char *)errbuf, errbuflen,
2919                          _("E535: Illegal character after <%c>"),
2920                          *--s);
2921             errmsg = errbuf;
2922           } else {
2923             errmsg = "";
2924           }
2925           break;
2926         }
2927       }
2928     }
2929   } else if (varp == &p_cot) {  // 'completeopt'
2930     if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
2931       errmsg = e_invarg;
2932     } else {
2933       completeopt_was_set();
2934     }
2935 #ifdef BACKSLASH_IN_FILENAME
2936   } else if (gvarp == &p_csl) {  // 'completeslash'
2937     if (check_opt_strings(p_csl, p_csl_values, false) != OK
2938         || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) {
2939       errmsg = e_invarg;
2940     }
2941 #endif
2942   } else if (varp == &curwin->w_p_scl) {
2943     // 'signcolumn'
2944     if (check_signcolumn(*varp) != OK) {
2945       errmsg = e_invarg;
2946     }
2947     // When changing the 'signcolumn' to or from 'number', recompute the
2948     // width of the number column if 'number' or 'relativenumber' is set.
2949     if (((*oldval == 'n' && *(oldval + 1) == 'u')
2950          || (*curwin->w_p_scl == 'n' && *(curwin->w_p_scl + 1) == 'u'))
2951         && (curwin->w_p_nu || curwin->w_p_rnu)) {
2952       curwin->w_nrwidth_line_count = 0;
2953     }
2954   } else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
2955     // 'foldcolumn'
2956     if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
2957       errmsg = e_invarg;
2958     }
2959   } else if (varp == &p_pt) {
2960     // 'pastetoggle': translate key codes like in a mapping
2961     if (*p_pt) {
2962       (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
2963                               CPO_TO_CPO_FLAGS);
2964       if (p != NULL) {
2965         if (new_value_alloced) {
2966           free_string_option(p_pt);
2967         }
2968         p_pt = p;
2969         new_value_alloced = true;
2970       }
2971     }
2972   } else if (varp == &p_bs) {  // 'backspace'
2973     if (ascii_isdigit(*p_bs)) {
2974       if (*p_bs > '3' || p_bs[1] != NUL) {
2975         errmsg = e_invarg;
2976       }
2977     } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
2978       errmsg = e_invarg;
2979     }
2980   } else if (varp == &p_bo) {
2981     if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
2982       errmsg = e_invarg;
2983     }
2984   } else if (gvarp == &p_tc) {  // 'tagcase'
2985     unsigned int *flags;
2986 
2987     if (opt_flags & OPT_LOCAL) {
2988       p = curbuf->b_p_tc;
2989       flags = &curbuf->b_tc_flags;
2990     } else {
2991       p = p_tc;
2992       flags = &tc_flags;
2993     }
2994 
2995     if ((opt_flags & OPT_LOCAL) && *p == NUL) {
2996       // make the local value empty: use the global value
2997       *flags = 0;
2998     } else if (*p == NUL
2999                || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
3000       errmsg = e_invarg;
3001     }
3002   } else if (varp == &p_cmp) {  // 'casemap'
3003     if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
3004       errmsg = e_invarg;
3005     }
3006   } else if (varp == &p_dip) {  // 'diffopt'
3007     if (diffopt_changed() == FAIL) {
3008       errmsg = e_invarg;
3009     }
3010   } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) {  // 'foldmethod'
3011     if (check_opt_strings(*varp, p_fdm_values, false) != OK
3012         || *curwin->w_p_fdm == NUL) {
3013       errmsg = e_invarg;
3014     } else {
3015       foldUpdateAll(curwin);
3016       if (foldmethodIsDiff(curwin)) {
3017         newFoldLevel();
3018       }
3019     }
3020   } else if (varp == &curwin->w_p_fde) {  // 'foldexpr'
3021     if (foldmethodIsExpr(curwin)) {
3022       foldUpdateAll(curwin);
3023     }
3024   } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) {  // 'foldmarker'
3025     p = vim_strchr(*varp, ',');
3026     if (p == NULL) {
3027       errmsg = N_("E536: comma required");
3028     } else if (p == *varp || p[1] == NUL) {
3029       errmsg = e_invarg;
3030     } else if (foldmethodIsMarker(curwin)) {
3031       foldUpdateAll(curwin);
3032     }
3033   } else if (gvarp == &p_cms) {  // 'commentstring'
3034     if (**varp != NUL && strstr((char *)(*varp), "%s") == NULL) {
3035       errmsg = N_("E537: 'commentstring' must be empty or contain %s");
3036     }
3037   } else if (varp == &p_fdo) {  // 'foldopen'
3038     if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
3039       errmsg = e_invarg;
3040     }
3041   } else if (varp == &p_fcl) {  // 'foldclose'
3042     if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
3043       errmsg = e_invarg;
3044     }
3045   } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) {  // 'foldignore'
3046     if (foldmethodIsIndent(curwin)) {
3047       foldUpdateAll(curwin);
3048     }
3049   } else if (varp == &p_ve) {  // 'virtualedit'
3050     if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) {
3051       errmsg = e_invarg;
3052     } else if (STRCMP(p_ve, oldval) != 0) {
3053       // Recompute cursor position in case the new 've' setting
3054       // changes something.
3055       validate_virtcol();
3056       coladvance(curwin->w_virtcol);
3057     }
3058   } else if (varp == &p_csqf) {
3059     if (p_csqf != NULL) {
3060       p = p_csqf;
3061       while (*p != NUL) {
3062         if (vim_strchr((char_u *)CSQF_CMDS, *p) == NULL
3063             || p[1] == NUL
3064             || vim_strchr((char_u *)CSQF_FLAGS, p[1]) == NULL
3065             || (p[2] != NUL && p[2] != ',')) {
3066           errmsg = e_invarg;
3067           break;
3068         } else if (p[2] == NUL) {
3069           break;
3070         } else {
3071           p += 3;
3072         }
3073       }
3074     }
3075   } else if (gvarp == &p_cino) {  // 'cinoptions'
3076     // TODO(vim): recognize errors
3077     parse_cino(curbuf);
3078     // inccommand
3079   } else if (varp == &p_icm) {
3080     if (check_opt_strings(p_icm, p_icm_values, false) != OK) {
3081       errmsg = e_invarg;
3082     }
3083   } else if (gvarp == &p_ft) {
3084     if (!valid_filetype(*varp)) {
3085       errmsg = e_invarg;
3086     } else {
3087       value_changed = STRCMP(oldval, *varp) != 0;
3088 
3089       // Since we check the value, there is no need to set P_INSECURE,
3090       // even when the value comes from a modeline.
3091       *value_checked = true;
3092     }
3093   } else if (gvarp == &p_syn) {
3094     if (!valid_filetype(*varp)) {
3095       errmsg = e_invarg;
3096     } else {
3097       value_changed = STRCMP(oldval, *varp) != 0;
3098 
3099       // Since we check the value, there is no need to set P_INSECURE,
3100       // even when the value comes from a modeline.
3101       *value_checked = true;
3102     }
3103   } else if (varp == &curwin->w_p_winhl) {
3104     if (!parse_winhl_opt(curwin)) {
3105       errmsg = e_invarg;
3106     }
3107   } else if (varp == &p_tpf) {
3108     if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
3109       errmsg = e_invarg;
3110     }
3111   } else if (varp == &(curbuf->b_p_vsts)) {  // 'varsofttabstop'
3112     char_u *cp;
3113 
3114     if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
3115       if (curbuf->b_p_vsts_array) {
3116         xfree(curbuf->b_p_vsts_array);
3117         curbuf->b_p_vsts_array = 0;
3118       }
3119     } else {
3120       for (cp = *varp; *cp; cp++) {
3121         if (ascii_isdigit(*cp)) {
3122           continue;
3123         }
3124         if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
3125           continue;
3126         }
3127         errmsg = e_invarg;
3128         break;
3129       }
3130       if (errmsg == NULL) {
3131         long *oldarray = curbuf->b_p_vsts_array;
3132         if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
3133           xfree(oldarray);
3134         } else {
3135           errmsg = e_invarg;
3136         }
3137       }
3138     }
3139   } else if (varp == &(curbuf->b_p_vts)) {  // 'vartabstop'
3140     char_u *cp;
3141 
3142     if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
3143       if (curbuf->b_p_vts_array) {
3144         xfree(curbuf->b_p_vts_array);
3145         curbuf->b_p_vts_array = NULL;
3146       }
3147     } else {
3148       for (cp = *varp; *cp; cp++) {
3149         if (ascii_isdigit(*cp)) {
3150           continue;
3151         }
3152         if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
3153           continue;
3154         }
3155         errmsg = e_invarg;
3156         break;
3157       }
3158       if (errmsg == NULL) {
3159         long *oldarray = curbuf->b_p_vts_array;
3160         if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
3161           xfree(oldarray);
3162           if (foldmethodIsIndent(curwin)) {
3163             foldUpdateAll(curwin);
3164           }
3165         } else {
3166           errmsg = e_invarg;
3167         }
3168       }
3169     }
3170   } else if (varp == &p_qftf) {
3171     if (!qf_process_qftf_option()) {
3172       errmsg = e_invarg;
3173     }
3174   } else {
3175     // Options that are a list of flags.
3176     p = NULL;
3177     if (varp == &p_ww) {  // 'whichwrap'
3178       p = (char_u *)WW_ALL;
3179     }
3180     if (varp == &p_shm) {  // 'shortmess'
3181       p = (char_u *)SHM_ALL;
3182     } else if (varp == &(p_cpo)) {  // 'cpoptions'
3183       p = (char_u *)CPO_VI;
3184     } else if (varp == &(curbuf->b_p_fo)) {  // 'formatoptions'
3185       p = (char_u *)FO_ALL;
3186     } else if (varp == &curwin->w_p_cocu) {  // 'concealcursor'
3187       p = (char_u *)COCU_ALL;
3188     } else if (varp == &p_mouse) {  // 'mouse'
3189       p = (char_u *)MOUSE_ALL;
3190     }
3191     if (p != NULL) {
3192       for (s = *varp; *s; s++) {
3193         if (vim_strchr(p, *s) == NULL) {
3194           errmsg = illegal_char(errbuf, errbuflen, *s);
3195           break;
3196         }
3197       }
3198     }
3199   }
3200 
3201   /*
3202    * If error detected, restore the previous value.
3203    */
3204   if (errmsg != NULL) {
3205     if (new_value_alloced) {
3206       free_string_option(*varp);
3207     }
3208     *varp = oldval;
3209     /*
3210      * When resetting some values, need to act on it.
3211      */
3212     if (did_chartab) {
3213       (void)init_chartab();
3214     }
3215   } else {
3216     // Remember where the option was set.
3217     set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
3218     // Free string options that are in allocated memory.
3219     // Use "free_oldval", because recursiveness may change the flags under
3220     // our fingers (esp. init_highlight()).
3221     if (free_oldval) {
3222       free_string_option(oldval);
3223     }
3224     if (new_value_alloced) {
3225       options[opt_idx].flags |= P_ALLOCED;
3226     } else {
3227       options[opt_idx].flags &= ~P_ALLOCED;
3228     }
3229 
3230     if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
3231         && ((int)options[opt_idx].indir & PV_BOTH)) {
3232       /* global option with local value set to use global value; free
3233        * the local value and make it empty */
3234       p = get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
3235       free_string_option(*(char_u **)p);
3236       *(char_u **)p = empty_option;
3237     } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
3238       // May set global value for local option.
3239       set_string_option_global(opt_idx, varp);
3240     }
3241 
3242     /*
3243      * Trigger the autocommand only after setting the flags.
3244      */
3245     // When 'syntax' is set, load the syntax of that name
3246     if (varp == &(curbuf->b_p_syn)) {
3247       static int syn_recursive = 0;
3248 
3249       syn_recursive++;
3250       // Only pass true for "force" when the value changed or not used
3251       // recursively, to avoid endless recurrence.
3252       apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname,
3253                      value_changed || syn_recursive == 1, curbuf);
3254       curbuf->b_flags |= BF_SYN_SET;
3255       syn_recursive--;
3256     } else if (varp == &(curbuf->b_p_ft)) {
3257       // 'filetype' is set, trigger the FileType autocommand
3258       // Skip this when called from a modeline and the filetype was
3259       // already set to this value.
3260       if (!(opt_flags & OPT_MODELINE) || value_changed) {
3261         static int ft_recursive = 0;
3262         int secure_save = secure;
3263 
3264         // Reset the secure flag, since the value of 'filetype' has
3265         // been checked to be safe.
3266         secure = 0;
3267 
3268         ft_recursive++;
3269         did_filetype = true;
3270         // Only pass true for "force" when the value changed or not
3271         // used recursively, to avoid endless recurrence.
3272         apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
3273                        value_changed || ft_recursive == 1, curbuf);
3274         ft_recursive--;
3275         // Just in case the old "curbuf" is now invalid
3276         if (varp != &(curbuf->b_p_ft)) {
3277           varp = NULL;
3278         }
3279         secure = secure_save;
3280       }
3281     }
3282     if (varp == &(curwin->w_s->b_p_spl)) {
3283       char_u fname[200];
3284       char_u *q = curwin->w_s->b_p_spl;
3285 
3286       // Skip the first name if it is "cjk".
3287       if (STRNCMP(q, "cjk,", 4) == 0) {
3288         q += 4;
3289       }
3290 
3291       /*
3292        * Source the spell/LANG.vim in 'runtimepath'.
3293        * They could set 'spellcapcheck' depending on the language.
3294        * Use the first name in 'spelllang' up to '_region' or
3295        * '.encoding'.
3296        */
3297       for (p = q; *p != NUL; p++) {
3298         if (!ASCII_ISALNUM(*p) && *p != '-') {
3299           break;
3300         }
3301       }
3302       if (p > q) {
3303         vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
3304                      (int)(p - q), q);
3305         source_runtime((char *)fname, DIP_ALL);
3306       }
3307     }
3308   }
3309 
3310   if (varp == &p_mouse) {
3311     setmouse();  // in case 'mouse' changed
3312   }
3313 
3314   if (curwin->w_curswant != MAXCOL
3315       && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
3316     curwin->w_set_curswant = true;
3317   }
3318 
3319   check_redraw(options[opt_idx].flags);
3320 
3321   return errmsg;
3322 }  // NOLINT(readability/fn_size)
3323 
3324 /// Simple int comparison function for use with qsort()
int_cmp(const void * a,const void * b)3325 static int int_cmp(const void *a, const void *b)
3326 {
3327   return *(const int *)a - *(const int *)b;
3328 }
3329 
3330 /// Handle setting 'signcolumn' for value 'val'
3331 ///
3332 /// @return OK when the value is valid, FAIL otherwise
check_signcolumn(char_u * val)3333 int check_signcolumn(char_u *val)
3334 {
3335   if (*val == NUL) {
3336     return FAIL;
3337   }
3338   // check for basic match
3339   if (check_opt_strings(val, p_scl_values, false) == OK) {
3340     return OK;
3341   }
3342 
3343   // check for 'auto:<NUMBER>-<NUMBER>'
3344   if (STRLEN(val) == 8
3345       && !STRNCMP(val, "auto:", 5)
3346       && ascii_isdigit(val[5])
3347       && val[6] == '-'
3348       && ascii_isdigit(val[7])) {
3349     int min = val[5] - '0';
3350     int max = val[7] - '0';
3351     if (min < 1 || max < 2 || min > 8 || max > 9 || min >= max) {
3352       return FAIL;
3353     }
3354     return OK;
3355   }
3356 
3357   return FAIL;
3358 }
3359 
3360 /// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
3361 ///
3362 /// @return error message, NULL if it's OK.
check_colorcolumn(win_T * wp)3363 char *check_colorcolumn(win_T *wp)
3364 {
3365   char_u *s;
3366   int col;
3367   unsigned int count = 0;
3368   int color_cols[256];
3369   int j = 0;
3370 
3371   if (wp->w_buffer == NULL) {
3372     return NULL;      // buffer was closed
3373   }
3374 
3375   for (s = wp->w_p_cc; *s != NUL && count < 255;) {
3376     if (*s == '-' || *s == '+') {
3377       // -N and +N: add to 'textwidth'
3378       col = (*s == '-') ? -1 : 1;
3379       s++;
3380       if (!ascii_isdigit(*s)) {
3381         return e_invarg;
3382       }
3383       col = col * getdigits_int(&s, true, 0);
3384       if (wp->w_buffer->b_p_tw == 0) {
3385         goto skip;          // 'textwidth' not set, skip this item
3386       }
3387       assert((col >= 0
3388               && wp->w_buffer->b_p_tw <= INT_MAX - col
3389               && wp->w_buffer->b_p_tw + col >= INT_MIN)
3390              || (col < 0
3391                  && wp->w_buffer->b_p_tw >= INT_MIN - col
3392                  && wp->w_buffer->b_p_tw + col <= INT_MAX));
3393       col += (int)wp->w_buffer->b_p_tw;
3394       if (col < 0) {
3395         goto skip;
3396       }
3397     } else if (ascii_isdigit(*s)) {
3398       col = getdigits_int(&s, true, 0);
3399     } else {
3400       return e_invarg;
3401     }
3402     color_cols[count++] = col - 1;      // 1-based to 0-based
3403 skip:
3404     if (*s == NUL) {
3405       break;
3406     }
3407     if (*s != ',') {
3408       return e_invarg;
3409     }
3410     if (*++s == NUL) {
3411       return e_invarg;        // illegal trailing comma as in "set cc=80,"
3412     }
3413   }
3414 
3415   xfree(wp->w_p_cc_cols);
3416   if (count == 0) {
3417     wp->w_p_cc_cols = NULL;
3418   } else {
3419     wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
3420     /* sort the columns for faster usage on screen redraw inside
3421      * win_line() */
3422     qsort(color_cols, count, sizeof(int), int_cmp);
3423 
3424     for (unsigned int i = 0; i < count; i++) {
3425       // skip duplicates
3426       if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
3427         wp->w_p_cc_cols[j++] = color_cols[i];
3428       }
3429     }
3430     wp->w_p_cc_cols[j] = -1;        // end marker
3431   }
3432 
3433   return NULL;    // no error
3434 }
3435 
check_blending(win_T * wp)3436 void check_blending(win_T *wp)
3437 {
3438   wp->w_grid_alloc.blending =
3439     wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow);
3440 }
3441 
3442 /// Calls mb_cptr2char_adv(p) and returns the character.
3443 /// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
3444 /// Returns 0 for invalid hex or invalid UTF-8 byte.
get_encoded_char_adv(char_u ** p)3445 static int get_encoded_char_adv(char_u **p)
3446 {
3447   char_u *s = *p;
3448 
3449   if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
3450     int64_t num = 0;
3451     int bytes;
3452     int n;
3453     for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
3454       *p += 2;
3455       n = hexhex2nr(*p);
3456       if (n < 0) {
3457         return 0;
3458       }
3459       num = num * 256 + n;
3460     }
3461     *p += 2;
3462     return (int)num;
3463   }
3464 
3465   // TODO(bfredl): use schar_T representation and utfc_ptr2len
3466   int clen = utf_ptr2len(s);
3467   int c = mb_cptr2char_adv((const char_u **)p);
3468   if (clen == 1 && c > 127) {  // Invalid UTF-8 byte
3469     return 0;
3470   }
3471   return c;
3472 }
3473 
3474 /// Handle setting 'listchars' or 'fillchars'.
3475 /// Assume monocell characters
3476 ///
3477 /// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
3478 /// @return error message, NULL if it's OK.
set_chars_option(win_T * wp,char_u ** varp,bool set)3479 static char *set_chars_option(win_T *wp, char_u **varp, bool set)
3480 {
3481   int round, i, len, entries;
3482   char_u *p, *s;
3483   int c1;
3484   int c2 = 0;
3485   int c3 = 0;
3486   char_u *last_multispace = NULL;  // Last occurrence of "multispace:"
3487   int multispace_len = 0;          // Length of lcs-multispace string
3488 
3489   struct chars_tab {
3490     int *cp;    ///< char value
3491     char *name;  ///< char id
3492     int def;    ///< default value
3493   };
3494   struct chars_tab *tab;
3495 
3496   struct chars_tab fcs_tab[] = {
3497     { &wp->w_p_fcs_chars.stl,     "stl",      ' '  },
3498     { &wp->w_p_fcs_chars.stlnc,   "stlnc",    ' '  },
3499     { &wp->w_p_fcs_chars.vert,    "vert",     9474 },  // │
3500     { &wp->w_p_fcs_chars.fold,    "fold",     183  },  // ·
3501     { &wp->w_p_fcs_chars.foldopen,   "foldopen",  '-'  },
3502     { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+'  },
3503     { &wp->w_p_fcs_chars.foldsep,    "foldsep",   9474 },  // │
3504     { &wp->w_p_fcs_chars.diff,    "diff",     '-'  },
3505     { &wp->w_p_fcs_chars.msgsep,  "msgsep",   ' '  },
3506     { &wp->w_p_fcs_chars.eob,     "eob",      '~'  },
3507   };
3508   struct chars_tab lcs_tab[] = {
3509     { &wp->w_p_lcs_chars.eol,     "eol",      NUL  },
3510     { &wp->w_p_lcs_chars.ext,     "extends",  NUL  },
3511     { &wp->w_p_lcs_chars.nbsp,    "nbsp",     NUL  },
3512     { &wp->w_p_lcs_chars.prec,    "precedes", NUL  },
3513     { &wp->w_p_lcs_chars.space,   "space",    NUL  },
3514     { &wp->w_p_lcs_chars.tab2,    "tab",      NUL  },
3515     { &wp->w_p_lcs_chars.lead,    "lead",     NUL  },
3516     { &wp->w_p_lcs_chars.trail,   "trail",    NUL  },
3517     { &wp->w_p_lcs_chars.conceal, "conceal",  NUL  },
3518   };
3519 
3520   if (varp == &p_lcs || varp == &wp->w_p_lcs) {
3521     tab = lcs_tab;
3522     entries = ARRAY_SIZE(lcs_tab);
3523     if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
3524       varp = &p_lcs;
3525     }
3526   } else {
3527     tab = fcs_tab;
3528     entries = ARRAY_SIZE(fcs_tab);
3529     if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
3530       varp = &p_fcs;
3531     }
3532     if (*p_ambw == 'd') {
3533       // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
3534       // forbidden (TUI limitation?). Set old defaults.
3535       fcs_tab[2].def = '|';
3536       fcs_tab[6].def = '|';
3537       fcs_tab[3].def = '-';
3538     } else {
3539       fcs_tab[2].def = 9474;  // │
3540       fcs_tab[6].def = 9474;  // │
3541       fcs_tab[3].def = 183;   // ·
3542     }
3543   }
3544 
3545   // first round: check for valid value, second round: assign values
3546   for (round = 0; round <= (set ? 1 : 0); round++) {
3547     if (round > 0) {
3548       // After checking that the value is valid: set defaults
3549       for (i = 0; i < entries; i++) {
3550         if (tab[i].cp != NULL) {
3551           *(tab[i].cp) = tab[i].def;
3552         }
3553       }
3554       if (varp == &p_lcs || varp == &wp->w_p_lcs) {
3555         wp->w_p_lcs_chars.tab1 = NUL;
3556         wp->w_p_lcs_chars.tab3 = NUL;
3557         if (wp->w_p_lcs_chars.multispace != NULL) {
3558           xfree(wp->w_p_lcs_chars.multispace);
3559         }
3560         if (multispace_len > 0) {
3561           wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
3562           wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
3563         } else {
3564           wp->w_p_lcs_chars.multispace = NULL;
3565         }
3566       }
3567     }
3568     p = *varp;
3569     while (*p) {
3570       for (i = 0; i < entries; i++) {
3571         len = (int)STRLEN(tab[i].name);
3572         if (STRNCMP(p, tab[i].name, len) == 0
3573             && p[len] == ':'
3574             && p[len + 1] != NUL) {
3575           c2 = c3 = 0;
3576           s = p + len + 1;
3577           c1 = get_encoded_char_adv(&s);
3578           if (c1 == 0 || utf_char2cells(c1) > 1) {
3579             return e_invarg;
3580           }
3581           if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
3582             if (*s == NUL) {
3583               return e_invarg;
3584             }
3585             c2 = get_encoded_char_adv(&s);
3586             if (c2 == 0 || utf_char2cells(c2) > 1) {
3587               return e_invarg;
3588             }
3589             if (!(*s == ',' || *s == NUL)) {
3590               c3 = get_encoded_char_adv(&s);
3591               if (c3 == 0 || utf_char2cells(c3) > 1) {
3592                 return e_invarg;
3593               }
3594             }
3595           }
3596           if (*s == ',' || *s == NUL) {
3597             if (round > 0) {
3598               if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
3599                 wp->w_p_lcs_chars.tab1 = c1;
3600                 wp->w_p_lcs_chars.tab2 = c2;
3601                 wp->w_p_lcs_chars.tab3 = c3;
3602               } else if (tab[i].cp != NULL) {
3603                 *(tab[i].cp) = c1;
3604               }
3605             }
3606             p = s;
3607             break;
3608           }
3609         }
3610       }
3611 
3612       if (i == entries) {
3613         len = (int)STRLEN("multispace");
3614         if ((varp == &p_lcs || varp == &wp->w_p_lcs)
3615             && STRNCMP(p, "multispace", len) == 0
3616             && p[len] == ':'
3617             && p[len + 1] != NUL) {
3618           s = p + len + 1;
3619           if (round == 0) {
3620             // Get length of lcs-multispace string in the first round
3621             last_multispace = p;
3622             multispace_len = 0;
3623             while (*s != NUL && *s != ',') {
3624               c1 = get_encoded_char_adv(&s);
3625               if (c1 == 0 || utf_char2cells(c1) > 1) {
3626                 return e_invarg;
3627               }
3628               multispace_len++;
3629             }
3630             if (multispace_len == 0) {
3631               // lcs-multispace cannot be an empty string
3632               return e_invarg;
3633             }
3634             p = s;
3635           } else {
3636             int multispace_pos = 0;
3637             while (*s != NUL && *s != ',') {
3638               c1 = get_encoded_char_adv(&s);
3639               if (p == last_multispace) {
3640                 wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
3641               }
3642             }
3643             p = s;
3644           }
3645         } else {
3646           return e_invarg;
3647         }
3648       }
3649       if (*p == ',') {
3650         p++;
3651       }
3652     }
3653   }
3654 
3655   return NULL;          // no error
3656 }
3657 
3658 /// Check validity of options with the 'statusline' format.
3659 /// Return error message or NULL.
check_stl_option(char_u * s)3660 char *check_stl_option(char_u *s)
3661 {
3662   int groupdepth = 0;
3663   static char errbuf[80];
3664 
3665   while (*s) {
3666     // Check for valid keys after % sequences
3667     while (*s && *s != '%') {
3668       s++;
3669     }
3670     if (!*s) {
3671       break;
3672     }
3673     s++;
3674     if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) {
3675       s++;
3676       continue;
3677     }
3678     if (*s == ')') {
3679       s++;
3680       if (--groupdepth < 0) {
3681         break;
3682       }
3683       continue;
3684     }
3685     if (*s == '-') {
3686       s++;
3687     }
3688     while (ascii_isdigit(*s)) {
3689       s++;
3690     }
3691     if (*s == STL_USER_HL) {
3692       continue;
3693     }
3694     if (*s == '.') {
3695       s++;
3696       while (*s && ascii_isdigit(*s)) {
3697         s++;
3698       }
3699     }
3700     if (*s == '(') {
3701       groupdepth++;
3702       continue;
3703     }
3704     if (vim_strchr(STL_ALL, *s) == NULL) {
3705       return illegal_char(errbuf, sizeof(errbuf), *s);
3706     }
3707     if (*s == '{') {
3708       int reevaluate = (*s == '%');
3709       s++;
3710       while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) {
3711         s++;
3712       }
3713       if (*s != '}') {
3714         return N_("E540: Unclosed expression sequence");
3715       }
3716     }
3717   }
3718   if (groupdepth != 0) {
3719     return N_("E542: unbalanced groups");
3720   }
3721   return NULL;
3722 }
3723 
did_set_spell_option(bool is_spellfile)3724 static char *did_set_spell_option(bool is_spellfile)
3725 {
3726   char *errmsg = NULL;
3727 
3728   if (is_spellfile) {
3729     int l = (int)STRLEN(curwin->w_s->b_p_spf);
3730     if (l > 0
3731         && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
3732       errmsg = e_invarg;
3733     }
3734   }
3735 
3736   if (errmsg == NULL) {
3737     FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
3738       if (wp->w_buffer == curbuf && wp->w_p_spell) {
3739         errmsg = did_set_spelllang(wp);
3740         break;
3741       }
3742     }
3743   }
3744 
3745   return errmsg;
3746 }
3747 
3748 /// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
3749 /// Return error message when failed, NULL when OK.
compile_cap_prog(synblock_T * synblock)3750 static char *compile_cap_prog(synblock_T *synblock)
3751   FUNC_ATTR_NONNULL_ALL
3752 {
3753   regprog_T *rp = synblock->b_cap_prog;
3754   char_u *re;
3755 
3756   if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
3757     synblock->b_cap_prog = NULL;
3758   } else {
3759     // Prepend a ^ so that we only match at one column
3760     re = concat_str((char_u *)"^", synblock->b_p_spc);
3761     synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
3762     xfree(re);
3763     if (synblock->b_cap_prog == NULL) {
3764       synblock->b_cap_prog = rp;         // restore the previous program
3765       return e_invarg;
3766     }
3767   }
3768 
3769   vim_regfree(rp);
3770   return NULL;
3771 }
3772 
3773 /// Handle setting `winhighlight' in window "wp"
parse_winhl_opt(win_T * wp)3774 static bool parse_winhl_opt(win_T *wp)
3775 {
3776   int w_hl_id_normal = 0;
3777   int w_hl_ids[HLF_COUNT] = { 0 };
3778   int hlf;
3779 
3780   const char *p = (const char *)wp->w_p_winhl;
3781   while (*p) {
3782     char *colon = strchr(p, ':');
3783     if (!colon) {
3784       return false;
3785     }
3786     size_t nlen = (size_t)(colon-p);
3787     char *hi = colon+1;
3788     char *commap = xstrchrnul(hi, ',');
3789     int len = (int)(commap-hi);
3790     int hl_id = len ? syn_check_group(hi, len) : -1;
3791 
3792     if (strncmp("Normal", p, nlen) == 0) {
3793       w_hl_id_normal = hl_id;
3794     } else {
3795       for (hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
3796         if (strlen(hlf_names[hlf]) == nlen
3797             && strncmp(hlf_names[hlf], p, nlen) == 0) {
3798           w_hl_ids[hlf] = hl_id;
3799           break;
3800         }
3801       }
3802       if (hlf == HLF_COUNT) {
3803         return false;
3804       }
3805     }
3806 
3807     p = *commap ? commap+1 : "";
3808   }
3809 
3810   wp->w_hl_id_normal = w_hl_id_normal;
3811   memcpy(wp->w_hl_ids, w_hl_ids, sizeof(w_hl_ids));
3812   wp->w_hl_needs_update = true;
3813   return true;
3814 }
3815 
3816 // Set the script_ctx for an option, taking care of setting the buffer- or
3817 // window-local value.
set_option_sctx_idx(int opt_idx,int opt_flags,sctx_T script_ctx)3818 static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
3819 {
3820   int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
3821   int indir = (int)options[opt_idx].indir;
3822   const LastSet last_set = {
3823     .script_ctx = {
3824       script_ctx.sc_sid,
3825       script_ctx.sc_seq,
3826       script_ctx.sc_lnum + sourcing_lnum
3827     },
3828     current_channel_id
3829   };
3830 
3831   // Remember where the option was set.  For local options need to do that
3832   // in the buffer or window structure.
3833   if (both || (opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0) {
3834     options[opt_idx].last_set = last_set;
3835   }
3836   if (both || (opt_flags & OPT_LOCAL)) {
3837     if (indir & PV_BUF) {
3838       curbuf->b_p_script_ctx[indir & PV_MASK] = last_set;
3839     } else if (indir & PV_WIN) {
3840       curwin->w_p_script_ctx[indir & PV_MASK] = last_set;
3841     }
3842   }
3843 }
3844 
3845 /// Set the value of a boolean option, taking care of side effects
3846 ///
3847 /// @param[in]  opt_idx  Option index in options[] table.
3848 /// @param[out]  varp  Pointer to the option variable.
3849 /// @param[in]  value  New value.
3850 /// @param[in]  opt_flags  OPT_LOCAL and/or OPT_GLOBAL.
3851 ///
3852 /// @return NULL on success, error message on error.
set_bool_option(const int opt_idx,char_u * const varp,const int value,const int opt_flags)3853 static char *set_bool_option(const int opt_idx, char_u *const varp, const int value,
3854                              const int opt_flags)
3855 {
3856   int old_value = *(int *)varp;
3857 
3858   // Disallow changing some options from secure mode
3859   if ((secure || sandbox != 0)
3860       && (options[opt_idx].flags & P_SECURE)) {
3861     return (char *)e_secure;
3862   }
3863 
3864   *(int *)varp = value;             // set the new value
3865   // Remember where the option was set.
3866   set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
3867 
3868 
3869   // May set global value for local option.
3870   if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
3871     *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value;
3872   }
3873 
3874   // Ensure that options set to p_force_on cannot be disabled.
3875   if ((int *)varp == &p_force_on && p_force_on == false) {
3876     p_force_on = true;
3877     return e_unsupportedoption;
3878     // Ensure that options set to p_force_off cannot be enabled.
3879   } else if ((int *)varp == &p_force_off && p_force_off == true) {
3880     p_force_off = false;
3881     return (char *)e_unsupportedoption;
3882   } else if ((int *)varp == &p_lrm) {
3883     // 'langremap' -> !'langnoremap'
3884     p_lnr = !p_lrm;
3885   } else if ((int *)varp == &p_lnr) {
3886     // 'langnoremap' -> !'langremap'
3887     p_lrm = !p_lnr;
3888   } else if ((int *)varp == &curwin->w_p_cul && !value && old_value) {
3889     // 'cursorline'
3890     reset_cursorline();
3891     // 'undofile'
3892   } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) {
3893     // Only take action when the option was set. When reset we do not
3894     // delete the undo file, the option may be set again without making
3895     // any changes in between.
3896     if (curbuf->b_p_udf || p_udf) {
3897       char_u hash[UNDO_HASH_SIZE];
3898 
3899       FOR_ALL_BUFFERS(bp) {
3900         // When 'undofile' is set globally: for every buffer, otherwise
3901         // only for the current buffer: Try to read in the undofile,
3902         // if one exists, the buffer wasn't changed and the buffer was
3903         // loaded
3904         if ((curbuf == bp
3905              || (opt_flags & OPT_GLOBAL) || opt_flags == 0)
3906             && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) {
3907           u_compute_hash(bp, hash);
3908           u_read_undo(NULL, hash, bp->b_fname);
3909         }
3910       }
3911     }
3912   } else if ((int *)varp == &curbuf->b_p_ro) {
3913     // when 'readonly' is reset globally, also reset readonlymode
3914     if (!curbuf->b_p_ro && (opt_flags & OPT_LOCAL) == 0) {
3915       readonlymode = false;
3916     }
3917 
3918     // when 'readonly' is set may give W10 again
3919     if (curbuf->b_p_ro) {
3920       curbuf->b_did_warn = false;
3921     }
3922 
3923     redraw_titles();
3924   } else if ((int *)varp == &curbuf->b_p_ma) {
3925     // when 'modifiable' is changed, redraw the window title
3926     redraw_titles();
3927   } else if ((int *)varp == &curbuf->b_p_eol) {
3928     // when 'endofline' is changed, redraw the window title
3929     redraw_titles();
3930   } else if ((int *)varp == &curbuf->b_p_fixeol) {
3931     // when 'fixeol' is changed, redraw the window title
3932     redraw_titles();
3933   } else if ((int *)varp == &curbuf->b_p_bomb) {
3934     // when 'bomb' is changed, redraw the window title and tab page text
3935     redraw_titles();
3936   } else if ((int *)varp == &curbuf->b_p_bin) {
3937     // when 'bin' is set also set some other options
3938     set_options_bin(old_value, curbuf->b_p_bin, opt_flags);
3939     redraw_titles();
3940   } else if ((int *)varp == &curbuf->b_p_bl && old_value != curbuf->b_p_bl) {
3941     // when 'buflisted' changes, trigger autocommands
3942     apply_autocmds(curbuf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE,
3943                    NULL, NULL, true, curbuf);
3944   } else if ((int *)varp == &curbuf->b_p_swf) {
3945     // when 'swf' is set, create swapfile, when reset remove swapfile
3946     if (curbuf->b_p_swf && p_uc) {
3947       ml_open_file(curbuf);                     // create the swap file
3948     } else {
3949       // no need to reset curbuf->b_may_swap, ml_open_file() will check
3950       // buf->b_p_swf
3951       mf_close_file(curbuf, true);              // remove the swap file
3952     }
3953   } else if ((int *)varp == &p_terse) {
3954     // when 'terse' is set change 'shortmess'
3955     char_u *p;
3956 
3957     p = vim_strchr(p_shm, SHM_SEARCH);
3958 
3959     // insert 's' in p_shm
3960     if (p_terse && p == NULL) {
3961       STRCPY(IObuff, p_shm);
3962       STRCAT(IObuff, "s");
3963       set_string_option_direct("shm", -1, IObuff, OPT_FREE, 0);
3964     } else if (!p_terse && p != NULL) {  // remove 's' from p_shm
3965       STRMOVE(p, p + 1);
3966     }
3967   } else if ((int *)varp == &p_paste) {
3968     // when 'paste' is set or reset also change other options
3969     paste_option_changed();
3970   } else if ((int *)varp == &p_im) {
3971     // when 'insertmode' is set from an autocommand need to do work here
3972     if (p_im) {
3973       if ((State & INSERT) == 0) {
3974         need_start_insertmode = true;
3975       }
3976       stop_insert_mode = false;
3977     } else if (old_value) {  // only reset if it was set previously
3978       need_start_insertmode = false;
3979       stop_insert_mode = true;
3980       if (restart_edit != 0 && mode_displayed) {
3981         clear_cmdline = true;  // remove "(insert)"
3982       }
3983       restart_edit = 0;
3984     }
3985   } else if ((int *)varp == &p_ic && p_hls) {
3986     // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
3987     redraw_all_later(SOME_VALID);
3988   } else if ((int *)varp == &p_hls) {
3989     // when 'hlsearch' is set or reset: reset no_hlsearch
3990     set_no_hlsearch(false);
3991   } else if ((int *)varp == &curwin->w_p_scb) {
3992     // when 'scrollbind' is set: snapshot the current position to avoid a jump
3993     // at the end of normal_cmd()
3994     if (curwin->w_p_scb) {
3995       do_check_scrollbind(false);
3996       curwin->w_scbind_pos = curwin->w_topline;
3997     }
3998   } else if ((int *)varp == &curwin->w_p_pvw) {
3999     // There can be only one window with 'previewwindow' set.
4000     if (curwin->w_p_pvw) {
4001       FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
4002         if (win->w_p_pvw && win != curwin) {
4003           curwin->w_p_pvw = false;
4004           return N_("E590: A preview window already exists");
4005         }
4006       }
4007     }
4008   } else if (varp == (char_u *)&(curbuf->b_p_lisp)) {
4009     // When 'lisp' option changes include/exclude '-' in
4010     // keyword characters.
4011     (void)buf_init_chartab(curbuf, false);          // ignore errors
4012   } else if ((int *)varp == &p_title) {
4013     // when 'title' changed, may need to change the title; same for 'icon'
4014     did_set_title();
4015   } else if ((int *)varp == &p_icon) {
4016     did_set_title();
4017   } else if ((int *)varp == &curbuf->b_changed) {
4018     if (!value) {
4019       save_file_ff(curbuf);             // Buffer is unchanged
4020     }
4021     redraw_titles();
4022     modified_was_set = value;
4023   }
4024 
4025 #ifdef BACKSLASH_IN_FILENAME
4026   else if ((int *)varp == &p_ssl) {
4027     if (p_ssl) {
4028       psepc = '/';
4029       psepcN = '\\';
4030       pseps[0] = '/';
4031     } else {
4032       psepc = '\\';
4033       psepcN = '/';
4034       pseps[0] = '\\';
4035     }
4036 
4037     // need to adjust the file name arguments and buffer names.
4038     buflist_slash_adjust();
4039     alist_slash_adjust();
4040     scriptnames_slash_adjust();
4041   }
4042 #endif
4043   else if ((int *)varp == &curwin->w_p_wrap) {
4044     // If 'wrap' is set, set w_leftcol to zero.
4045     if (curwin->w_p_wrap) {
4046       curwin->w_leftcol = 0;
4047     }
4048   } else if ((int *)varp == &p_ea) {
4049     if (p_ea && !old_value) {
4050       win_equal(curwin, false, 0);
4051     }
4052   } else if ((int *)varp == &p_acd) {
4053     // Change directories when the 'acd' option is set now.
4054     do_autochdir();
4055   } else if ((int *)varp == &curwin->w_p_diff) {  // 'diff'
4056     // May add or remove the buffer from the list of diff buffers.
4057     diff_buf_adjust(curwin);
4058     if (foldmethodIsDiff(curwin)) {
4059       foldUpdateAll(curwin);
4060     }
4061   } else if ((int *)varp == &curwin->w_p_spell) {  // 'spell'
4062     if (curwin->w_p_spell) {
4063       char *errmsg = did_set_spelllang(curwin);
4064       if (errmsg != NULL) {
4065         emsg(_(errmsg));
4066       }
4067     }
4068   }
4069 
4070   if ((int *)varp == &curwin->w_p_arab) {
4071     if (curwin->w_p_arab) {
4072       /*
4073        * 'arabic' is set, handle various sub-settings.
4074        */
4075       if (!p_tbidi) {
4076         // set rightleft mode
4077         if (!curwin->w_p_rl) {
4078           curwin->w_p_rl = true;
4079           changed_window_setting();
4080         }
4081 
4082         // Enable Arabic shaping (major part of what Arabic requires)
4083         if (!p_arshape) {
4084           p_arshape = true;
4085           redraw_all_later(NOT_VALID);
4086         }
4087       }
4088 
4089       // Arabic requires a utf-8 encoding, inform the user if its not
4090       // set.
4091       if (STRCMP(p_enc, "utf-8") != 0) {
4092         static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'");
4093 
4094         msg_source(HL_ATTR(HLF_W));
4095         msg_attr(_(w_arabic), HL_ATTR(HLF_W));
4096         set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
4097       }
4098 
4099       // set 'delcombine'
4100       p_deco = true;
4101 
4102       // Force-set the necessary keymap for arabic.
4103       set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
4104     } else {
4105       /*
4106        * 'arabic' is reset, handle various sub-settings.
4107        */
4108       if (!p_tbidi) {
4109         // reset rightleft mode
4110         if (curwin->w_p_rl) {
4111           curwin->w_p_rl = false;
4112           changed_window_setting();
4113         }
4114 
4115         // 'arabicshape' isn't reset, it is a global option and
4116         // another window may still need it "on".
4117       }
4118 
4119       // 'delcombine' isn't reset, it is a global option and another
4120       // window may still want it "on".
4121 
4122       // Revert to the default keymap
4123       curbuf->b_p_iminsert = B_IMODE_NONE;
4124       curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
4125     }
4126   }
4127 
4128 
4129   /*
4130    * End of handling side effects for bool options.
4131    */
4132 
4133   // after handling side effects, call autocommand
4134 
4135   options[opt_idx].flags |= P_WAS_SET;
4136 
4137   // Don't do this while starting up or recursively.
4138   if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
4139     char buf_old[2];
4140     char buf_new[2];
4141     char buf_type[7];
4142     vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d",
4143                  old_value ? true: false);
4144     vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d",
4145                  value ? true: false);
4146     vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
4147                  (opt_flags & OPT_LOCAL) ? "local" : "global");
4148     set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
4149     set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
4150     set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
4151     apply_autocmds(EVENT_OPTIONSET,
4152                    (char_u *)options[opt_idx].fullname,
4153                    NULL, false, NULL);
4154     reset_v_option_vars();
4155   }
4156 
4157   if (options[opt_idx].flags & P_UI_OPTION) {
4158     ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
4159                        BOOLEAN_OBJ(value));
4160   }
4161 
4162   comp_col();                       // in case 'ruler' or 'showcmd' changed
4163   if (curwin->w_curswant != MAXCOL
4164       && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
4165     curwin->w_set_curswant = true;
4166   }
4167   check_redraw(options[opt_idx].flags);
4168 
4169   return NULL;
4170 }
4171 
4172 /// Set the value of a number option, taking care of side effects
4173 ///
4174 /// @param[in]  opt_idx  Option index in options[] table.
4175 /// @param[out]  varp  Pointer to the option variable.
4176 /// @param[in]  value  New value.
4177 /// @param  errbuf  Buffer for error messages.
4178 /// @param[in]  errbuflen  Length of `errbuf`.
4179 /// @param[in]  opt_flags  OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
4180 ///
4181 /// @return NULL on success, error message on error.
set_num_option(int opt_idx,char_u * varp,long value,char * errbuf,size_t errbuflen,int opt_flags)4182 static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, size_t errbuflen,
4183                             int opt_flags)
4184 {
4185   char *errmsg = NULL;
4186   long old_value = *(long *)varp;
4187   long old_Rows = Rows;                 // remember old Rows
4188   long *pp = (long *)varp;
4189 
4190   // Disallow changing some options from secure mode.
4191   if ((secure || sandbox != 0)
4192       && (options[opt_idx].flags & P_SECURE)) {
4193     return e_secure;
4194   }
4195 
4196   // Many number options assume their value is in the signed int range.
4197   if (value < INT_MIN || value > INT_MAX) {
4198     return e_invarg;
4199   }
4200 
4201   // Options that need some validation.
4202   if (pp == &p_wh) {
4203     if (value < 1) {
4204       errmsg = e_positive;
4205     } else if (p_wmh > value) {
4206       errmsg = e_winheight;
4207     }
4208   } else if (pp == &p_hh) {
4209     if (value < 0) {
4210       errmsg = e_positive;
4211     }
4212   } else if (pp == &p_wmh) {
4213     if (value < 0) {
4214       errmsg = e_positive;
4215     } else if (value > p_wh) {
4216       errmsg = e_winheight;
4217     }
4218   } else if (pp == &p_wiw) {
4219     if (value < 1) {
4220       errmsg = e_positive;
4221     } else if (p_wmw > value) {
4222       errmsg = e_winwidth;
4223     }
4224   } else if (pp == &p_wmw) {
4225     if (value < 0) {
4226       errmsg = e_positive;
4227     } else if (value > p_wiw) {
4228       errmsg = e_winwidth;
4229     }
4230   } else if (pp == &p_mco) {
4231     value = MAX_MCO;
4232   } else if (pp == &p_titlelen) {
4233     if (value < 0) {
4234       errmsg = e_positive;
4235     }
4236   } else if (pp == &p_uc) {
4237     if (value < 0) {
4238       errmsg = e_positive;
4239     }
4240   } else if (pp == &p_ch) {
4241     int minval = ui_has(kUIMessages) ? 0 : 1;
4242     if (value < minval) {
4243       errmsg = e_positive;
4244     }
4245   } else if (pp == &p_tm) {
4246     if (value < 0) {
4247       errmsg = e_positive;
4248     }
4249   } else if (pp == &p_hi) {
4250     if (value < 0) {
4251       errmsg = e_positive;
4252     } else if (value > 10000) {
4253       errmsg = e_invarg;
4254     }
4255   } else if (pp == &p_re) {
4256     if (value < 0 || value > 2) {
4257       errmsg = e_invarg;
4258     }
4259   } else if (pp == &p_report) {
4260     if (value < 0) {
4261       errmsg = e_positive;
4262     }
4263   } else if (pp == &p_so) {
4264     if (value < 0 && full_screen) {
4265       errmsg = e_positive;
4266     }
4267   } else if (pp == &p_siso) {
4268     if (value < 0 && full_screen) {
4269       errmsg = e_positive;
4270     }
4271   } else if (pp == &p_cwh) {
4272     if (value < 1) {
4273       errmsg = e_positive;
4274     }
4275   } else if (pp == &p_ut) {
4276     if (value < 0) {
4277       errmsg = e_positive;
4278     }
4279   } else if (pp == &p_ss) {
4280     if (value < 0) {
4281       errmsg = e_positive;
4282     }
4283   } else if (pp == &curwin->w_p_fdl || pp == &curwin->w_allbuf_opt.wo_fdl) {
4284     if (value < 0) {
4285       errmsg = e_positive;
4286     }
4287   } else if (pp == &curwin->w_p_cole || pp == &curwin->w_allbuf_opt.wo_cole) {
4288     if (value < 0) {
4289       errmsg = e_positive;
4290     } else if (value > 3) {
4291       errmsg = e_invarg;
4292     }
4293   } else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) {
4294     if (value < 1) {
4295       errmsg = e_positive;
4296     } else if (value > 20) {
4297       errmsg = e_invarg;
4298     }
4299   } else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) {
4300     if (value < 0 || value > B_IMODE_LAST) {
4301       errmsg = e_invarg;
4302     }
4303   } else if (pp == &curbuf->b_p_imsearch || pp == &p_imsearch) {
4304     if (value < -1 || value > B_IMODE_LAST) {
4305       errmsg = e_invarg;
4306     }
4307   } else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
4308     errmsg = e_invarg;
4309   } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
4310     if (value < -1 || value > SB_MAX) {
4311       errmsg = e_invarg;
4312     }
4313   } else if (pp == &curbuf->b_p_sw || pp == &p_sw) {
4314     if (value < 0) {
4315       errmsg = e_positive;
4316     }
4317   } else if (pp == &curbuf->b_p_ts || pp == &p_ts) {
4318     if (value < 1) {
4319       errmsg = e_positive;
4320     }
4321   } else if (pp == &curbuf->b_p_tw || pp == &p_tw) {
4322     if (value < 0) {
4323       errmsg = e_positive;
4324     }
4325   } else if (pp == &p_wd) {
4326     if (value < 0) {
4327       errmsg = e_positive;
4328     }
4329   }
4330 
4331   // Don't change the value and return early if validation failed.
4332   if (errmsg != NULL) {
4333     return (char *)errmsg;
4334   }
4335 
4336   *pp = value;
4337   // Remember where the option was set.
4338   set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
4339 
4340   // For these options we want to fix some invalid values.
4341   if (pp == &p_window) {
4342     if (p_window < 1) {
4343       p_window = Rows - 1;
4344     } else if (p_window >= Rows) {
4345       p_window = Rows - 1;
4346     }
4347   } else if (pp == &p_ch) {
4348     if (ui_has(kUIMessages)) {
4349       p_ch = 0;
4350     }
4351     if (p_ch > Rows - min_rows() + 1) {
4352       p_ch = Rows - min_rows() + 1;
4353     }
4354   }
4355 
4356   // Number options that need some action when changed
4357   if (pp == &p_wh) {
4358     // 'winheight'
4359     if (!ONE_WINDOW && curwin->w_height < p_wh) {
4360       win_setheight((int)p_wh);
4361     }
4362   } else if (pp == &p_hh) {
4363     // 'helpheight'
4364     if (!ONE_WINDOW && curbuf->b_help && curwin->w_height < p_hh) {
4365       win_setheight((int)p_hh);
4366     }
4367   } else if (pp == &p_wmh) {
4368     // 'winminheight'
4369     win_setminheight();
4370   } else if (pp == &p_wiw) {
4371     // 'winwidth'
4372     if (!ONE_WINDOW && curwin->w_width < p_wiw) {
4373       win_setwidth((int)p_wiw);
4374     }
4375   } else if (pp == &p_wmw) {
4376     // 'winminwidth'
4377     win_setminwidth();
4378   } else if (pp == &p_ls) {
4379     last_status(false);  // (re)set last window status line.
4380   } else if (pp == &p_stal) {
4381     // (re)set tab page line
4382     shell_new_rows();   // recompute window positions and heights
4383   } else if (pp == &curwin->w_p_fdl) {
4384     newFoldLevel();
4385   } else if (pp == &curwin->w_p_fml) {
4386     foldUpdateAll(curwin);
4387   } else if (pp == &curwin->w_p_fdn) {
4388     if (foldmethodIsSyntax(curwin) || foldmethodIsIndent(curwin)) {
4389       foldUpdateAll(curwin);
4390     }
4391   } else if (pp == &curbuf->b_p_sw || pp == &curbuf->b_p_ts) {
4392     // 'shiftwidth' or 'tabstop'
4393     if (foldmethodIsIndent(curwin)) {
4394       foldUpdateAll(curwin);
4395     }
4396     // When 'shiftwidth' changes, or it's zero and 'tabstop' changes:
4397     // parse 'cinoptions'.
4398     if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) {
4399       parse_cino(curbuf);
4400     }
4401   } else if (pp == &curbuf->b_p_iminsert) {
4402     showmode();
4403     // Show/unshow value of 'keymap' in status lines.
4404     status_redraw_curbuf();
4405   } else if (pp == &p_titlelen) {
4406     // if 'titlelen' has changed, redraw the title
4407     if (starting != NO_SCREEN && old_value != p_titlelen) {
4408       need_maketitle = true;
4409     }
4410   } else if (pp == &p_ch) {
4411     // if p_ch changed value, change the command line height
4412     // Only compute the new window layout when startup has been
4413     // completed. Otherwise the frame sizes may be wrong.
4414     if (p_ch != old_value && full_screen) {
4415       command_height();
4416     }
4417   } else if (pp == &p_uc) {
4418     // when 'updatecount' changes from zero to non-zero, open swap files
4419     if (p_uc && !old_value) {
4420       ml_open_files();
4421     }
4422   } else if (pp == &p_pb) {
4423     p_pb = MAX(MIN(p_pb, 100), 0);
4424     hl_invalidate_blends();
4425     pum_grid.blending = (p_pb > 0);
4426     if (pum_drawn()) {
4427       pum_redraw();
4428     }
4429   } else if (pp == &p_pyx) {
4430     if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
4431       errmsg = e_invarg;
4432     }
4433   } else if (pp == &p_ul || pp == &curbuf->b_p_ul) {
4434     // sync undo before 'undolevels' changes
4435     // use the old value, otherwise u_sync() may not work properly
4436     *pp = old_value;
4437     u_sync(true);
4438     *pp = value;
4439   } else if (pp == &curbuf->b_p_tw) {
4440     FOR_ALL_TAB_WINDOWS(tp, wp) {
4441       check_colorcolumn(wp);
4442     }
4443   } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
4444     if (curbuf->terminal) {
4445       // Force the scrollback to take effect.
4446       terminal_check_size(curbuf->terminal);
4447     }
4448   } else if (pp == &curwin->w_p_nuw) {
4449     curwin->w_nrwidth_line_count = 0;
4450   } else if (pp == &curwin->w_p_winbl && value != old_value) {
4451     // 'floatblend'
4452     curwin->w_p_winbl = MAX(MIN(curwin->w_p_winbl, 100), 0);
4453     curwin->w_hl_needs_update = true;
4454     check_blending(curwin);
4455   }
4456 
4457 
4458   // Check the (new) bounds for Rows and Columns here.
4459   if (p_lines < min_rows() && full_screen) {
4460     if (errbuf != NULL) {
4461       vim_snprintf((char *)errbuf, errbuflen,
4462                    _("E593: Need at least %d lines"), min_rows());
4463       errmsg = errbuf;
4464     }
4465     p_lines = min_rows();
4466   }
4467   if (p_columns < MIN_COLUMNS && full_screen) {
4468     if (errbuf != NULL) {
4469       vim_snprintf((char *)errbuf, errbuflen,
4470                    _("E594: Need at least %d columns"), MIN_COLUMNS);
4471       errmsg = errbuf;
4472     }
4473     p_columns = MIN_COLUMNS;
4474   }
4475 
4476   // True max size is defined by check_shellsize()
4477   p_lines = MIN(p_lines, INT_MAX);
4478   p_columns = MIN(p_columns, INT_MAX);
4479 
4480   // If the screen (shell) height has been changed, assume it is the
4481   // physical screenheight.
4482   if (p_lines != Rows || p_columns != Columns) {
4483     // Changing the screen size is not allowed while updating the screen.
4484     if (updating_screen) {
4485       *pp = old_value;
4486     } else if (full_screen) {
4487       screen_resize((int)p_columns, (int)p_lines);
4488     } else {
4489       // TODO(bfredl): is this branch ever needed?
4490       // Postpone the resizing; check the size and cmdline position for
4491       // messages.
4492       Rows = (int)p_lines;
4493       Columns = (int)p_columns;
4494       check_shellsize();
4495       if (cmdline_row > Rows - p_ch && Rows > p_ch) {
4496         assert(p_ch >= 0 && Rows - p_ch <= INT_MAX);
4497         cmdline_row = (int)(Rows - p_ch);
4498       }
4499     }
4500     if (p_window >= Rows || !option_was_set("window")) {
4501       p_window = Rows - 1;
4502     }
4503   }
4504 
4505   if ((curwin->w_p_scr <= 0
4506        || (curwin->w_p_scr > curwin->w_height
4507            && curwin->w_height > 0))
4508       && full_screen) {
4509     if (pp == &(curwin->w_p_scr)) {
4510       if (curwin->w_p_scr != 0) {
4511         errmsg = e_scroll;
4512       }
4513       win_comp_scroll(curwin);
4514     } else if (curwin->w_p_scr <= 0) {
4515       // If 'scroll' became invalid because of a side effect silently adjust it.
4516       curwin->w_p_scr = 1;
4517     } else {  // curwin->w_p_scr > curwin->w_height
4518       curwin->w_p_scr = curwin->w_height;
4519     }
4520   }
4521   if ((p_sj < -100 || p_sj >= Rows) && full_screen) {
4522     if (Rows != old_Rows) {     // Rows changed, just adjust p_sj
4523       p_sj = Rows / 2;
4524     } else {
4525       errmsg = e_scroll;
4526       p_sj = 1;
4527     }
4528   }
4529 
4530   // May set global value for local option.
4531   if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
4532     *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp;
4533   }
4534 
4535   options[opt_idx].flags |= P_WAS_SET;
4536 
4537   // Don't do this while starting up, failure or recursively.
4538   if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
4539     char buf_old[NUMBUFLEN];
4540     char buf_new[NUMBUFLEN];
4541     char buf_type[7];
4542 
4543     vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
4544     vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
4545     vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
4546                  (opt_flags & OPT_LOCAL) ? "local" : "global");
4547     set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
4548     set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
4549     set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
4550     apply_autocmds(EVENT_OPTIONSET,
4551                    (char_u *)options[opt_idx].fullname,
4552                    NULL, false, NULL);
4553     reset_v_option_vars();
4554   }
4555 
4556   if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
4557     ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
4558                        INTEGER_OBJ(value));
4559   }
4560 
4561   comp_col();                       // in case 'columns' or 'ls' changed
4562   if (curwin->w_curswant != MAXCOL
4563       && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
4564     curwin->w_set_curswant = true;
4565   }
4566   check_redraw(options[opt_idx].flags);
4567 
4568   return (char *)errmsg;
4569 }
4570 
trigger_optionsset_string(int opt_idx,int opt_flags,char * oldval,char * newval)4571 static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval)
4572 {
4573   // Don't do this recursively.
4574   if (oldval != NULL
4575       && newval != NULL
4576       && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
4577     char buf_type[7];
4578 
4579     vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
4580                  (opt_flags & OPT_LOCAL) ? "local" : "global");
4581     set_vim_var_string(VV_OPTION_OLD, oldval, -1);
4582     set_vim_var_string(VV_OPTION_NEW, newval, -1);
4583     set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
4584     apply_autocmds(EVENT_OPTIONSET,
4585                    (char_u *)options[opt_idx].fullname, NULL, false, NULL);
4586     reset_v_option_vars();
4587   }
4588 }
4589 
4590 /// Called after an option changed: check if something needs to be redrawn.
check_redraw(uint32_t flags)4591 static void check_redraw(uint32_t flags)
4592 {
4593   // Careful: P_RCLR and P_RALL are a combination of other P_ flags
4594   bool doclear = (flags & P_RCLR) == P_RCLR;
4595   bool all = ((flags & P_RALL) == P_RALL || doclear);
4596 
4597   if ((flags & P_RSTAT) || all) {  // mark all status lines dirty
4598     status_redraw_all();
4599   }
4600 
4601   if ((flags & P_RBUF) || (flags & P_RWIN) || all) {
4602     changed_window_setting();
4603   }
4604   if (flags & P_RBUF) {
4605     redraw_curbuf_later(NOT_VALID);
4606   }
4607   if (flags & P_RWINONLY) {
4608     redraw_later(curwin, NOT_VALID);
4609   }
4610   if (doclear) {
4611     redraw_all_later(CLEAR);
4612   } else if (all) {
4613     redraw_all_later(NOT_VALID);
4614   }
4615 }
4616 
4617 /// Find index for named option
4618 ///
4619 /// @param[in]  arg  Option to find index for.
4620 /// @param[in]  len  Length of the option.
4621 ///
4622 /// @return Index of the option or -1 if option was not found.
findoption_len(const char * const arg,const size_t len)4623 int findoption_len(const char *const arg, const size_t len)
4624 {
4625   const char *s;
4626   const char *p;
4627   static int quick_tab[27] = { 0, 0 };  // quick access table
4628 
4629   // For first call: Initialize the quick-access table.
4630   // It contains the index for the first option that starts with a certain
4631   // letter.  There are 26 letters, plus the first "t_" option.
4632   if (quick_tab[1] == 0) {
4633     p = options[0].fullname;
4634     for (short int i = 1; (s = options[i].fullname) != NULL; i++) {
4635       if (s[0] != p[0]) {
4636         if (s[0] == 't' && s[1] == '_') {
4637           quick_tab[26] = i;
4638         } else {
4639           quick_tab[CharOrdLow(s[0])] = i;
4640         }
4641       }
4642       p = s;
4643     }
4644   }
4645 
4646   // Check for name starting with an illegal character.
4647   if (len == 0 || arg[0] < 'a' || arg[0] > 'z') {
4648     return -1;
4649   }
4650 
4651   int opt_idx;
4652   const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_');
4653   if (is_term_opt) {
4654     opt_idx = quick_tab[26];
4655   } else {
4656     opt_idx = quick_tab[CharOrdLow(arg[0])];
4657   }
4658   // Match full name
4659   for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) {
4660     if (strncmp(arg, s, len) == 0 && s[len] == NUL) {
4661       break;
4662     }
4663   }
4664   if (s == NULL && !is_term_opt) {
4665     opt_idx = quick_tab[CharOrdLow(arg[0])];
4666     // Match short name
4667     for (; options[opt_idx].fullname != NULL; opt_idx++) {
4668       s = options[opt_idx].shortname;
4669       if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) {
4670         break;
4671       }
4672       s = NULL;
4673     }
4674   }
4675   if (s == NULL) {
4676     opt_idx = -1;
4677   } else {
4678     // Nvim: handle option aliases.
4679     if (STRNCMP(options[opt_idx].fullname, "viminfo", 7) == 0) {
4680       if (STRLEN(options[opt_idx].fullname) == 7) {
4681         return findoption_len("shada", 5);
4682       }
4683       assert(STRCMP(options[opt_idx].fullname, "viminfofile") == 0);
4684       return findoption_len("shadafile", 9);
4685     }
4686   }
4687   return opt_idx;
4688 }
4689 
is_tty_option(const char * name)4690 bool is_tty_option(const char *name)
4691   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
4692 {
4693   return (name[0] == 't' && name[1] == '_')
4694          || strequal(name, "term")
4695          || strequal(name, "ttytype");
4696 }
4697 
4698 #define TCO_BUFFER_SIZE 8
4699 /// @param name TUI-related option
4700 /// @param[out,allocated] value option string value
get_tty_option(const char * name,char ** value)4701 bool get_tty_option(const char *name, char **value)
4702 {
4703   if (strequal(name, "t_Co")) {
4704     if (value) {
4705       if (t_colors <= 1) {
4706         *value = xstrdup("");
4707       } else {
4708         *value = xmalloc(TCO_BUFFER_SIZE);
4709         snprintf(*value, TCO_BUFFER_SIZE, "%d", t_colors);
4710       }
4711     }
4712     return true;
4713   }
4714 
4715   if (strequal(name, "term")) {
4716     if (value) {
4717       *value = p_term ? xstrdup(p_term) : xstrdup("nvim");
4718     }
4719     return true;
4720   }
4721 
4722   if (strequal(name, "ttytype")) {
4723     if (value) {
4724       *value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim");
4725     }
4726     return true;
4727   }
4728 
4729   if (is_tty_option(name)) {
4730     if (value) {
4731       // XXX: All other t_* options were removed in 3baba1e7.
4732       *value = xstrdup("");
4733     }
4734     return true;
4735   }
4736 
4737   return false;
4738 }
4739 
set_tty_option(const char * name,char * value)4740 bool set_tty_option(const char *name, char *value)
4741 {
4742   if (strequal(name, "term")) {
4743     if (p_term) {
4744       xfree(p_term);
4745     }
4746     p_term = value;
4747     return true;
4748   }
4749 
4750   if (strequal(name, "ttytype")) {
4751     if (p_ttytype) {
4752       xfree(p_ttytype);
4753     }
4754     p_ttytype = value;
4755     return true;
4756   }
4757 
4758   return false;
4759 }
4760 
4761 /// Find index for an option
4762 ///
4763 /// @param[in]  arg  Option name.
4764 ///
4765 /// @return Option index or -1 if option was not found.
findoption(const char * const arg)4766 static int findoption(const char *const arg)
4767   FUNC_ATTR_NONNULL_ALL
4768 {
4769   return findoption_len(arg, strlen(arg));
4770 }
4771 
4772 /// Gets the value for an option.
4773 ///
4774 /// @param stringval  NULL when only checking existence
4775 ///
4776 /// @returns:
4777 /// Number or Toggle option: 1, *numval gets value.
4778 ///           String option: 0, *stringval gets allocated string.
4779 /// Hidden Number or Toggle option: -1.
4780 ///           hidden String option: -2.
4781 ///                 unknown option: -3.
get_option_value(const char * name,long * numval,char_u ** stringval,int opt_flags)4782 int get_option_value(const char *name, long *numval, char_u **stringval, int opt_flags)
4783 {
4784   if (get_tty_option(name, (char **)stringval)) {
4785     return 0;
4786   }
4787 
4788   int opt_idx = findoption(name);
4789   if (opt_idx < 0) {  // Unknown option.
4790     return -3;
4791   }
4792 
4793   char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
4794 
4795   if (options[opt_idx].flags & P_STRING) {
4796     if (varp == NULL) {  // hidden option
4797       return -2;
4798     }
4799     if (stringval != NULL) {
4800       *stringval = vim_strsave(*(char_u **)(varp));
4801     }
4802     return 0;
4803   }
4804 
4805   if (varp == NULL) {  // hidden option
4806     return -1;
4807   }
4808   if (options[opt_idx].flags & P_NUM) {
4809     *numval = *(long *)varp;
4810   } else {
4811     // Special case: 'modified' is b_changed, but we also want to consider
4812     // it set when 'ff' or 'fenc' changed.
4813     if ((int *)varp == &curbuf->b_changed) {
4814       *numval = curbufIsChanged();
4815     } else {
4816       *numval = (long)*(int *)varp;  // NOLINT(whitespace/cast)
4817     }
4818   }
4819   return 1;
4820 }
4821 
4822 // Returns the option attributes and its value. Unlike the above function it
4823 // will return either global value or local value of the option depending on
4824 // what was requested, but it will never return global value if it was
4825 // requested to return local one and vice versa. Neither it will return
4826 // buffer-local value if it was requested to return window-local one.
4827 //
4828 // Pretends that option is absent if it is not present in the requested scope
4829 // (i.e. has no global, window-local or buffer-local value depending on
4830 // opt_type).
4831 //
4832 // Returned flags:
4833 //       0 hidden or unknown option, also option that does not have requested
4834 //         type (see SREQ_* in option_defs.h)
4835 //  see SOPT_* in option_defs.h for other flags
4836 //
4837 // Possible opt_type values: see SREQ_* in option_defs.h
get_option_value_strict(char * name,int64_t * numval,char ** stringval,int opt_type,void * from)4838 int get_option_value_strict(char *name, int64_t *numval, char **stringval, int opt_type, void *from)
4839 {
4840   if (get_tty_option(name, stringval)) {
4841     return SOPT_STRING | SOPT_GLOBAL;
4842   }
4843 
4844   char_u *varp = NULL;
4845   int rv = 0;
4846   int opt_idx = findoption(name);
4847   if (opt_idx < 0) {
4848     return 0;
4849   }
4850 
4851   vimoption_T *p = &options[opt_idx];
4852 
4853   // Hidden option
4854   if (p->var == NULL) {
4855     return 0;
4856   }
4857 
4858   if (p->flags & P_BOOL) {
4859     rv |= SOPT_BOOL;
4860   } else if (p->flags & P_NUM) {
4861     rv |= SOPT_NUM;
4862   } else if (p->flags & P_STRING) {
4863     rv |= SOPT_STRING;
4864   }
4865 
4866   if (p->indir == PV_NONE) {
4867     if (opt_type == SREQ_GLOBAL) {
4868       rv |= SOPT_GLOBAL;
4869     } else {
4870       return 0;  // Did not request global-only option
4871     }
4872   } else {
4873     if (p->indir & PV_BOTH) {
4874       rv |= SOPT_GLOBAL;
4875     }
4876 
4877     if (p->indir & PV_WIN) {
4878       if (opt_type == SREQ_BUF) {
4879         return 0;  // Requested buffer-local, not window-local option
4880       } else {
4881         rv |= SOPT_WIN;
4882       }
4883     } else if (p->indir & PV_BUF) {
4884       if (opt_type == SREQ_WIN) {
4885         return 0;  // Requested window-local, not buffer-local option
4886       } else {
4887         rv |= SOPT_BUF;
4888       }
4889     }
4890   }
4891 
4892   if (stringval == NULL) {
4893     return rv;
4894   }
4895 
4896   if (opt_type == SREQ_GLOBAL) {
4897     if (p->var == VAR_WIN) {
4898       return 0;
4899     } else {
4900       varp = p->var;
4901     }
4902   } else {
4903     if (opt_type == SREQ_BUF) {
4904       // Special case: 'modified' is b_changed, but we also want to
4905       // consider it set when 'ff' or 'fenc' changed.
4906       if (p->indir == PV_MOD) {
4907         *numval = bufIsChanged((buf_T *)from);
4908         varp = NULL;
4909       } else {
4910         buf_T *save_curbuf = curbuf;
4911 
4912         // only getting a pointer, no need to use aucmd_prepbuf()
4913         curbuf = (buf_T *)from;
4914         curwin->w_buffer = curbuf;
4915         varp = get_varp(p);
4916         curbuf = save_curbuf;
4917         curwin->w_buffer = curbuf;
4918       }
4919     } else if (opt_type == SREQ_WIN) {
4920       win_T *save_curwin = curwin;
4921       curwin = (win_T *)from;
4922       curbuf = curwin->w_buffer;
4923       varp = get_varp(p);
4924       curwin = save_curwin;
4925       curbuf = curwin->w_buffer;
4926     }
4927 
4928     if (varp == p->var) {
4929       return (rv | SOPT_UNSET);
4930     }
4931   }
4932 
4933   if (varp != NULL) {
4934     if (p->flags & P_STRING) {
4935       *stringval = xstrdup(*(char **)(varp));
4936     } else if (p->flags & P_NUM) {
4937       *numval = *(long *)varp;
4938     } else {
4939       *numval = *(int *)varp;
4940     }
4941   }
4942 
4943   return rv;
4944 }
4945 
4946 /// Set the value of an option
4947 ///
4948 /// @param[in]  name  Option name.
4949 /// @param[in]  number  New value for the number or boolean option.
4950 /// @param[in]  string  New value for string option.
4951 /// @param[in]  opt_flags  Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
4952 ///
4953 /// @return NULL on success, error message on error.
set_option_value(const char * const name,const long number,const char * const string,const int opt_flags)4954 char *set_option_value(const char *const name, const long number, const char *const string,
4955                        const int opt_flags)
4956   FUNC_ATTR_NONNULL_ARG(1)
4957 {
4958   if (is_tty_option(name)) {
4959     return NULL;  // Fail silently; many old vimrcs set t_xx options.
4960   }
4961 
4962   int opt_idx;
4963   char_u *varp;
4964 
4965   opt_idx = findoption(name);
4966   if (opt_idx < 0) {
4967     semsg(_("E355: Unknown option: %s"), name);
4968   } else {
4969     uint32_t flags = options[opt_idx].flags;
4970     // Disallow changing some options in the sandbox
4971     if (sandbox > 0 && (flags & P_SECURE)) {
4972       emsg(_(e_sandbox));
4973       return NULL;
4974     }
4975     if (flags & P_STRING) {
4976       const char *s = string;
4977       if (s == NULL) {
4978         s = "";
4979       }
4980       return set_string_option(opt_idx, s, opt_flags);
4981     } else {
4982       varp = get_varp_scope(&(options[opt_idx]), opt_flags);
4983       if (varp != NULL) {       // hidden option is not changed
4984         if (number == 0 && string != NULL) {
4985           int idx;
4986 
4987           // Either we are given a string or we are setting option
4988           // to zero.
4989           for (idx = 0; string[idx] == '0'; idx++) {}
4990           if (string[idx] != NUL || idx == 0) {
4991             // There's another character after zeros or the string
4992             // is empty.  In both cases, we are trying to set a
4993             // num option using a string.
4994             semsg(_("E521: Number required: &%s = '%s'"),
4995                   name, string);
4996             return NULL;  // do nothing as we hit an error
4997           }
4998         }
4999         if (flags & P_NUM) {
5000           return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags);
5001         } else {
5002           return set_bool_option(opt_idx, varp, (int)number, opt_flags);
5003         }
5004       }
5005     }
5006   }
5007   return NULL;
5008 }
5009 
5010 // Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
5011 // When "has_lt" is true there is a '<' before "*arg_arg".
5012 // Returns 0 when the key is not recognized.
find_key_option_len(const char_u * arg_arg,size_t len,bool has_lt)5013 int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
5014 {
5015   int key = 0;
5016   int modifiers;
5017   const char_u *arg = arg_arg;
5018 
5019   // Don't use get_special_key_code() for t_xx, we don't want it to call
5020   // add_termcap_entry().
5021   if (len >= 4 && arg[0] == 't' && arg[1] == '_') {
5022     key = TERMCAP2KEY(arg[2], arg[3]);
5023   } else if (has_lt) {
5024     arg--;  // put arg at the '<'
5025     modifiers = 0;
5026     key = find_special_key(&arg, len + 1, &modifiers, true, true, false);
5027     if (modifiers) {  // can't handle modifiers here
5028       key = 0;
5029     }
5030   }
5031   return key;
5032 }
5033 
find_key_option(const char_u * arg,bool has_lt)5034 static int find_key_option(const char_u *arg, bool has_lt)
5035 {
5036   return find_key_option_len(arg, STRLEN(arg), has_lt);
5037 }
5038 
5039 /// if 'all' == 0: show changed options
5040 /// if 'all' == 1: show all normal options
5041 ///
5042 /// @param opt_flags  OPT_LOCAL and/or OPT_GLOBAL
showoptions(int all,int opt_flags)5043 static void showoptions(int all, int opt_flags)
5044 {
5045   vimoption_T *p;
5046   int col;
5047   char_u *varp;
5048   int item_count;
5049   int run;
5050   int row, rows;
5051   int cols;
5052   int i;
5053   int len;
5054 
5055 #define INC 20
5056 #define GAP 3
5057 
5058   vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT);
5059 
5060   // Highlight title
5061   if (opt_flags & OPT_GLOBAL) {
5062     msg_puts_title(_("\n--- Global option values ---"));
5063   } else if (opt_flags & OPT_LOCAL) {
5064     msg_puts_title(_("\n--- Local option values ---"));
5065   } else {
5066     msg_puts_title(_("\n--- Options ---"));
5067   }
5068 
5069   // Do the loop two times:
5070   // 1. display the short items
5071   // 2. display the long items (only strings and numbers)
5072   for (run = 1; run <= 2 && !got_int; run++) {
5073     // collect the items in items[]
5074     item_count = 0;
5075     for (p = &options[0]; p->fullname != NULL; p++) {
5076       // apply :filter /pat/
5077       if (message_filtered((char_u *)p->fullname)) {
5078         continue;
5079       }
5080 
5081       varp = NULL;
5082       if (opt_flags != 0) {
5083         if (p->indir != PV_NONE) {
5084           varp = get_varp_scope(p, opt_flags);
5085         }
5086       } else {
5087         varp = get_varp(p);
5088       }
5089       if (varp != NULL
5090           && (all == 1 || (all == 0 && !optval_default(p, varp)))) {
5091         if (p->flags & P_BOOL) {
5092           len = 1;                      //  a toggle option fits always
5093         } else {
5094           option_value2string(p, opt_flags);
5095           len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
5096         }
5097         if ((len <= INC - GAP && run == 1)
5098             || (len > INC - GAP && run == 2)) {
5099           items[item_count++] = p;
5100         }
5101       }
5102     }
5103 
5104     /*
5105      * display the items
5106      */
5107     if (run == 1) {
5108       assert(Columns <= INT_MAX - GAP
5109              && Columns + GAP >= INT_MIN + 3
5110              && (Columns + GAP - 3) / INC >= INT_MIN
5111              && (Columns + GAP - 3) / INC <= INT_MAX);
5112       cols = (int)((Columns + GAP - 3) / INC);
5113       if (cols == 0) {
5114         cols = 1;
5115       }
5116       rows = (item_count + cols - 1) / cols;
5117     } else {    // run == 2
5118       rows = item_count;
5119     }
5120     for (row = 0; row < rows && !got_int; row++) {
5121       msg_putchar('\n');                        // go to next line
5122       if (got_int) {                            // 'q' typed in more
5123         break;
5124       }
5125       col = 0;
5126       for (i = row; i < item_count; i += rows) {
5127         msg_col = col;                          // make columns
5128         showoneopt(items[i], opt_flags);
5129         col += INC;
5130       }
5131       ui_flush();
5132       os_breakcheck();
5133     }
5134   }
5135   xfree(items);
5136 }
5137 
5138 /// Return true if option "p" has its default value.
optval_default(vimoption_T * p,char_u * varp)5139 static int optval_default(vimoption_T *p, char_u *varp)
5140 {
5141   if (varp == NULL) {
5142     return true;            // hidden option is always at default
5143   }
5144   if (p->flags & P_NUM) {
5145     return *(long *)varp == (long)(intptr_t)p->def_val;
5146   }
5147   if (p->flags & P_BOOL) {
5148     return *(int *)varp == (int)(intptr_t)p->def_val;
5149   }
5150   // P_STRING
5151   return STRCMP(*(char_u **)varp, p->def_val) == 0;
5152 }
5153 
5154 /// Send update to UIs with values of UI relevant options
ui_refresh_options(void)5155 void ui_refresh_options(void)
5156 {
5157   for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
5158     uint32_t flags = options[opt_idx].flags;
5159     if (!(flags & P_UI_OPTION)) {
5160       continue;
5161     }
5162     String name = cstr_as_string(options[opt_idx].fullname);
5163     void *varp = options[opt_idx].var;
5164     Object value = OBJECT_INIT;
5165     if (flags & P_BOOL) {
5166       value = BOOLEAN_OBJ(*(int *)varp);
5167     } else if (flags & P_NUM) {
5168       value = INTEGER_OBJ(*(long *)varp);
5169     } else if (flags & P_STRING) {
5170       // cstr_as_string handles NULL string
5171       value = STRING_OBJ(cstr_as_string(*(char **)varp));
5172     }
5173     ui_call_option_set(name, value);
5174   }
5175   if (p_mouse != NULL) {
5176     setmouse();
5177   }
5178 }
5179 
5180 /// showoneopt: show the value of one option
5181 /// must not be called with a hidden option!
5182 ///
5183 /// @param opt_flags  OPT_LOCAL or OPT_GLOBAL
showoneopt(vimoption_T * p,int opt_flags)5184 static void showoneopt(vimoption_T *p, int opt_flags)
5185 {
5186   char_u *varp;
5187   int save_silent = silent_mode;
5188 
5189   silent_mode = false;
5190   info_message = true;          // use mch_msg(), not mch_errmsg()
5191 
5192   varp = get_varp_scope(p, opt_flags);
5193 
5194   // for 'modified' we also need to check if 'ff' or 'fenc' changed.
5195   if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed
5196                               ? !curbufIsChanged() : !*(int *)varp)) {
5197     msg_puts("no");
5198   } else if ((p->flags & P_BOOL) && *(int *)varp < 0) {
5199     msg_puts("--");
5200   } else {
5201     msg_puts("  ");
5202   }
5203   msg_puts(p->fullname);
5204   if (!(p->flags & P_BOOL)) {
5205     msg_putchar('=');
5206     // put value string in NameBuff
5207     option_value2string(p, opt_flags);
5208     msg_outtrans(NameBuff);
5209   }
5210 
5211   silent_mode = save_silent;
5212   info_message = false;
5213 }
5214 
5215 /// Write modified options as ":set" commands to a file.
5216 ///
5217 /// There are three values for "opt_flags":
5218 /// OPT_GLOBAL:         Write global option values and fresh values of
5219 ///             buffer-local options (used for start of a session
5220 ///             file).
5221 /// OPT_GLOBAL + OPT_LOCAL: Idem, add fresh values of window-local options for
5222 ///             curwin (used for a vimrc file).
5223 /// OPT_LOCAL:          Write buffer-local option values for curbuf, fresh
5224 ///             and local values for window-local options of
5225 ///             curwin.  Local values are also written when at the
5226 ///             default value, because a modeline or autocommand
5227 ///             may have set them when doing ":edit file" and the
5228 ///             user has set them back at the default or fresh
5229 ///             value.
5230 ///             When "local_only" is true, don't write fresh
5231 ///             values, only local values (for ":mkview").
5232 /// (fresh value = value used for a new buffer or window for a local option).
5233 ///
5234 /// Return FAIL on error, OK otherwise.
makeset(FILE * fd,int opt_flags,int local_only)5235 int makeset(FILE *fd, int opt_flags, int local_only)
5236 {
5237   vimoption_T *p;
5238   char_u *varp;                    // currently used value
5239   char_u *varp_fresh;              // local value
5240   char_u *varp_local = NULL;       // fresh value
5241   char *cmd;
5242   int round;
5243   int pri;
5244 
5245   /*
5246    * Some options are never written:
5247    * - Options that don't have a default (terminal name, columns, lines).
5248    * - Terminal options.
5249    * - Hidden options.
5250    *
5251    * Do the loop over "options[]" twice: once for options with the
5252    * P_PRI_MKRC flag and once without.
5253    */
5254   for (pri = 1; pri >= 0; pri--) {
5255     for (p = &options[0]; p->fullname; p++) {
5256       if (!(p->flags & P_NO_MKRC)
5257           && ((pri == 1) == ((p->flags & P_PRI_MKRC) != 0))) {
5258         // skip global option when only doing locals
5259         if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) {
5260           continue;
5261         }
5262 
5263         // Do not store options like 'bufhidden' and 'syntax' in a vimrc
5264         // file, they are always buffer-specific.
5265         if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB)) {
5266           continue;
5267         }
5268 
5269         varp = get_varp_scope(p, opt_flags);
5270         // Hidden options are never written.
5271         if (!varp) {
5272           continue;
5273         }
5274         // Global values are only written when not at the default value.
5275         if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) {
5276           continue;
5277         }
5278 
5279         if ((opt_flags & OPT_SKIPRTP)
5280             && (p->var == (char_u *)&p_rtp || p->var == (char_u *)&p_pp)) {
5281           continue;
5282         }
5283 
5284         round = 2;
5285         if (p->indir != PV_NONE) {
5286           if (p->var == VAR_WIN) {
5287             // skip window-local option when only doing globals
5288             if (!(opt_flags & OPT_LOCAL)) {
5289               continue;
5290             }
5291             // When fresh value of window-local option is not at the
5292             // default, need to write it too.
5293             if (!(opt_flags & OPT_GLOBAL) && !local_only) {
5294               varp_fresh = get_varp_scope(p, OPT_GLOBAL);
5295               if (!optval_default(p, varp_fresh)) {
5296                 round = 1;
5297                 varp_local = varp;
5298                 varp = varp_fresh;
5299               }
5300             }
5301           }
5302         }
5303 
5304         // Round 1: fresh value for window-local options.
5305         // Round 2: other values
5306         for (; round <= 2; varp = varp_local, round++) {
5307           if (round == 1 || (opt_flags & OPT_GLOBAL)) {
5308             cmd = "set";
5309           } else {
5310             cmd = "setlocal";
5311           }
5312 
5313           if (p->flags & P_BOOL) {
5314             if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL) {
5315               return FAIL;
5316             }
5317           } else if (p->flags & P_NUM) {
5318             if (put_setnum(fd, cmd, p->fullname, (long *)varp) == FAIL) {
5319               return FAIL;
5320             }
5321           } else {    // P_STRING
5322             int do_endif = false;
5323 
5324             // Don't set 'syntax' and 'filetype' again if the value is
5325             // already right, avoids reloading the syntax file.
5326             if (p->indir == PV_SYN || p->indir == PV_FT) {
5327               if (fprintf(fd, "if &%s != '%s'", p->fullname,
5328                           *(char_u **)(varp)) < 0
5329                   || put_eol(fd) < 0) {
5330                 return FAIL;
5331               }
5332               do_endif = true;
5333             }
5334             if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
5335                               p->flags) == FAIL) {
5336               return FAIL;
5337             }
5338             if (do_endif) {
5339               if (put_line(fd, "endif") == FAIL) {
5340                 return FAIL;
5341               }
5342             }
5343           }
5344         }
5345       }
5346     }
5347   }
5348   return OK;
5349 }
5350 
5351 /// Generate set commands for the local fold options only.  Used when
5352 /// 'sessionoptions' or 'viewoptions' contains "folds" but not "options".
makefoldset(FILE * fd)5353 int makefoldset(FILE *fd)
5354 {
5355   if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
5356       || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
5357       == FAIL
5358       || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
5359       == FAIL
5360       || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
5361       == FAIL
5362       || put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
5363       || put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
5364       || put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL
5365       || put_setbool(fd, "setlocal", "fen",
5366                      curwin->w_p_fen) == FAIL) {
5367     return FAIL;
5368   }
5369 
5370   return OK;
5371 }
5372 
put_setstring(FILE * fd,char * cmd,char * name,char_u ** valuep,uint64_t flags)5373 static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint64_t flags)
5374 {
5375   char_u *s;
5376   char_u *buf = NULL;
5377   char_u *part = NULL;
5378   char_u *p;
5379 
5380   if (fprintf(fd, "%s %s=", cmd, name) < 0) {
5381     return FAIL;
5382   }
5383   if (*valuep != NULL) {
5384     // Output 'pastetoggle' as key names.  For other
5385     // options some characters have to be escaped with
5386     // CTRL-V or backslash
5387     if (valuep == &p_pt) {
5388       s = *valuep;
5389       while (*s != NUL) {
5390         if (put_escstr(fd, (char_u *)str2special((const char **)&s, false,
5391                                                  false), 2)
5392             == FAIL) {
5393           return FAIL;
5394         }
5395       }
5396     } else if ((flags & P_EXPAND) != 0) {
5397       size_t size = (size_t)STRLEN(*valuep) + 1;
5398 
5399       // replace home directory in the whole option value into "buf"
5400       buf = xmalloc(size);
5401       home_replace(NULL, *valuep, buf, size, false);
5402 
5403       // If the option value is longer than MAXPATHL, we need to append
5404       // each comma separated part of the option separately, so that it
5405       // can be expanded when read back.
5406       if (size >= MAXPATHL && (flags & P_COMMA) != 0
5407           && vim_strchr(*valuep, ',') != NULL) {
5408         part = xmalloc(size);
5409 
5410         // write line break to clear the option, e.g. ':set rtp='
5411         if (put_eol(fd) == FAIL) {
5412           goto fail;
5413         }
5414         p = buf;
5415         while (*p != NUL) {
5416           // for each comma separated option part, append value to
5417           // the option, :set rtp+=value
5418           if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
5419             goto fail;
5420           }
5421           (void)copy_option_part(&p, part, size, ",");
5422           if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
5423             goto fail;
5424           }
5425         }
5426         xfree(buf);
5427         xfree(part);
5428         return OK;
5429       }
5430       if (put_escstr(fd, buf, 2) == FAIL) {
5431         xfree(buf);
5432         return FAIL;
5433       }
5434       xfree(buf);
5435     } else if (put_escstr(fd, *valuep, 2) == FAIL) {
5436       return FAIL;
5437     }
5438   }
5439   if (put_eol(fd) < 0) {
5440     return FAIL;
5441   }
5442   return OK;
5443 fail:
5444   xfree(buf);
5445   xfree(part);
5446   return FAIL;
5447 }
5448 
put_setnum(FILE * fd,char * cmd,char * name,long * valuep)5449 static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep)
5450 {
5451   long wc;
5452 
5453   if (fprintf(fd, "%s %s=", cmd, name) < 0) {
5454     return FAIL;
5455   }
5456   if (wc_use_keyname((char_u *)valuep, &wc)) {
5457     // print 'wildchar' and 'wildcharm' as a key name
5458     if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0) {
5459       return FAIL;
5460     }
5461   } else if (fprintf(fd, "%" PRId64, (int64_t)(*valuep)) < 0) {
5462     return FAIL;
5463   }
5464   if (put_eol(fd) < 0) {
5465     return FAIL;
5466   }
5467   return OK;
5468 }
5469 
put_setbool(FILE * fd,char * cmd,char * name,int value)5470 static int put_setbool(FILE *fd, char *cmd, char *name, int value)
5471 {
5472   if (value < 0) {      // global/local option using global value
5473     return OK;
5474   }
5475   if (fprintf(fd, "%s %s%s", cmd, value ? "" : "no", name) < 0
5476       || put_eol(fd) < 0) {
5477     return FAIL;
5478   }
5479   return OK;
5480 }
5481 
5482 /// Compute columns for ruler and shown command. 'sc_col' is also used to
5483 /// decide what the maximum length of a message on the status line can be.
5484 /// If there is a status line for the last window, 'sc_col' is independent
5485 /// of 'ru_col'.
5486 
5487 #define COL_RULER 17        // columns needed by standard ruler
5488 
comp_col(void)5489 void comp_col(void)
5490 {
5491   int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW));
5492 
5493   sc_col = 0;
5494   ru_col = 0;
5495   if (p_ru) {
5496     ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
5497     // no last status line, adjust sc_col
5498     if (!last_has_status) {
5499       sc_col = ru_col;
5500     }
5501   }
5502   if (p_sc) {
5503     sc_col += SHOWCMD_COLS;
5504     if (!p_ru || last_has_status) {         // no need for separating space
5505       sc_col++;
5506     }
5507   }
5508   assert(sc_col >= 0
5509          && INT_MIN + sc_col <= Columns
5510          && Columns - sc_col <= INT_MAX);
5511   sc_col = (int)(Columns - sc_col);
5512   assert(ru_col >= 0
5513          && INT_MIN + ru_col <= Columns
5514          && Columns - ru_col <= INT_MAX);
5515   ru_col = (int)(Columns - ru_col);
5516   if (sc_col <= 0) {            // screen too narrow, will become a mess
5517     sc_col = 1;
5518   }
5519   if (ru_col <= 0) {
5520     ru_col = 1;
5521   }
5522   set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
5523 }
5524 
5525 // Unset local option value, similar to ":set opt<".
unset_global_local_option(char * name,void * from)5526 void unset_global_local_option(char *name, void *from)
5527 {
5528   vimoption_T *p;
5529   buf_T *buf = (buf_T *)from;
5530 
5531   int opt_idx = findoption(name);
5532   if (opt_idx < 0) {
5533     semsg(_("E355: Unknown option: %s"), name);
5534     return;
5535   }
5536   p = &(options[opt_idx]);
5537 
5538   switch ((int)p->indir)
5539   {
5540   // global option with local value: use local value if it's been set
5541   case PV_EP:
5542     clear_string_option(&buf->b_p_ep);
5543     break;
5544   case PV_KP:
5545     clear_string_option(&buf->b_p_kp);
5546     break;
5547   case PV_PATH:
5548     clear_string_option(&buf->b_p_path);
5549     break;
5550   case PV_AR:
5551     buf->b_p_ar = -1;
5552     break;
5553   case PV_BKC:
5554     clear_string_option(&buf->b_p_bkc);
5555     buf->b_bkc_flags = 0;
5556     break;
5557   case PV_TAGS:
5558     clear_string_option(&buf->b_p_tags);
5559     break;
5560   case PV_TC:
5561     clear_string_option(&buf->b_p_tc);
5562     buf->b_tc_flags = 0;
5563     break;
5564   case PV_SISO:
5565     curwin->w_p_siso = -1;
5566     break;
5567   case PV_SO:
5568     curwin->w_p_so = -1;
5569     break;
5570   case PV_DEF:
5571     clear_string_option(&buf->b_p_def);
5572     break;
5573   case PV_INC:
5574     clear_string_option(&buf->b_p_inc);
5575     break;
5576   case PV_DICT:
5577     clear_string_option(&buf->b_p_dict);
5578     break;
5579   case PV_TSR:
5580     clear_string_option(&buf->b_p_tsr);
5581     break;
5582   case PV_TSRFU:
5583     clear_string_option(&buf->b_p_tsrfu);
5584     break;
5585   case PV_FP:
5586     clear_string_option(&buf->b_p_fp);
5587     break;
5588   case PV_EFM:
5589     clear_string_option(&buf->b_p_efm);
5590     break;
5591   case PV_GP:
5592     clear_string_option(&buf->b_p_gp);
5593     break;
5594   case PV_MP:
5595     clear_string_option(&buf->b_p_mp);
5596     break;
5597   case PV_SBR:
5598     clear_string_option(&((win_T *)from)->w_p_sbr);
5599     break;
5600   case PV_STL:
5601     clear_string_option(&((win_T *)from)->w_p_stl);
5602     break;
5603   case PV_UL:
5604     buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
5605     break;
5606   case PV_LW:
5607     clear_string_option(&buf->b_p_lw);
5608     break;
5609   case PV_MENC:
5610     clear_string_option(&buf->b_p_menc);
5611     break;
5612   case PV_LCS:
5613     clear_string_option(&((win_T *)from)->w_p_lcs);
5614     set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true);
5615     redraw_later((win_T *)from, NOT_VALID);
5616     break;
5617   case PV_FCS:
5618     clear_string_option(&((win_T *)from)->w_p_fcs);
5619     set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
5620     redraw_later((win_T *)from, NOT_VALID);
5621     break;
5622   }
5623 }
5624 
5625 /// Get pointer to option variable, depending on local or global scope.
get_varp_scope(vimoption_T * p,int opt_flags)5626 static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
5627 {
5628   if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
5629     if (p->var == VAR_WIN) {
5630       return (char_u *)GLOBAL_WO(get_varp(p));
5631     }
5632     return p->var;
5633   }
5634   if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
5635     switch ((int)p->indir) {
5636     case PV_FP:
5637       return (char_u *)&(curbuf->b_p_fp);
5638     case PV_EFM:
5639       return (char_u *)&(curbuf->b_p_efm);
5640     case PV_GP:
5641       return (char_u *)&(curbuf->b_p_gp);
5642     case PV_MP:
5643       return (char_u *)&(curbuf->b_p_mp);
5644     case PV_EP:
5645       return (char_u *)&(curbuf->b_p_ep);
5646     case PV_KP:
5647       return (char_u *)&(curbuf->b_p_kp);
5648     case PV_PATH:
5649       return (char_u *)&(curbuf->b_p_path);
5650     case PV_AR:
5651       return (char_u *)&(curbuf->b_p_ar);
5652     case PV_TAGS:
5653       return (char_u *)&(curbuf->b_p_tags);
5654     case PV_TC:
5655       return (char_u *)&(curbuf->b_p_tc);
5656     case PV_SISO:
5657       return (char_u *)&(curwin->w_p_siso);
5658     case PV_SO:
5659       return (char_u *)&(curwin->w_p_so);
5660     case PV_DEF:
5661       return (char_u *)&(curbuf->b_p_def);
5662     case PV_INC:
5663       return (char_u *)&(curbuf->b_p_inc);
5664     case PV_DICT:
5665       return (char_u *)&(curbuf->b_p_dict);
5666     case PV_TSR:
5667       return (char_u *)&(curbuf->b_p_tsr);
5668     case PV_TSRFU:
5669       return (char_u *)&(curbuf->b_p_tsrfu);
5670     case PV_TFU:
5671       return (char_u *)&(curbuf->b_p_tfu);
5672     case PV_SBR:
5673       return (char_u *)&(curwin->w_p_sbr);
5674     case PV_STL:
5675       return (char_u *)&(curwin->w_p_stl);
5676     case PV_UL:
5677       return (char_u *)&(curbuf->b_p_ul);
5678     case PV_LW:
5679       return (char_u *)&(curbuf->b_p_lw);
5680     case PV_BKC:
5681       return (char_u *)&(curbuf->b_p_bkc);
5682     case PV_MENC:
5683       return (char_u *)&(curbuf->b_p_menc);
5684     case PV_FCS:
5685       return (char_u *)&(curwin->w_p_fcs);
5686     case PV_LCS:
5687       return (char_u *)&(curwin->w_p_lcs);
5688     }
5689     return NULL;     // "cannot happen"
5690   }
5691   return get_varp(p);
5692 }
5693 
5694 /// Get pointer to option variable.
get_varp(vimoption_T * p)5695 static char_u *get_varp(vimoption_T *p)
5696 {
5697   // hidden option, always return NULL
5698   if (p->var == NULL) {
5699     return NULL;
5700   }
5701 
5702   switch ((int)p->indir) {
5703   case PV_NONE:
5704     return p->var;
5705 
5706   // global option with local value: use local value if it's been set
5707   case PV_EP:
5708     return *curbuf->b_p_ep != NUL
5709            ? (char_u *)&curbuf->b_p_ep : p->var;
5710   case PV_KP:
5711     return *curbuf->b_p_kp != NUL
5712            ? (char_u *)&curbuf->b_p_kp : p->var;
5713   case PV_PATH:
5714     return *curbuf->b_p_path != NUL
5715            ? (char_u *)&(curbuf->b_p_path) : p->var;
5716   case PV_AR:
5717     return curbuf->b_p_ar >= 0
5718            ? (char_u *)&(curbuf->b_p_ar) : p->var;
5719   case PV_TAGS:
5720     return *curbuf->b_p_tags != NUL
5721            ? (char_u *)&(curbuf->b_p_tags) : p->var;
5722   case PV_TC:
5723     return *curbuf->b_p_tc != NUL
5724            ? (char_u *)&(curbuf->b_p_tc) : p->var;
5725   case PV_SISO:
5726     return curwin->w_p_siso >= 0
5727            ? (char_u *)&(curwin->w_p_siso) : p->var;
5728   case PV_SO:
5729     return curwin->w_p_so >= 0
5730            ? (char_u *)&(curwin->w_p_so) : p->var;
5731   case PV_BKC:
5732     return *curbuf->b_p_bkc != NUL
5733            ? (char_u *)&(curbuf->b_p_bkc) : p->var;
5734   case PV_DEF:
5735     return *curbuf->b_p_def != NUL
5736            ? (char_u *)&(curbuf->b_p_def) : p->var;
5737   case PV_INC:
5738     return *curbuf->b_p_inc != NUL
5739            ? (char_u *)&(curbuf->b_p_inc) : p->var;
5740   case PV_DICT:
5741     return *curbuf->b_p_dict != NUL
5742            ? (char_u *)&(curbuf->b_p_dict) : p->var;
5743   case PV_TSR:
5744     return *curbuf->b_p_tsr != NUL
5745            ? (char_u *)&(curbuf->b_p_tsr) : p->var;
5746   case PV_TSRFU:
5747     return *curbuf->b_p_tsrfu != NUL
5748            ? (char_u *)&(curbuf->b_p_tsrfu) : p->var;
5749   case PV_FP:
5750     return *curbuf->b_p_fp != NUL
5751            ? (char_u *)&(curbuf->b_p_fp) : p->var;
5752   case PV_EFM:
5753     return *curbuf->b_p_efm != NUL
5754            ? (char_u *)&(curbuf->b_p_efm) : p->var;
5755   case PV_GP:
5756     return *curbuf->b_p_gp != NUL
5757            ? (char_u *)&(curbuf->b_p_gp) : p->var;
5758   case PV_MP:
5759     return *curbuf->b_p_mp != NUL
5760            ? (char_u *)&(curbuf->b_p_mp) : p->var;
5761   case PV_SBR:
5762     return *curwin->w_p_sbr != NUL
5763            ? (char_u *)&(curwin->w_p_sbr) : p->var;
5764   case PV_STL:
5765     return *curwin->w_p_stl != NUL
5766            ? (char_u *)&(curwin->w_p_stl) : p->var;
5767   case PV_UL:
5768     return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL
5769            ? (char_u *)&(curbuf->b_p_ul) : p->var;
5770   case PV_LW:
5771     return *curbuf->b_p_lw != NUL
5772            ? (char_u *)&(curbuf->b_p_lw) : p->var;
5773   case PV_MENC:
5774     return *curbuf->b_p_menc != NUL
5775            ? (char_u *)&(curbuf->b_p_menc) : p->var;
5776   case PV_FCS:
5777     return *curwin->w_p_fcs != NUL
5778            ? (char_u *)&(curwin->w_p_fcs) : p->var;
5779   case PV_LCS:
5780     return *curwin->w_p_lcs != NUL
5781            ? (char_u *)&(curwin->w_p_lcs) : p->var;
5782 
5783   case PV_ARAB:
5784     return (char_u *)&(curwin->w_p_arab);
5785   case PV_LIST:
5786     return (char_u *)&(curwin->w_p_list);
5787   case PV_SPELL:
5788     return (char_u *)&(curwin->w_p_spell);
5789   case PV_CUC:
5790     return (char_u *)&(curwin->w_p_cuc);
5791   case PV_CUL:
5792     return (char_u *)&(curwin->w_p_cul);
5793   case PV_CULOPT:
5794     return (char_u *)&(curwin->w_p_culopt);
5795   case PV_CC:
5796     return (char_u *)&(curwin->w_p_cc);
5797   case PV_DIFF:
5798     return (char_u *)&(curwin->w_p_diff);
5799   case PV_FDC:
5800     return (char_u *)&(curwin->w_p_fdc);
5801   case PV_FEN:
5802     return (char_u *)&(curwin->w_p_fen);
5803   case PV_FDI:
5804     return (char_u *)&(curwin->w_p_fdi);
5805   case PV_FDL:
5806     return (char_u *)&(curwin->w_p_fdl);
5807   case PV_FDM:
5808     return (char_u *)&(curwin->w_p_fdm);
5809   case PV_FML:
5810     return (char_u *)&(curwin->w_p_fml);
5811   case PV_FDN:
5812     return (char_u *)&(curwin->w_p_fdn);
5813   case PV_FDE:
5814     return (char_u *)&(curwin->w_p_fde);
5815   case PV_FDT:
5816     return (char_u *)&(curwin->w_p_fdt);
5817   case PV_FMR:
5818     return (char_u *)&(curwin->w_p_fmr);
5819   case PV_NU:
5820     return (char_u *)&(curwin->w_p_nu);
5821   case PV_RNU:
5822     return (char_u *)&(curwin->w_p_rnu);
5823   case PV_NUW:
5824     return (char_u *)&(curwin->w_p_nuw);
5825   case PV_WFH:
5826     return (char_u *)&(curwin->w_p_wfh);
5827   case PV_WFW:
5828     return (char_u *)&(curwin->w_p_wfw);
5829   case PV_PVW:
5830     return (char_u *)&(curwin->w_p_pvw);
5831   case PV_RL:
5832     return (char_u *)&(curwin->w_p_rl);
5833   case PV_RLC:
5834     return (char_u *)&(curwin->w_p_rlc);
5835   case PV_SCROLL:
5836     return (char_u *)&(curwin->w_p_scr);
5837   case PV_WRAP:
5838     return (char_u *)&(curwin->w_p_wrap);
5839   case PV_LBR:
5840     return (char_u *)&(curwin->w_p_lbr);
5841   case PV_BRI:
5842     return (char_u *)&(curwin->w_p_bri);
5843   case PV_BRIOPT:
5844     return (char_u *)&(curwin->w_p_briopt);
5845   case PV_SCBIND:
5846     return (char_u *)&(curwin->w_p_scb);
5847   case PV_CRBIND:
5848     return (char_u *)&(curwin->w_p_crb);
5849   case PV_COCU:
5850     return (char_u *)&(curwin->w_p_cocu);
5851   case PV_COLE:
5852     return (char_u *)&(curwin->w_p_cole);
5853 
5854   case PV_AI:
5855     return (char_u *)&(curbuf->b_p_ai);
5856   case PV_BIN:
5857     return (char_u *)&(curbuf->b_p_bin);
5858   case PV_BOMB:
5859     return (char_u *)&(curbuf->b_p_bomb);
5860   case PV_BH:
5861     return (char_u *)&(curbuf->b_p_bh);
5862   case PV_BT:
5863     return (char_u *)&(curbuf->b_p_bt);
5864   case PV_BL:
5865     return (char_u *)&(curbuf->b_p_bl);
5866   case PV_CHANNEL:
5867     return (char_u *)&(curbuf->b_p_channel);
5868   case PV_CI:
5869     return (char_u *)&(curbuf->b_p_ci);
5870   case PV_CIN:
5871     return (char_u *)&(curbuf->b_p_cin);
5872   case PV_CINK:
5873     return (char_u *)&(curbuf->b_p_cink);
5874   case PV_CINO:
5875     return (char_u *)&(curbuf->b_p_cino);
5876   case PV_CINW:
5877     return (char_u *)&(curbuf->b_p_cinw);
5878   case PV_COM:
5879     return (char_u *)&(curbuf->b_p_com);
5880   case PV_CMS:
5881     return (char_u *)&(curbuf->b_p_cms);
5882   case PV_CPT:
5883     return (char_u *)&(curbuf->b_p_cpt);
5884 #ifdef BACKSLASH_IN_FILENAME
5885   case PV_CSL:
5886     return (char_u *)&(curbuf->b_p_csl);
5887 #endif
5888   case PV_CFU:
5889     return (char_u *)&(curbuf->b_p_cfu);
5890   case PV_OFU:
5891     return (char_u *)&(curbuf->b_p_ofu);
5892   case PV_EOL:
5893     return (char_u *)&(curbuf->b_p_eol);
5894   case PV_FIXEOL:
5895     return (char_u *)&(curbuf->b_p_fixeol);
5896   case PV_ET:
5897     return (char_u *)&(curbuf->b_p_et);
5898   case PV_FENC:
5899     return (char_u *)&(curbuf->b_p_fenc);
5900   case PV_FF:
5901     return (char_u *)&(curbuf->b_p_ff);
5902   case PV_FT:
5903     return (char_u *)&(curbuf->b_p_ft);
5904   case PV_FO:
5905     return (char_u *)&(curbuf->b_p_fo);
5906   case PV_FLP:
5907     return (char_u *)&(curbuf->b_p_flp);
5908   case PV_IMI:
5909     return (char_u *)&(curbuf->b_p_iminsert);
5910   case PV_IMS:
5911     return (char_u *)&(curbuf->b_p_imsearch);
5912   case PV_INF:
5913     return (char_u *)&(curbuf->b_p_inf);
5914   case PV_ISK:
5915     return (char_u *)&(curbuf->b_p_isk);
5916   case PV_INEX:
5917     return (char_u *)&(curbuf->b_p_inex);
5918   case PV_INDE:
5919     return (char_u *)&(curbuf->b_p_inde);
5920   case PV_INDK:
5921     return (char_u *)&(curbuf->b_p_indk);
5922   case PV_FEX:
5923     return (char_u *)&(curbuf->b_p_fex);
5924   case PV_LISP:
5925     return (char_u *)&(curbuf->b_p_lisp);
5926   case PV_ML:
5927     return (char_u *)&(curbuf->b_p_ml);
5928   case PV_MPS:
5929     return (char_u *)&(curbuf->b_p_mps);
5930   case PV_MA:
5931     return (char_u *)&(curbuf->b_p_ma);
5932   case PV_MOD:
5933     return (char_u *)&(curbuf->b_changed);
5934   case PV_NF:
5935     return (char_u *)&(curbuf->b_p_nf);
5936   case PV_PI:
5937     return (char_u *)&(curbuf->b_p_pi);
5938   case PV_QE:
5939     return (char_u *)&(curbuf->b_p_qe);
5940   case PV_RO:
5941     return (char_u *)&(curbuf->b_p_ro);
5942   case PV_SCBK:
5943     return (char_u *)&(curbuf->b_p_scbk);
5944   case PV_SI:
5945     return (char_u *)&(curbuf->b_p_si);
5946   case PV_STS:
5947     return (char_u *)&(curbuf->b_p_sts);
5948   case PV_SUA:
5949     return (char_u *)&(curbuf->b_p_sua);
5950   case PV_SWF:
5951     return (char_u *)&(curbuf->b_p_swf);
5952   case PV_SMC:
5953     return (char_u *)&(curbuf->b_p_smc);
5954   case PV_SYN:
5955     return (char_u *)&(curbuf->b_p_syn);
5956   case PV_SPC:
5957     return (char_u *)&(curwin->w_s->b_p_spc);
5958   case PV_SPF:
5959     return (char_u *)&(curwin->w_s->b_p_spf);
5960   case PV_SPL:
5961     return (char_u *)&(curwin->w_s->b_p_spl);
5962   case PV_SPO:
5963     return (char_u *)&(curwin->w_s->b_p_spo);
5964   case PV_SW:
5965     return (char_u *)&(curbuf->b_p_sw);
5966   case PV_TFU:
5967     return (char_u *)&(curbuf->b_p_tfu);
5968   case PV_TS:
5969     return (char_u *)&(curbuf->b_p_ts);
5970   case PV_TW:
5971     return (char_u *)&(curbuf->b_p_tw);
5972   case PV_UDF:
5973     return (char_u *)&(curbuf->b_p_udf);
5974   case PV_WM:
5975     return (char_u *)&(curbuf->b_p_wm);
5976   case PV_VSTS:
5977     return (char_u *)&(curbuf->b_p_vsts);
5978   case PV_VTS:
5979     return (char_u *)&(curbuf->b_p_vts);
5980   case PV_KMAP:
5981     return (char_u *)&(curbuf->b_p_keymap);
5982   case PV_SCL:
5983     return (char_u *)&(curwin->w_p_scl);
5984   case PV_WINHL:
5985     return (char_u *)&(curwin->w_p_winhl);
5986   case PV_WINBL:
5987     return (char_u *)&(curwin->w_p_winbl);
5988   default:
5989     iemsg(_("E356: get_varp ERROR"));
5990   }
5991   // always return a valid pointer to avoid a crash!
5992   return (char_u *)&(curbuf->b_p_wm);
5993 }
5994 
5995 /// Get the value of 'equalprg', either the buffer-local one or the global one.
get_equalprg(void)5996 char_u *get_equalprg(void)
5997 {
5998   if (*curbuf->b_p_ep == NUL) {
5999     return p_ep;
6000   }
6001   return curbuf->b_p_ep;
6002 }
6003 
6004 /// Copy options from one window to another.
6005 /// Used when splitting a window.
win_copy_options(win_T * wp_from,win_T * wp_to)6006 void win_copy_options(win_T *wp_from, win_T *wp_to)
6007 {
6008   copy_winopt(&wp_from->w_onebuf_opt, &wp_to->w_onebuf_opt);
6009   copy_winopt(&wp_from->w_allbuf_opt, &wp_to->w_allbuf_opt);
6010 }
6011 
6012 /// Copy the options from one winopt_T to another.
6013 /// Doesn't free the old option values in "to", use clear_winopt() for that.
6014 /// The 'scroll' option is not copied, because it depends on the window height.
6015 /// The 'previewwindow' option is reset, there can be only one preview window.
copy_winopt(winopt_T * from,winopt_T * to)6016 void copy_winopt(winopt_T *from, winopt_T *to)
6017 {
6018   to->wo_arab = from->wo_arab;
6019   to->wo_list = from->wo_list;
6020   to->wo_nu = from->wo_nu;
6021   to->wo_rnu = from->wo_rnu;
6022   to->wo_nuw = from->wo_nuw;
6023   to->wo_rl  = from->wo_rl;
6024   to->wo_rlc = vim_strsave(from->wo_rlc);
6025   to->wo_sbr = vim_strsave(from->wo_sbr);
6026   to->wo_stl = vim_strsave(from->wo_stl);
6027   to->wo_wrap = from->wo_wrap;
6028   to->wo_wrap_save = from->wo_wrap_save;
6029   to->wo_lbr = from->wo_lbr;
6030   to->wo_bri = from->wo_bri;
6031   to->wo_briopt = vim_strsave(from->wo_briopt);
6032   to->wo_scb = from->wo_scb;
6033   to->wo_scb_save = from->wo_scb_save;
6034   to->wo_crb = from->wo_crb;
6035   to->wo_crb_save = from->wo_crb_save;
6036   to->wo_spell = from->wo_spell;
6037   to->wo_cuc = from->wo_cuc;
6038   to->wo_cul = from->wo_cul;
6039   to->wo_culopt = vim_strsave(from->wo_culopt);
6040   to->wo_cc = vim_strsave(from->wo_cc);
6041   to->wo_diff = from->wo_diff;
6042   to->wo_diff_saved = from->wo_diff_saved;
6043   to->wo_cocu = vim_strsave(from->wo_cocu);
6044   to->wo_cole = from->wo_cole;
6045   to->wo_fdc = vim_strsave(from->wo_fdc);
6046   to->wo_fdc_save = from->wo_diff_saved
6047                     ? vim_strsave(from->wo_fdc_save) : empty_option;
6048   to->wo_fen = from->wo_fen;
6049   to->wo_fen_save = from->wo_fen_save;
6050   to->wo_fdi = vim_strsave(from->wo_fdi);
6051   to->wo_fml = from->wo_fml;
6052   to->wo_fdl = from->wo_fdl;
6053   to->wo_fdl_save = from->wo_fdl_save;
6054   to->wo_fdm = vim_strsave(from->wo_fdm);
6055   to->wo_fdm_save = from->wo_diff_saved
6056                     ? vim_strsave(from->wo_fdm_save) : empty_option;
6057   to->wo_fdn = from->wo_fdn;
6058   to->wo_fde = vim_strsave(from->wo_fde);
6059   to->wo_fdt = vim_strsave(from->wo_fdt);
6060   to->wo_fmr = vim_strsave(from->wo_fmr);
6061   to->wo_scl = vim_strsave(from->wo_scl);
6062   to->wo_winhl = vim_strsave(from->wo_winhl);
6063   to->wo_fcs = vim_strsave(from->wo_fcs);
6064   to->wo_lcs = vim_strsave(from->wo_lcs);
6065   to->wo_winbl = from->wo_winbl;
6066   check_winopt(to);             // don't want NULL pointers
6067 }
6068 
6069 /// Check string options in a window for a NULL value.
check_win_options(win_T * win)6070 void check_win_options(win_T *win)
6071 {
6072   check_winopt(&win->w_onebuf_opt);
6073   check_winopt(&win->w_allbuf_opt);
6074 }
6075 
6076 /// Check for NULL pointers in a winopt_T and replace them with empty_option.
check_winopt(winopt_T * wop)6077 static void check_winopt(winopt_T *wop)
6078 {
6079   check_string_option(&wop->wo_fdc);
6080   check_string_option(&wop->wo_fdc_save);
6081   check_string_option(&wop->wo_fdi);
6082   check_string_option(&wop->wo_fdm);
6083   check_string_option(&wop->wo_fdm_save);
6084   check_string_option(&wop->wo_fde);
6085   check_string_option(&wop->wo_fdt);
6086   check_string_option(&wop->wo_fmr);
6087   check_string_option(&wop->wo_scl);
6088   check_string_option(&wop->wo_rlc);
6089   check_string_option(&wop->wo_sbr);
6090   check_string_option(&wop->wo_stl);
6091   check_string_option(&wop->wo_culopt);
6092   check_string_option(&wop->wo_cc);
6093   check_string_option(&wop->wo_cocu);
6094   check_string_option(&wop->wo_briopt);
6095   check_string_option(&wop->wo_winhl);
6096   check_string_option(&wop->wo_fcs);
6097   check_string_option(&wop->wo_lcs);
6098 }
6099 
6100 /// Free the allocated memory inside a winopt_T.
clear_winopt(winopt_T * wop)6101 void clear_winopt(winopt_T *wop)
6102 {
6103   clear_string_option(&wop->wo_fdc);
6104   clear_string_option(&wop->wo_fdc_save);
6105   clear_string_option(&wop->wo_fdi);
6106   clear_string_option(&wop->wo_fdm);
6107   clear_string_option(&wop->wo_fdm_save);
6108   clear_string_option(&wop->wo_fde);
6109   clear_string_option(&wop->wo_fdt);
6110   clear_string_option(&wop->wo_fmr);
6111   clear_string_option(&wop->wo_scl);
6112   clear_string_option(&wop->wo_rlc);
6113   clear_string_option(&wop->wo_sbr);
6114   clear_string_option(&wop->wo_stl);
6115   clear_string_option(&wop->wo_culopt);
6116   clear_string_option(&wop->wo_cc);
6117   clear_string_option(&wop->wo_cocu);
6118   clear_string_option(&wop->wo_briopt);
6119   clear_string_option(&wop->wo_winhl);
6120   clear_string_option(&wop->wo_fcs);
6121   clear_string_option(&wop->wo_lcs);
6122 }
6123 
didset_window_options(win_T * wp)6124 void didset_window_options(win_T *wp)
6125 {
6126   check_colorcolumn(wp);
6127   briopt_check(wp);
6128   fill_culopt_flags(NULL, wp);
6129   set_chars_option(wp, &wp->w_p_fcs, true);
6130   set_chars_option(wp, &wp->w_p_lcs, true);
6131   parse_winhl_opt(wp);  // sets w_hl_needs_update also for w_p_winbl
6132   check_blending(wp);
6133   wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
6134 }
6135 
6136 
6137 /// Copy global option values to local options for one buffer.
6138 /// Used when creating a new buffer and sometimes when entering a buffer.
6139 /// flags:
6140 /// BCO_ENTER    We will enter the buf buffer.
6141 /// BCO_ALWAYS   Always copy the options, but only set b_p_initialized when
6142 ///      appropriate.
6143 /// BCO_NOHELP   Don't copy the values to a help buffer.
buf_copy_options(buf_T * buf,int flags)6144 void buf_copy_options(buf_T *buf, int flags)
6145 {
6146   int should_copy = true;
6147   char_u *save_p_isk = NULL;           // init for GCC
6148   int dont_do_help;
6149   int did_isk = false;
6150 
6151   /*
6152    * Skip this when the option defaults have not been set yet.  Happens when
6153    * main() allocates the first buffer.
6154    */
6155   if (p_cpo != NULL) {
6156     //
6157     // Always copy when entering and 'cpo' contains 'S'.
6158     // Don't copy when already initialized.
6159     // Don't copy when 'cpo' contains 's' and not entering.
6160     //    'S'      BCO_ENTER  initialized  's'  should_copy
6161     //    yes        yes          X         X      true
6162     //    yes        no          yes        X      false
6163     //    no          X          yes        X      false
6164     //     X         no          no        yes     false
6165     //     X         no          no        no      true
6166     //    no         yes         no         X      true
6167     ///
6168     if ((vim_strchr(p_cpo, CPO_BUFOPTGLOB) == NULL || !(flags & BCO_ENTER))
6169         && (buf->b_p_initialized
6170             || (!(flags & BCO_ENTER)
6171                 && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) {
6172       should_copy = false;
6173     }
6174 
6175     if (should_copy || (flags & BCO_ALWAYS)) {
6176       /* Don't copy the options specific to a help buffer when
6177       * BCO_NOHELP is given or the options were initialized already
6178       * (jumping back to a help file with CTRL-T or CTRL-O) */
6179       dont_do_help = ((flags & BCO_NOHELP) && buf->b_help)
6180                      || buf->b_p_initialized;
6181       if (dont_do_help) {               // don't free b_p_isk
6182         save_p_isk = buf->b_p_isk;
6183         buf->b_p_isk = NULL;
6184       }
6185       // Always free the allocated strings.  If not already initialized,
6186       // reset 'readonly' and copy 'fileformat'.
6187       if (!buf->b_p_initialized) {
6188         free_buf_options(buf, true);
6189         buf->b_p_ro = false;                    // don't copy readonly
6190         buf->b_p_fenc = vim_strsave(p_fenc);
6191         switch (*p_ffs) {
6192         case 'm':
6193           buf->b_p_ff = vim_strsave((char_u *)FF_MAC);
6194           break;
6195         case 'd':
6196           buf->b_p_ff = vim_strsave((char_u *)FF_DOS);
6197           break;
6198         case 'u':
6199           buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
6200           break;
6201         default:
6202           buf->b_p_ff = vim_strsave(p_ff);
6203           break;
6204         }
6205         buf->b_p_bh = empty_option;
6206         buf->b_p_bt = empty_option;
6207       } else {
6208         free_buf_options(buf, false);
6209       }
6210 
6211       buf->b_p_ai = p_ai;
6212       buf->b_p_ai_nopaste = p_ai_nopaste;
6213       buf->b_p_sw = p_sw;
6214       buf->b_p_scbk = p_scbk;
6215       buf->b_p_tw = p_tw;
6216       buf->b_p_tw_nopaste = p_tw_nopaste;
6217       buf->b_p_tw_nobin = p_tw_nobin;
6218       buf->b_p_wm = p_wm;
6219       buf->b_p_wm_nopaste = p_wm_nopaste;
6220       buf->b_p_wm_nobin = p_wm_nobin;
6221       buf->b_p_bin = p_bin;
6222       buf->b_p_bomb = p_bomb;
6223       buf->b_p_et = p_et;
6224       buf->b_p_fixeol = p_fixeol;
6225       buf->b_p_et_nobin = p_et_nobin;
6226       buf->b_p_et_nopaste = p_et_nopaste;
6227       buf->b_p_ml = p_ml;
6228       buf->b_p_ml_nobin = p_ml_nobin;
6229       buf->b_p_inf = p_inf;
6230       buf->b_p_swf = cmdmod.noswapfile ? false : p_swf;
6231       buf->b_p_cpt = vim_strsave(p_cpt);
6232 #ifdef BACKSLASH_IN_FILENAME
6233       buf->b_p_csl = vim_strsave(p_csl);
6234 #endif
6235       buf->b_p_cfu = vim_strsave(p_cfu);
6236       buf->b_p_ofu = vim_strsave(p_ofu);
6237       buf->b_p_tfu = vim_strsave(p_tfu);
6238       buf->b_p_sts = p_sts;
6239       buf->b_p_sts_nopaste = p_sts_nopaste;
6240       buf->b_p_vsts = vim_strsave(p_vsts);
6241       if (p_vsts && p_vsts != empty_option) {
6242         tabstop_set(p_vsts, &buf->b_p_vsts_array);
6243       } else {
6244         buf->b_p_vsts_array = 0;
6245       }
6246       buf->b_p_vsts_nopaste = p_vsts_nopaste
6247                                 ? vim_strsave(p_vsts_nopaste)
6248                                 : NULL;
6249       buf->b_p_com = vim_strsave(p_com);
6250       buf->b_p_cms = vim_strsave(p_cms);
6251       buf->b_p_fo = vim_strsave(p_fo);
6252       buf->b_p_flp = vim_strsave(p_flp);
6253       buf->b_p_nf = vim_strsave(p_nf);
6254       buf->b_p_mps = vim_strsave(p_mps);
6255       buf->b_p_si = p_si;
6256       buf->b_p_channel = 0;
6257       buf->b_p_ci = p_ci;
6258       buf->b_p_cin = p_cin;
6259       buf->b_p_cink = vim_strsave(p_cink);
6260       buf->b_p_cino = vim_strsave(p_cino);
6261       // Don't copy 'filetype', it must be detected
6262       buf->b_p_ft = empty_option;
6263       buf->b_p_pi = p_pi;
6264       buf->b_p_cinw = vim_strsave(p_cinw);
6265       buf->b_p_lisp = p_lisp;
6266       // Don't copy 'syntax', it must be set
6267       buf->b_p_syn = empty_option;
6268       buf->b_p_smc = p_smc;
6269       buf->b_s.b_syn_isk = empty_option;
6270       buf->b_s.b_p_spc = vim_strsave(p_spc);
6271       (void)compile_cap_prog(&buf->b_s);
6272       buf->b_s.b_p_spf = vim_strsave(p_spf);
6273       buf->b_s.b_p_spl = vim_strsave(p_spl);
6274       buf->b_s.b_p_spo = vim_strsave(p_spo);
6275       buf->b_p_inde = vim_strsave(p_inde);
6276       buf->b_p_indk = vim_strsave(p_indk);
6277       buf->b_p_fp = empty_option;
6278       buf->b_p_fex = vim_strsave(p_fex);
6279       buf->b_p_sua = vim_strsave(p_sua);
6280       buf->b_p_keymap = vim_strsave(p_keymap);
6281       buf->b_kmap_state |= KEYMAP_INIT;
6282       // This isn't really an option, but copying the langmap and IME
6283       // state from the current buffer is better than resetting it.
6284       buf->b_p_iminsert = p_iminsert;
6285       buf->b_p_imsearch = p_imsearch;
6286 
6287       // options that are normally global but also have a local value
6288       // are not copied, start using the global value
6289       buf->b_p_ar = -1;
6290       buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
6291       buf->b_p_bkc = empty_option;
6292       buf->b_bkc_flags = 0;
6293       buf->b_p_gp = empty_option;
6294       buf->b_p_mp = empty_option;
6295       buf->b_p_efm = empty_option;
6296       buf->b_p_ep = empty_option;
6297       buf->b_p_kp = empty_option;
6298       buf->b_p_path = empty_option;
6299       buf->b_p_tags = empty_option;
6300       buf->b_p_tc = empty_option;
6301       buf->b_tc_flags = 0;
6302       buf->b_p_def = empty_option;
6303       buf->b_p_inc = empty_option;
6304       buf->b_p_inex = vim_strsave(p_inex);
6305       buf->b_p_dict = empty_option;
6306       buf->b_p_tsr = empty_option;
6307       buf->b_p_tsrfu = empty_option;
6308       buf->b_p_qe = vim_strsave(p_qe);
6309       buf->b_p_udf = p_udf;
6310       buf->b_p_lw = empty_option;
6311       buf->b_p_menc = empty_option;
6312 
6313       /*
6314        * Don't copy the options set by ex_help(), use the saved values,
6315        * when going from a help buffer to a non-help buffer.
6316        * Don't touch these at all when BCO_NOHELP is used and going from
6317        * or to a help buffer.
6318        */
6319       if (dont_do_help) {
6320         buf->b_p_isk = save_p_isk;
6321         if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
6322           tabstop_set(p_vts, &buf->b_p_vts_array);
6323         } else {
6324           buf->b_p_vts_array = NULL;
6325         }
6326       } else {
6327         buf->b_p_isk = vim_strsave(p_isk);
6328         did_isk = true;
6329         buf->b_p_ts = p_ts;
6330         buf->b_p_vts = vim_strsave(p_vts);
6331         if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
6332           tabstop_set(p_vts, &buf->b_p_vts_array);
6333         } else {
6334           buf->b_p_vts_array = NULL;
6335         }
6336         buf->b_help = false;
6337         if (buf->b_p_bt[0] == 'h') {
6338           clear_string_option(&buf->b_p_bt);
6339         }
6340         buf->b_p_ma = p_ma;
6341       }
6342     }
6343 
6344     /*
6345      * When the options should be copied (ignoring BCO_ALWAYS), set the
6346      * flag that indicates that the options have been initialized.
6347      */
6348     if (should_copy) {
6349       buf->b_p_initialized = true;
6350     }
6351   }
6352 
6353   check_buf_options(buf);           // make sure we don't have NULLs
6354   if (did_isk) {
6355     (void)buf_init_chartab(buf, false);
6356   }
6357 }
6358 
6359 /// Reset the 'modifiable' option and its default value.
reset_modifiable(void)6360 void reset_modifiable(void)
6361 {
6362   int opt_idx;
6363 
6364   curbuf->b_p_ma = false;
6365   p_ma = false;
6366   opt_idx = findoption("ma");
6367   if (opt_idx >= 0) {
6368     options[opt_idx].def_val = false;
6369   }
6370 }
6371 
6372 /// Set the global value for 'iminsert' to the local value.
set_iminsert_global(void)6373 void set_iminsert_global(void)
6374 {
6375   p_iminsert = curbuf->b_p_iminsert;
6376 }
6377 
6378 /// Set the global value for 'imsearch' to the local value.
set_imsearch_global(void)6379 void set_imsearch_global(void)
6380 {
6381   p_imsearch = curbuf->b_p_imsearch;
6382 }
6383 
6384 static int expand_option_idx = -1;
6385 static char_u expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
6386 static int expand_option_flags = 0;
6387 
6388 /// @param opt_flags  OPT_GLOBAL and/or OPT_LOCAL
set_context_in_set_cmd(expand_T * xp,char_u * arg,int opt_flags)6389 void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
6390 {
6391   char_u nextchar;
6392   uint32_t flags = 0;           // init for GCC
6393   int opt_idx = 0;              // init for GCC
6394   char_u *p;
6395   char_u *s;
6396   int is_term_option = false;
6397   int key;
6398 
6399   expand_option_flags = opt_flags;
6400 
6401   xp->xp_context = EXPAND_SETTINGS;
6402   if (*arg == NUL) {
6403     xp->xp_pattern = arg;
6404     return;
6405   }
6406   p = arg + STRLEN(arg) - 1;
6407   if (*p == ' ' && *(p - 1) != '\\') {
6408     xp->xp_pattern = p + 1;
6409     return;
6410   }
6411   while (p > arg) {
6412     s = p;
6413     // count number of backslashes before ' ' or ','
6414     if (*p == ' ' || *p == ',') {
6415       while (s > arg && *(s - 1) == '\\') {
6416         s--;
6417       }
6418     }
6419     // break at a space with an even number of backslashes
6420     if (*p == ' ' && ((p - s) & 1) == 0) {
6421       p++;
6422       break;
6423     }
6424     p--;
6425   }
6426   if (STRNCMP(p, "no", 2) == 0) {
6427     xp->xp_context = EXPAND_BOOL_SETTINGS;
6428     p += 2;
6429   }
6430   if (STRNCMP(p, "inv", 3) == 0) {
6431     xp->xp_context = EXPAND_BOOL_SETTINGS;
6432     p += 3;
6433   }
6434   xp->xp_pattern = arg = p;
6435   if (*arg == '<') {
6436     while (*p != '>') {
6437       if (*p++ == NUL) {            // expand terminal option name
6438         return;
6439       }
6440     }
6441     key = get_special_key_code(arg + 1);
6442     if (key == 0) {                 // unknown name
6443       xp->xp_context = EXPAND_NOTHING;
6444       return;
6445     }
6446     nextchar = *++p;
6447     is_term_option = true;
6448     expand_option_name[2] = (char_u)KEY2TERMCAP0(key);
6449     expand_option_name[3] = KEY2TERMCAP1(key);
6450   } else {
6451     if (p[0] == 't' && p[1] == '_') {
6452       p += 2;
6453       if (*p != NUL) {
6454         p++;
6455       }
6456       if (*p == NUL) {
6457         return;                 // expand option name
6458       }
6459       nextchar = *++p;
6460       is_term_option = true;
6461       expand_option_name[2] = p[-2];
6462       expand_option_name[3] = p[-1];
6463     } else {
6464       // Allow * wildcard.
6465       while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') {
6466         p++;
6467       }
6468       if (*p == NUL) {
6469         return;
6470       }
6471       nextchar = *p;
6472       opt_idx = findoption_len((const char *)arg, (size_t)(p - arg));
6473       if (opt_idx == -1 || options[opt_idx].var == NULL) {
6474         xp->xp_context = EXPAND_NOTHING;
6475         return;
6476       }
6477       flags = options[opt_idx].flags;
6478       if (flags & P_BOOL) {
6479         xp->xp_context = EXPAND_NOTHING;
6480         return;
6481       }
6482     }
6483   }
6484   // handle "-=" and "+="
6485   if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') {
6486     p++;
6487     nextchar = '=';
6488   }
6489   if ((nextchar != '=' && nextchar != ':')
6490       || xp->xp_context == EXPAND_BOOL_SETTINGS) {
6491     xp->xp_context = EXPAND_UNSUCCESSFUL;
6492     return;
6493   }
6494   if (p[1] == NUL) {
6495     xp->xp_context = EXPAND_OLD_SETTING;
6496     if (is_term_option) {
6497       expand_option_idx = -1;
6498     } else {
6499       expand_option_idx = opt_idx;
6500     }
6501     xp->xp_pattern = p + 1;
6502     return;
6503   }
6504   xp->xp_context = EXPAND_NOTHING;
6505   if (is_term_option || (flags & P_NUM)) {
6506     return;
6507   }
6508 
6509   xp->xp_pattern = p + 1;
6510 
6511   if (flags & P_EXPAND) {
6512     p = options[opt_idx].var;
6513     if (p == (char_u *)&p_bdir
6514         || p == (char_u *)&p_dir
6515         || p == (char_u *)&p_path
6516         || p == (char_u *)&p_pp
6517         || p == (char_u *)&p_rtp
6518         || p == (char_u *)&p_cdpath
6519         || p == (char_u *)&p_vdir) {
6520       xp->xp_context = EXPAND_DIRECTORIES;
6521       if (p == (char_u *)&p_path
6522           || p == (char_u *)&p_cdpath) {
6523         xp->xp_backslash = XP_BS_THREE;
6524       } else {
6525         xp->xp_backslash = XP_BS_ONE;
6526       }
6527     } else if (p == (char_u *)&p_ft) {
6528       xp->xp_context = EXPAND_FILETYPE;
6529     } else {
6530       xp->xp_context = EXPAND_FILES;
6531       // for 'tags' need three backslashes for a space
6532       if (p == (char_u *)&p_tags) {
6533         xp->xp_backslash = XP_BS_THREE;
6534       } else {
6535         xp->xp_backslash = XP_BS_ONE;
6536       }
6537     }
6538   }
6539 
6540   // For an option that is a list of file names, find the start of the
6541   // last file name.
6542   for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; p--) {
6543     // count number of backslashes before ' ' or ','
6544     if (*p == ' ' || *p == ',') {
6545       s = p;
6546       while (s > xp->xp_pattern && *(s - 1) == '\\') {
6547         s--;
6548       }
6549       if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
6550           || (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) {
6551         xp->xp_pattern = p + 1;
6552         break;
6553       }
6554     }
6555 
6556     // for 'spellsuggest' start at "file:"
6557     if (options[opt_idx].var == (char_u *)&p_sps
6558         && STRNCMP(p, "file:", 5) == 0) {
6559       xp->xp_pattern = p + 5;
6560       break;
6561     }
6562   }
6563 }
6564 
ExpandSettings(expand_T * xp,regmatch_T * regmatch,int * num_file,char_u *** file)6565 int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file)
6566 {
6567   int num_normal = 0;  // Nr of matching non-term-code settings
6568   int match;
6569   int count = 0;
6570   char_u *str;
6571   int loop;
6572   static char *(names[]) = { "all" };
6573   int ic = regmatch->rm_ic;  // remember the ignore-case flag
6574 
6575   // do this loop twice:
6576   // loop == 0: count the number of matching options
6577   // loop == 1: copy the matching options into allocated memory
6578   for (loop = 0; loop <= 1; loop++) {
6579     regmatch->rm_ic = ic;
6580     if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
6581       for (match = 0; match < (int)ARRAY_SIZE(names);
6582            match++) {
6583         if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) {
6584           if (loop == 0) {
6585             num_normal++;
6586           } else {
6587             (*file)[count++] = vim_strsave((char_u *)names[match]);
6588           }
6589         }
6590       }
6591     }
6592     for (size_t opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
6593          opt_idx++) {
6594       if (options[opt_idx].var == NULL) {
6595         continue;
6596       }
6597       if (xp->xp_context == EXPAND_BOOL_SETTINGS
6598           && !(options[opt_idx].flags & P_BOOL)) {
6599         continue;
6600       }
6601       match = false;
6602       if (vim_regexec(regmatch, str, (colnr_T)0)
6603           || (options[opt_idx].shortname != NULL
6604               && vim_regexec(regmatch,
6605                              (char_u *)options[opt_idx].shortname,
6606                              (colnr_T)0))) {
6607         match = true;
6608       }
6609 
6610       if (match) {
6611         if (loop == 0) {
6612           num_normal++;
6613         } else {
6614           (*file)[count++] = vim_strsave(str);
6615         }
6616       }
6617     }
6618 
6619     if (loop == 0) {
6620       if (num_normal > 0) {
6621         *num_file = num_normal;
6622       } else {
6623         return OK;
6624       }
6625       *file = (char_u **)xmalloc((size_t)(*num_file) * sizeof(char_u *));
6626     }
6627   }
6628   return OK;
6629 }
6630 
ExpandOldSetting(int * num_file,char_u *** file)6631 void ExpandOldSetting(int *num_file, char_u ***file)
6632 {
6633   char_u *var = NULL;
6634 
6635   *num_file = 0;
6636   *file = (char_u **)xmalloc(sizeof(char_u *));
6637 
6638   /*
6639    * For a terminal key code expand_option_idx is < 0.
6640    */
6641   if (expand_option_idx < 0) {
6642     expand_option_idx = findoption((const char *)expand_option_name);
6643   }
6644 
6645   if (expand_option_idx >= 0) {
6646     // Put string of option value in NameBuff.
6647     option_value2string(&options[expand_option_idx], expand_option_flags);
6648     var = NameBuff;
6649   } else {
6650     var = (char_u *)"";
6651   }
6652 
6653   // A backslash is required before some characters.  This is the reverse of
6654   // what happens in do_set().
6655   char_u *buf = vim_strsave_escaped(var, escape_chars);
6656 
6657 #ifdef BACKSLASH_IN_FILENAME
6658   // For MS-Windows et al. we don't double backslashes at the start and
6659   // before a file name character.
6660   for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
6661     if (var[0] == '\\' && var[1] == '\\'
6662         && expand_option_idx >= 0
6663         && (options[expand_option_idx].flags & P_EXPAND)
6664         && vim_isfilec(var[2])
6665         && (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
6666       STRMOVE(var, var + 1);
6667     }
6668   }
6669 #endif
6670 
6671   *file[0] = buf;
6672   *num_file = 1;
6673 }
6674 
6675 /// Get the value for the numeric or string option///opp in a nice format into
6676 /// NameBuff[].  Must not be called with a hidden option!
6677 ///
6678 /// @param opt_flags  OPT_GLOBAL and/or OPT_LOCAL
option_value2string(vimoption_T * opp,int opt_flags)6679 static void option_value2string(vimoption_T *opp, int opt_flags)
6680 {
6681   char_u *varp;
6682 
6683   varp = get_varp_scope(opp, opt_flags);
6684 
6685   if (opp->flags & P_NUM) {
6686     long wc = 0;
6687 
6688     if (wc_use_keyname(varp, &wc)) {
6689       STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
6690     } else if (wc != 0) {
6691       STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff));
6692     } else {
6693       snprintf((char *)NameBuff,
6694                sizeof(NameBuff),
6695                "%" PRId64,
6696                (int64_t)*(long *)varp);
6697     }
6698   } else {  // P_STRING
6699     varp = *(char_u **)(varp);
6700     if (varp == NULL) {  // Just in case.
6701       NameBuff[0] = NUL;
6702     } else if (opp->flags & P_EXPAND) {
6703       home_replace(NULL, varp, NameBuff, MAXPATHL, false);
6704       // Translate 'pastetoggle' into special key names.
6705     } else if ((char_u **)opp->var == &p_pt) {
6706       str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
6707     } else {
6708       STRLCPY(NameBuff, varp, MAXPATHL);
6709     }
6710   }
6711 }
6712 
6713 /// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be
6714 /// printed as a keyname.
6715 /// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
wc_use_keyname(char_u * varp,long * wcp)6716 static int wc_use_keyname(char_u *varp, long *wcp)
6717 {
6718   if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) {
6719     *wcp = *(long *)varp;
6720     if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)(*wcp)) >= 0) {
6721       return true;
6722     }
6723   }
6724   return false;
6725 }
6726 
6727 /// Any character has an equivalent 'langmap' character.  This is used for
6728 /// keyboards that have a special language mode that sends characters above
6729 /// 128 (although other characters can be translated too).  The "to" field is a
6730 /// Vim command character.  This avoids having to switch the keyboard back to
6731 /// ASCII mode when leaving Insert mode.
6732 ///
6733 /// langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
6734 /// commands.
6735 /// langmap_mapga.ga_data is a sorted table of langmap_entry_T.
6736 /// This does the same as langmap_mapchar[] for characters >= 256.
6737 ///
6738 /// With multi-byte support use growarray for 'langmap' chars >= 256
6739 typedef struct {
6740   int from;
6741   int to;
6742 } langmap_entry_T;
6743 
6744 static garray_T langmap_mapga = GA_EMPTY_INIT_VALUE;
6745 
6746 /// Search for an entry in "langmap_mapga" for "from".  If found set the "to"
6747 /// field.  If not found insert a new entry at the appropriate location.
langmap_set_entry(int from,int to)6748 static void langmap_set_entry(int from, int to)
6749 {
6750   langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
6751   unsigned int a = 0;
6752   assert(langmap_mapga.ga_len >= 0);
6753   unsigned int b = (unsigned int)langmap_mapga.ga_len;
6754 
6755   // Do a binary search for an existing entry.
6756   while (a != b) {
6757     unsigned int i = (a + b) / 2;
6758     int d = entries[i].from - from;
6759 
6760     if (d == 0) {
6761       entries[i].to = to;
6762       return;
6763     }
6764     if (d < 0) {
6765       a = i + 1;
6766     } else {
6767       b = i;
6768     }
6769   }
6770 
6771   ga_grow(&langmap_mapga, 1);
6772 
6773   // insert new entry at position "a"
6774   entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
6775   memmove(entries + 1, entries,
6776           ((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
6777   langmap_mapga.ga_len++;
6778   entries[0].from = from;
6779   entries[0].to = to;
6780 }
6781 
6782 /// Apply 'langmap' to multi-byte character "c" and return the result.
langmap_adjust_mb(int c)6783 int langmap_adjust_mb(int c)
6784 {
6785   langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
6786   int a = 0;
6787   int b = langmap_mapga.ga_len;
6788 
6789   while (a != b) {
6790     int i = (a + b) / 2;
6791     int d = entries[i].from - c;
6792 
6793     if (d == 0) {
6794       return entries[i].to;        // found matching entry
6795     }
6796     if (d < 0) {
6797       a = i + 1;
6798     } else {
6799       b = i;
6800     }
6801   }
6802   return c;    // no entry found, return "c" unmodified
6803 }
6804 
langmap_init(void)6805 static void langmap_init(void)
6806 {
6807   for (int i = 0; i < 256; i++) {
6808     langmap_mapchar[i] = (char_u)i;      // we init with a one-to-one map
6809   }
6810   ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
6811 }
6812 
6813 /// Called when langmap option is set; the language map can be
6814 /// changed at any time!
langmap_set(void)6815 static void langmap_set(void)
6816 {
6817   char_u *p;
6818   char_u *p2;
6819   int from, to;
6820 
6821   ga_clear(&langmap_mapga);                 // clear the previous map first
6822   langmap_init();                           // back to one-to-one map
6823 
6824   for (p = p_langmap; p[0] != NUL;) {
6825     for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
6826          MB_PTR_ADV(p2)) {
6827       if (p2[0] == '\\' && p2[1] != NUL) {
6828         p2++;
6829       }
6830     }
6831     if (p2[0] == ';') {
6832       p2++;                 // abcd;ABCD form, p2 points to A
6833     } else {
6834       p2 = NULL;            // aAbBcCdD form, p2 is NULL
6835     }
6836     while (p[0]) {
6837       if (p[0] == ',') {
6838         p++;
6839         break;
6840       }
6841       if (p[0] == '\\' && p[1] != NUL) {
6842         p++;
6843       }
6844       from = utf_ptr2char(p);
6845       to = NUL;
6846       if (p2 == NULL) {
6847         MB_PTR_ADV(p);
6848         if (p[0] != ',') {
6849           if (p[0] == '\\') {
6850             p++;
6851           }
6852           to = utf_ptr2char(p);
6853         }
6854       } else {
6855         if (p2[0] != ',') {
6856           if (p2[0] == '\\') {
6857             p2++;
6858           }
6859           to = utf_ptr2char(p2);
6860         }
6861       }
6862       if (to == NUL) {
6863         semsg(_("E357: 'langmap': Matching character missing for %s"),
6864               transchar(from));
6865         return;
6866       }
6867 
6868       if (from >= 256) {
6869         langmap_set_entry(from, to);
6870       } else {
6871         assert(to <= UCHAR_MAX);
6872         langmap_mapchar[from & 255] = (char_u)to;
6873       }
6874 
6875       // Advance to next pair
6876       MB_PTR_ADV(p);
6877       if (p2 != NULL) {
6878         MB_PTR_ADV(p2);
6879         if (*p == ';') {
6880           p = p2;
6881           if (p[0] != NUL) {
6882             if (p[0] != ',') {
6883               semsg(_("E358: 'langmap': Extra characters after semicolon: %s"),
6884                     p);
6885               return;
6886             }
6887             p++;
6888           }
6889           break;
6890         }
6891       }
6892     }
6893   }
6894 }
6895 
6896 /// Return true if format option 'x' is in effect.
6897 /// Take care of no formatting when 'paste' is set.
has_format_option(int x)6898 bool has_format_option(int x)
6899   FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
6900 {
6901   if (p_paste) {
6902     return false;
6903   }
6904   return vim_strchr(curbuf->b_p_fo, x) != NULL;
6905 }
6906 
6907 /// @returns true if "x" is present in 'shortmess' option, or
6908 /// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
shortmess(int x)6909 bool shortmess(int x)
6910 {
6911   return (p_shm != NULL
6912           && (vim_strchr(p_shm, x) != NULL
6913               || (vim_strchr(p_shm, 'a') != NULL
6914                   && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
6915 }
6916 
6917 /// paste_option_changed() - Called after p_paste was set or reset.
paste_option_changed(void)6918 static void paste_option_changed(void)
6919 {
6920   static int old_p_paste = false;
6921   static int save_sm = 0;
6922   static int save_sta = 0;
6923   static int save_ru = 0;
6924   static int save_ri = 0;
6925   static int save_hkmap = 0;
6926 
6927   if (p_paste) {
6928     /*
6929      * Paste switched from off to on.
6930      * Save the current values, so they can be restored later.
6931      */
6932     if (!old_p_paste) {
6933       // save options for each buffer
6934       FOR_ALL_BUFFERS(buf) {
6935         buf->b_p_tw_nopaste = buf->b_p_tw;
6936         buf->b_p_wm_nopaste = buf->b_p_wm;
6937         buf->b_p_sts_nopaste = buf->b_p_sts;
6938         buf->b_p_ai_nopaste = buf->b_p_ai;
6939         buf->b_p_et_nopaste = buf->b_p_et;
6940         if (buf->b_p_vsts_nopaste) {
6941           xfree(buf->b_p_vsts_nopaste);
6942         }
6943         buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
6944                                     ? vim_strsave(buf->b_p_vsts)
6945                                     : NULL;
6946       }
6947 
6948       // save global options
6949       save_sm = p_sm;
6950       save_sta = p_sta;
6951       save_ru = p_ru;
6952       save_ri = p_ri;
6953       save_hkmap = p_hkmap;
6954       // save global values for local buffer options
6955       p_ai_nopaste = p_ai;
6956       p_et_nopaste = p_et;
6957       p_sts_nopaste = p_sts;
6958       p_tw_nopaste = p_tw;
6959       p_wm_nopaste = p_wm;
6960       if (p_vsts_nopaste) {
6961         xfree(p_vsts_nopaste);
6962       }
6963       p_vsts_nopaste = p_vsts && p_vsts != empty_option
6964                           ? vim_strsave(p_vsts)
6965                           : NULL;
6966     }
6967 
6968     // Always set the option values, also when 'paste' is set when it is
6969     // already on.
6970     // set options for each buffer
6971     FOR_ALL_BUFFERS(buf) {
6972       buf->b_p_tw = 0;              // textwidth is 0
6973       buf->b_p_wm = 0;              // wrapmargin is 0
6974       buf->b_p_sts = 0;             // softtabstop is 0
6975       buf->b_p_ai = 0;              // no auto-indent
6976       buf->b_p_et = 0;              // no expandtab
6977       if (buf->b_p_vsts) {
6978         free_string_option(buf->b_p_vsts);
6979       }
6980       buf->b_p_vsts = empty_option;
6981       if (buf->b_p_vsts_array) {
6982         xfree(buf->b_p_vsts_array);
6983       }
6984       buf->b_p_vsts_array = 0;
6985     }
6986 
6987     // set global options
6988     p_sm = 0;                       // no showmatch
6989     p_sta = 0;                      // no smarttab
6990     if (p_ru) {
6991       status_redraw_all();          // redraw to remove the ruler
6992     }
6993     p_ru = 0;                       // no ruler
6994     p_ri = 0;                       // no reverse insert
6995     p_hkmap = 0;                    // no Hebrew keyboard
6996     // set global values for local buffer options
6997     p_tw = 0;
6998     p_wm = 0;
6999     p_sts = 0;
7000     p_ai = 0;
7001     if (p_vsts) {
7002       free_string_option(p_vsts);
7003     }
7004     p_vsts = empty_option;
7005   } else if (old_p_paste) {
7006     // Paste switched from on to off: Restore saved values.
7007 
7008     // restore options for each buffer
7009     FOR_ALL_BUFFERS(buf) {
7010       buf->b_p_tw = buf->b_p_tw_nopaste;
7011       buf->b_p_wm = buf->b_p_wm_nopaste;
7012       buf->b_p_sts = buf->b_p_sts_nopaste;
7013       buf->b_p_ai = buf->b_p_ai_nopaste;
7014       buf->b_p_et = buf->b_p_et_nopaste;
7015       if (buf->b_p_vsts) {
7016         free_string_option(buf->b_p_vsts);
7017       }
7018       buf->b_p_vsts = buf->b_p_vsts_nopaste
7019                         ? vim_strsave(buf->b_p_vsts_nopaste)
7020                         : empty_option;
7021       if (buf->b_p_vsts_array) {
7022         xfree(buf->b_p_vsts_array);
7023       }
7024       if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
7025         tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
7026       } else {
7027         buf->b_p_vsts_array = 0;
7028       }
7029     }
7030 
7031     // restore global options
7032     p_sm = save_sm;
7033     p_sta = save_sta;
7034     if (p_ru != save_ru) {
7035       status_redraw_all();          // redraw to draw the ruler
7036     }
7037     p_ru = save_ru;
7038     p_ri = save_ri;
7039     p_hkmap = save_hkmap;
7040     // set global values for local buffer options
7041     p_ai = p_ai_nopaste;
7042     p_et = p_et_nopaste;
7043     p_sts = p_sts_nopaste;
7044     p_tw = p_tw_nopaste;
7045     p_wm = p_wm_nopaste;
7046     if (p_vsts) {
7047       free_string_option(p_vsts);
7048     }
7049     p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
7050   }
7051 
7052   old_p_paste = p_paste;
7053 }
7054 
7055 /// vimrc_found() - Called when a vimrc or "VIMINIT" has been found.
7056 ///
7057 /// Set the values for options that didn't get set yet to the defaults.
7058 /// When "fname" is not NULL, use it to set $"envname" when it wasn't set yet.
vimrc_found(char * fname,char * envname)7059 void vimrc_found(char *fname, char *envname)
7060 {
7061   if (fname != NULL && envname != NULL) {
7062     char *p = vim_getenv(envname);
7063     if (p == NULL) {
7064       // Set $MYVIMRC to the first vimrc file found.
7065       p = FullName_save(fname, false);
7066       if (p != NULL) {
7067         os_setenv(envname, p, 1);
7068         xfree(p);
7069       }
7070     } else {
7071       xfree(p);
7072     }
7073   }
7074 }
7075 
7076 /// Check whether global option has been set
7077 ///
7078 /// @param[in]  name  Option name.
7079 ///
7080 /// @return True if it was set.
option_was_set(const char * name)7081 bool option_was_set(const char *name)
7082 {
7083   int idx;
7084 
7085   idx = findoption(name);
7086   if (idx < 0) {  // Unknown option.
7087     return false;
7088   } else if (options[idx].flags & P_WAS_SET) {
7089     return true;
7090   }
7091   return false;
7092 }
7093 
7094 /// Reset the flag indicating option "name" was set.
7095 ///
7096 /// @param[in]  name  Option name.
reset_option_was_set(const char * name)7097 void reset_option_was_set(const char *name)
7098 {
7099   const int idx = findoption(name);
7100 
7101   if (idx >= 0) {
7102     options[idx].flags &= ~P_WAS_SET;
7103   }
7104 }
7105 
7106 /// fill_breakat_flags() -- called when 'breakat' changes value.
fill_breakat_flags(void)7107 static void fill_breakat_flags(void)
7108 {
7109   char_u *p;
7110   int i;
7111 
7112   for (i = 0; i < 256; i++) {
7113     breakat_flags[i] = false;
7114   }
7115 
7116   if (p_breakat != NULL) {
7117     for (p = p_breakat; *p; p++) {
7118       breakat_flags[*p] = true;
7119     }
7120   }
7121 }
7122 
7123 /// fill_culopt_flags() -- called when 'culopt' changes value
fill_culopt_flags(char_u * val,win_T * wp)7124 static int fill_culopt_flags(char_u *val, win_T *wp)
7125 {
7126   char_u *p;
7127   char_u culopt_flags_new = 0;
7128 
7129   if (val == NULL) {
7130     p = wp->w_p_culopt;
7131   } else {
7132     p = val;
7133   }
7134   while (*p != NUL) {
7135     if (STRNCMP(p, "line", 4) == 0) {
7136       p += 4;
7137       culopt_flags_new |= CULOPT_LINE;
7138     } else if (STRNCMP(p, "both", 4) == 0) {
7139       p += 4;
7140       culopt_flags_new |= CULOPT_LINE | CULOPT_NBR;
7141     } else if (STRNCMP(p, "number", 6) == 0) {
7142       p += 6;
7143       culopt_flags_new |= CULOPT_NBR;
7144     } else if (STRNCMP(p, "screenline", 10) == 0) {
7145       p += 10;
7146       culopt_flags_new |= CULOPT_SCRLINE;
7147     }
7148 
7149     if (*p != ',' && *p != NUL) {
7150       return FAIL;
7151     }
7152     if (*p == ',') {
7153       p++;
7154     }
7155   }
7156 
7157   // Can't have both "line" and "screenline".
7158   if ((culopt_flags_new & CULOPT_LINE) && (culopt_flags_new & CULOPT_SCRLINE)) {
7159     return FAIL;
7160   }
7161   wp->w_p_culopt_flags = culopt_flags_new;
7162 
7163   return OK;
7164 }
7165 
7166 /// Check an option that can be a range of string values.
7167 ///
7168 /// @param list  when true: accept a list of values
7169 ///
7170 /// @return  OK for correct value, FAIL otherwise. Empty is always OK.
check_opt_strings(char_u * val,char ** values,int list)7171 static int check_opt_strings(char_u *val, char **values, int list)
7172 {
7173   return opt_strings_flags(val, values, NULL, list);
7174 }
7175 
7176 /// Handle an option that can be a range of string values.
7177 /// Set a flag in "*flagp" for each string present.
7178 ///
7179 /// @param val  new value
7180 /// @param values  array of valid string values
7181 /// @param list  when true: accept a list of values
7182 ///
7183 /// @return  OK for correct value, FAIL otherwise. Empty is always OK.
opt_strings_flags(char_u * val,char ** values,unsigned * flagp,bool list)7184 static int opt_strings_flags(char_u *val, char **values, unsigned *flagp, bool list)
7185 {
7186   unsigned int new_flags = 0;
7187 
7188   while (*val) {
7189     for (unsigned int i = 0;; i++) {
7190       if (values[i] == NULL) {          // val not found in values[]
7191         return FAIL;
7192       }
7193 
7194       size_t len = STRLEN(values[i]);
7195       if (STRNCMP(values[i], val, len) == 0
7196           && ((list && val[len] == ',') || val[len] == NUL)) {
7197         val += len + (val[len] == ',');
7198         assert(i < sizeof(1U) * 8);
7199         new_flags |= (1U << i);
7200         break;                  // check next item in val list
7201       }
7202     }
7203   }
7204   if (flagp != NULL) {
7205     *flagp = new_flags;
7206   }
7207 
7208   return OK;
7209 }
7210 
7211 /// Read the 'wildmode' option, fill wim_flags[].
check_opt_wim(void)7212 static int check_opt_wim(void)
7213 {
7214   char_u new_wim_flags[4];
7215   char_u *p;
7216   int i;
7217   int idx = 0;
7218 
7219   for (i = 0; i < 4; i++) {
7220     new_wim_flags[i] = 0;
7221   }
7222 
7223   for (p = p_wim; *p; p++) {
7224     for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
7225     if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
7226       return FAIL;
7227     }
7228     if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
7229       new_wim_flags[idx] |= WIM_LONGEST;
7230     } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
7231       new_wim_flags[idx] |= WIM_FULL;
7232     } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
7233       new_wim_flags[idx] |= WIM_LIST;
7234     } else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) {
7235       new_wim_flags[idx] |= WIM_BUFLASTUSED;
7236     } else {
7237       return FAIL;
7238     }
7239     p += i;
7240     if (*p == NUL) {
7241       break;
7242     }
7243     if (*p == ',') {
7244       if (idx == 3) {
7245         return FAIL;
7246       }
7247       idx++;
7248     }
7249   }
7250 
7251   // fill remaining entries with last flag
7252   while (idx < 3) {
7253     new_wim_flags[idx + 1] = new_wim_flags[idx];
7254     idx++;
7255   }
7256 
7257   // only when there are no errors, wim_flags[] is changed
7258   for (i = 0; i < 4; i++) {
7259     wim_flags[i] = new_wim_flags[i];
7260   }
7261   return OK;
7262 }
7263 
7264 /// Check if backspacing over something is allowed.
7265 /// @param  what  BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP
can_bs(int what)7266 bool can_bs(int what)
7267 {
7268   if (what == BS_START && bt_prompt(curbuf)) {
7269     return false;
7270   }
7271   switch (*p_bs) {
7272   case '3':
7273     return true;
7274   case '2':
7275     return what != BS_NOSTOP;
7276   case '1':
7277     return what != BS_START;
7278   case '0':
7279     return false;
7280   }
7281   return vim_strchr(p_bs, what) != NULL;
7282 }
7283 
7284 /// Save the current values of 'fileformat' and 'fileencoding', so that we know
7285 /// the file must be considered changed when the value is different.
save_file_ff(buf_T * buf)7286 void save_file_ff(buf_T *buf)
7287 {
7288   buf->b_start_ffc = *buf->b_p_ff;
7289   buf->b_start_eol = buf->b_p_eol;
7290   buf->b_start_bomb = buf->b_p_bomb;
7291 
7292   // Only use free/alloc when necessary, they take time.
7293   if (buf->b_start_fenc == NULL
7294       || STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
7295     xfree(buf->b_start_fenc);
7296     buf->b_start_fenc = vim_strsave(buf->b_p_fenc);
7297   }
7298 }
7299 
7300 /// Return true if 'fileformat' and/or 'fileencoding' has a different value
7301 /// from when editing started (save_file_ff() called).
7302 /// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
7303 /// changed and 'binary' is not set.
7304 /// Also when 'endofline' was changed and 'fixeol' is not set.
7305 /// When "ignore_empty" is true don't consider a new, empty buffer to be
7306 /// changed.
file_ff_differs(buf_T * buf,bool ignore_empty)7307 bool file_ff_differs(buf_T *buf, bool ignore_empty)
7308   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
7309 {
7310   // In a buffer that was never loaded the options are not valid.
7311   if (buf->b_flags & BF_NEVERLOADED) {
7312     return false;
7313   }
7314   if (ignore_empty
7315       && (buf->b_flags & BF_NEW)
7316       && buf->b_ml.ml_line_count == 1
7317       && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
7318     return false;
7319   }
7320   if (buf->b_start_ffc != *buf->b_p_ff) {
7321     return true;
7322   }
7323   if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
7324     return true;
7325   }
7326   if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
7327     return true;
7328   }
7329   if (buf->b_start_fenc == NULL) {
7330     return *buf->b_p_fenc != NUL;
7331   }
7332   return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
7333 }
7334 
7335 /// return OK if "p" is a valid fileformat name, FAIL otherwise.
check_ff_value(char_u * p)7336 int check_ff_value(char_u *p)
7337 {
7338   return check_opt_strings(p, p_ff_values, false);
7339 }
7340 
7341 // Set the integer values corresponding to the string setting of 'vartabstop'.
7342 // "array" will be set, caller must free it if needed.
tabstop_set(char_u * var,long ** array)7343 bool tabstop_set(char_u *var, long **array)
7344 {
7345   long valcount = 1;
7346   int t;
7347   char_u *cp;
7348 
7349   if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
7350     *array = NULL;
7351     return true;
7352   }
7353 
7354   for (cp = var; *cp != NUL; cp++) {
7355     if (cp == var || cp[-1] == ',') {
7356       char_u *end;
7357 
7358       if (strtol((char *)cp, (char **)&end, 10) <= 0) {
7359         if (cp != end) {
7360           emsg(_(e_positive));
7361         } else {
7362           emsg(_(e_invarg));
7363         }
7364         return false;
7365       }
7366     }
7367 
7368     if (ascii_isdigit(*cp)) {
7369       continue;
7370     }
7371     if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
7372       valcount++;
7373       continue;
7374     }
7375     emsg(_(e_invarg));
7376     return false;
7377   }
7378 
7379   *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
7380   (*array)[0] = valcount;
7381 
7382   t = 1;
7383   for (cp = var; *cp != NUL;) {
7384     (*array)[t++] = atoi((char *)cp);
7385     while (*cp != NUL && *cp != ',') {
7386       cp++;
7387     }
7388     if (*cp != NUL) {
7389       cp++;
7390     }
7391   }
7392 
7393   return true;
7394 }
7395 
7396 // Calculate the number of screen spaces a tab will occupy.
7397 // If "vts" is set then the tab widths are taken from that array,
7398 // otherwise the value of ts is used.
tabstop_padding(colnr_T col,long ts_arg,long * vts)7399 int tabstop_padding(colnr_T col, long ts_arg, long *vts)
7400 {
7401   long ts = ts_arg == 0 ? 8 : ts_arg;
7402   colnr_T tabcol = 0;
7403   int t;
7404   long padding = 0;
7405 
7406   if (vts == NULL || vts[0] == 0) {
7407     return (int)(ts - (col % ts));
7408   }
7409 
7410   const long tabcount = vts[0];
7411 
7412   for (t = 1; t <= tabcount; t++) {
7413     tabcol += (colnr_T)vts[t];
7414     if (tabcol > col) {
7415       padding = tabcol - col;
7416       break;
7417     }
7418   }
7419   if (t > tabcount) {
7420     padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
7421   }
7422 
7423   return (int)padding;
7424 }
7425 
7426 // Find the size of the tab that covers a particular column.
tabstop_at(colnr_T col,long ts,long * vts)7427 int tabstop_at(colnr_T col, long ts, long *vts)
7428 {
7429   colnr_T tabcol = 0;
7430   int t;
7431   long tab_size = 0;
7432 
7433   if (vts == NULL || vts[0] == 0) {
7434     return (int)ts;
7435   }
7436 
7437   const long tabcount = vts[0];
7438   for (t = 1; t <= tabcount; t++) {
7439     tabcol += (colnr_T)vts[t];
7440     if (tabcol > col) {
7441       tab_size = vts[t];
7442       break;
7443     }
7444   }
7445   if (t > tabcount) {
7446     tab_size = vts[tabcount];
7447   }
7448 
7449   return (int)tab_size;
7450 }
7451 
7452 // Find the column on which a tab starts.
tabstop_start(colnr_T col,long ts,long * vts)7453 colnr_T tabstop_start(colnr_T col, long ts, long *vts)
7454 {
7455   colnr_T tabcol = 0;
7456   int t;
7457 
7458   if (vts == NULL || vts[0] == 0) {
7459     return (int)((col / ts) * ts);
7460   }
7461 
7462   const long tabcount = vts[0];
7463   for (t = 1; t <= tabcount; t++) {
7464     tabcol += (colnr_T)vts[t];
7465     if (tabcol > col) {
7466       return (int)(tabcol - vts[t]);
7467     }
7468   }
7469 
7470   const int excess = (int)(tabcol % vts[tabcount]);
7471   return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
7472 }
7473 
7474 // Find the number of tabs and spaces necessary to get from one column
7475 // to another.
tabstop_fromto(colnr_T start_col,colnr_T end_col,long ts_arg,long * vts,int * ntabs,int * nspcs)7476 void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
7477                     int *nspcs)
7478 {
7479   int spaces = end_col - start_col;
7480   colnr_T tabcol = 0;
7481   long padding = 0;
7482   int t;
7483   long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
7484 
7485   if (vts == NULL || vts[0] == 0) {
7486     int tabs = 0;
7487 
7488     const int initspc = (int)(ts - (start_col % ts));
7489     if (spaces >= initspc) {
7490       spaces -= initspc;
7491       tabs++;
7492     }
7493     tabs += (int)(spaces / ts);
7494     spaces -= (int)((spaces / ts) * ts);
7495 
7496     *ntabs = tabs;
7497     *nspcs = spaces;
7498     return;
7499   }
7500 
7501   // Find the padding needed to reach the next tabstop.
7502   const long tabcount = vts[0];
7503   for (t = 1; t <= tabcount; t++) {
7504     tabcol += (colnr_T)vts[t];
7505     if (tabcol > start_col) {
7506       padding = tabcol - start_col;
7507       break;
7508     }
7509   }
7510   if (t > tabcount) {
7511     padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
7512   }
7513 
7514   // If the space needed is less than the padding no tabs can be used.
7515   if (spaces < padding) {
7516     *ntabs = 0;
7517     *nspcs = spaces;
7518     return;
7519   }
7520 
7521   *ntabs = 1;
7522   spaces -= (int)padding;
7523 
7524   // At least one tab has been used. See if any more will fit.
7525   while (spaces != 0 && ++t <= tabcount) {
7526     padding = vts[t];
7527     if (spaces < padding) {
7528       *nspcs = spaces;
7529       return;
7530     }
7531     *ntabs += 1;
7532     spaces -= (int)padding;
7533   }
7534 
7535   *ntabs += spaces / (int)vts[tabcount];
7536   *nspcs = spaces % (int)vts[tabcount];
7537 }
7538 
7539 // See if two tabstop arrays contain the same values.
tabstop_eq(long * ts1,long * ts2)7540 bool tabstop_eq(long *ts1, long *ts2)
7541 {
7542   int t;
7543 
7544   if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
7545     return false;
7546   }
7547   if (ts1 == ts2) {
7548     return true;
7549   }
7550   if (ts1[0] != ts2[0]) {
7551     return false;
7552   }
7553 
7554   for (t = 1; t <= ts1[0]; t++) {
7555     if (ts1[t] != ts2[t]) {
7556       return false;
7557     }
7558   }
7559 
7560   return true;
7561 }
7562 
7563 // Copy a tabstop array, allocating space for the new array.
tabstop_copy(long * oldts)7564 int *tabstop_copy(long *oldts)
7565 {
7566   long *newts;
7567   int t;
7568 
7569   if (oldts == 0) {
7570     return 0;
7571   }
7572 
7573   newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
7574   for (t = 0; t <= oldts[0]; t++) {
7575     newts[t] = oldts[t];
7576   }
7577 
7578   return (int *)newts;
7579 }
7580 
7581 // Return a count of the number of tabstops.
tabstop_count(long * ts)7582 int tabstop_count(long *ts)
7583 {
7584   return ts != NULL ? (int)ts[0] : 0;
7585 }
7586 
7587 // Return the first tabstop, or 8 if there are no tabstops defined.
tabstop_first(long * ts)7588 int tabstop_first(long *ts)
7589 {
7590   return ts != NULL ? (int)ts[1] : 8;
7591 }
7592 
7593 /// Return the effective shiftwidth value for current buffer, using the
7594 /// 'tabstop' value when 'shiftwidth' is zero.
get_sw_value(buf_T * buf)7595 int get_sw_value(buf_T *buf)
7596 {
7597   long result = get_sw_value_col(buf, 0);
7598   assert(result >= 0 && result <= INT_MAX);
7599   return (int)result;
7600 }
7601 
7602 // Idem, using the first non-black in the current line.
get_sw_value_indent(buf_T * buf)7603 long get_sw_value_indent(buf_T *buf)
7604 {
7605   pos_T pos = curwin->w_cursor;
7606 
7607   pos.col = (colnr_T)getwhitecols_curline();
7608   return get_sw_value_pos(buf, &pos);
7609 }
7610 
7611 // Idem, using "pos".
get_sw_value_pos(buf_T * buf,pos_T * pos)7612 long get_sw_value_pos(buf_T *buf, pos_T *pos)
7613 {
7614   pos_T save_cursor = curwin->w_cursor;
7615   long sw_value;
7616 
7617   curwin->w_cursor = *pos;
7618   sw_value = get_sw_value_col(buf, get_nolist_virtcol());
7619   curwin->w_cursor = save_cursor;
7620   return sw_value;
7621 }
7622 
7623 // Idem, using virtual column "col".
get_sw_value_col(buf_T * buf,colnr_T col)7624 long get_sw_value_col(buf_T *buf, colnr_T col)
7625 {
7626   return buf->b_p_sw ? buf->b_p_sw
7627                      : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
7628 }
7629 
7630 /// Return the effective softtabstop value for the current buffer,
7631 /// using the shiftwidth  value when 'softtabstop' is negative.
get_sts_value(void)7632 int get_sts_value(void)
7633 {
7634   long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
7635   assert(result >= 0 && result <= INT_MAX);
7636   return (int)result;
7637 }
7638 
7639 /// This is called when 'breakindentopt' is changed and when a window is
7640 /// initialized
briopt_check(win_T * wp)7641 static bool briopt_check(win_T *wp)
7642 {
7643   int bri_shift = 0;
7644   int bri_min = 20;
7645   bool bri_sbr = false;
7646   int bri_list = 0;
7647 
7648   char_u *p = wp->w_p_briopt;
7649   while (*p != NUL)
7650   {
7651     if (STRNCMP(p, "shift:", 6) == 0
7652         && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
7653       p += 6;
7654       bri_shift = getdigits_int(&p, true, 0);
7655     } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
7656       p += 4;
7657       bri_min = getdigits_int(&p, true, 0);
7658     } else if (STRNCMP(p, "sbr", 3) == 0) {
7659       p += 3;
7660       bri_sbr = true;
7661     } else if (STRNCMP(p, "list:", 5) == 0) {
7662       p += 5;
7663       bri_list = (int)getdigits(&p, false, 0);
7664     }
7665     if (*p != ',' && *p != NUL) {
7666       return false;
7667     }
7668     if (*p == ',') {
7669       p++;
7670     }
7671   }
7672 
7673   wp->w_briopt_shift = bri_shift;
7674   wp->w_briopt_min = bri_min;
7675   wp->w_briopt_sbr = bri_sbr;
7676   wp->w_briopt_list  = bri_list;
7677 
7678   return true;
7679 }
7680 
7681 /// Get the local or global value of 'backupcopy'.
7682 ///
7683 /// @param buf The buffer.
get_bkc_value(buf_T * buf)7684 unsigned int get_bkc_value(buf_T *buf)
7685 {
7686   return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
7687 }
7688 
7689 /// Get the local or global value of 'showbreak'.
7690 ///
7691 /// @param win  If not NULL, the window to get the local option from; global
7692 ///             otherwise.
get_showbreak_value(win_T * const win)7693 char_u *get_showbreak_value(win_T *const win)
7694   FUNC_ATTR_WARN_UNUSED_RESULT
7695 {
7696   if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) {
7697     return p_sbr;
7698   }
7699   if (STRCMP(win->w_p_sbr, "NONE") == 0) {
7700     return empty_option;
7701   }
7702   return win->w_p_sbr;
7703 }
7704 
7705 /// Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC.
get_fileformat(const buf_T * buf)7706 int get_fileformat(const buf_T *buf)
7707   FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
7708 {
7709   int c = *buf->b_p_ff;
7710 
7711   if (buf->b_p_bin || c == 'u') {
7712     return EOL_UNIX;
7713   }
7714   if (c == 'm') {
7715     return EOL_MAC;
7716   }
7717   return EOL_DOS;
7718 }
7719 
7720 /// Like get_fileformat(), but override 'fileformat' with "p" for "++opt=val"
7721 /// argument.
7722 ///
7723 /// @param eap  can be NULL!
get_fileformat_force(const buf_T * buf,const exarg_T * eap)7724 int get_fileformat_force(const buf_T *buf, const exarg_T *eap)
7725   FUNC_ATTR_NONNULL_ARG(1)
7726 {
7727   int c;
7728 
7729   if (eap != NULL && eap->force_ff != 0) {
7730     c = eap->force_ff;
7731   } else {
7732     if ((eap != NULL && eap->force_bin != 0)
7733         ? (eap->force_bin == FORCE_BIN) : buf->b_p_bin) {
7734       return EOL_UNIX;
7735     }
7736     c = *buf->b_p_ff;
7737   }
7738   if (c == 'u') {
7739     return EOL_UNIX;
7740   }
7741   if (c == 'm') {
7742     return EOL_MAC;
7743   }
7744   return EOL_DOS;
7745 }
7746 
7747 /// Return the default fileformat from 'fileformats'.
default_fileformat(void)7748 int default_fileformat(void)
7749 {
7750   switch (*p_ffs) {
7751   case 'm':
7752     return EOL_MAC;
7753   case 'd':
7754     return EOL_DOS;
7755   }
7756   return EOL_UNIX;
7757 }
7758 
7759 /// Set the current end-of-line type to EOL_UNIX, EOL_MAC, or EOL_DOS.
7760 ///
7761 /// Sets 'fileformat'.
7762 ///
7763 /// @param eol_style End-of-line style.
7764 /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
set_fileformat(int eol_style,int opt_flags)7765 void set_fileformat(int eol_style, int opt_flags)
7766 {
7767   char *p = NULL;
7768 
7769   switch (eol_style) {
7770   case EOL_UNIX:
7771     p = FF_UNIX;
7772     break;
7773   case EOL_MAC:
7774     p = FF_MAC;
7775     break;
7776   case EOL_DOS:
7777     p = FF_DOS;
7778     break;
7779   }
7780 
7781   // p is NULL if "eol_style" is EOL_UNKNOWN.
7782   if (p != NULL) {
7783     set_string_option_direct("ff",
7784                              -1,
7785                              (char_u *)p,
7786                              OPT_FREE | opt_flags,
7787                              0);
7788   }
7789 
7790   // This may cause the buffer to become (un)modified.
7791   check_status(curbuf);
7792   redraw_tabline = true;
7793   need_maketitle = true;  // Set window title later.
7794 }
7795 
7796 /// Skip to next part of an option argument: skip space and comma
skip_to_option_part(const char_u * p)7797 char_u *skip_to_option_part(const char_u *p)
7798 {
7799   if (*p == ',') {
7800     p++;
7801   }
7802   while (*p == ' ') {
7803     p++;
7804   }
7805   return (char_u *)p;
7806 }
7807 
7808 /// Isolate one part of a string option separated by `sep_chars`.
7809 ///
7810 /// @param[in,out]  option    advanced to the next part
7811 /// @param[in,out]  buf       copy of the isolated part
7812 /// @param[in]      maxlen    length of `buf`
7813 /// @param[in]      sep_chars chars that separate the option parts
7814 ///
7815 /// @return length of `*option`
copy_option_part(char_u ** option,char_u * buf,size_t maxlen,char * sep_chars)7816 size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_chars)
7817 {
7818   size_t len = 0;
7819   char_u *p = *option;
7820 
7821   // skip '.' at start of option part, for 'suffixes'
7822   if (*p == '.') {
7823     buf[len++] = *p++;
7824   }
7825   while (*p != NUL && vim_strchr((char_u *)sep_chars, *p) == NULL) {
7826     // Skip backslash before a separator character and space.
7827     if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL) {
7828       p++;
7829     }
7830     if (len < maxlen - 1) {
7831       buf[len++] = *p;
7832     }
7833     p++;
7834   }
7835   buf[len] = NUL;
7836 
7837   if (*p != NUL && *p != ',') {  // skip non-standard separator
7838     p++;
7839   }
7840   p = skip_to_option_part(p);    // p points to next file name
7841 
7842   *option = p;
7843   return len;
7844 }
7845 
7846 /// Return true when 'shell' has "csh" in the tail.
csh_like_shell(void)7847 int csh_like_shell(void)
7848 {
7849   return strstr((char *)path_tail(p_sh), "csh") != NULL;
7850 }
7851 
7852 /// Return true when 'shell' has "fish" in the tail.
fish_like_shell(void)7853 bool fish_like_shell(void)
7854 {
7855   return strstr((char *)path_tail(p_sh), "fish") != NULL;
7856 }
7857 
7858 /// Return the number of requested sign columns, based on current
7859 /// buffer signs and on user configuration.
win_signcol_count(win_T * wp)7860 int win_signcol_count(win_T *wp)
7861 {
7862   return win_signcol_configured(wp, NULL);
7863 }
7864 
7865 /// Return the number of requested sign columns, based on user / configuration.
win_signcol_configured(win_T * wp,int * is_fixed)7866 int win_signcol_configured(win_T *wp, int *is_fixed)
7867 {
7868   int minimum = 0, maximum = 1, needed_signcols;
7869   const char *scl = (const char *)wp->w_p_scl;
7870 
7871   if (is_fixed) {
7872     *is_fixed = 1;
7873   }
7874 
7875   // Note: It checks "no" or "number" in 'signcolumn' option
7876   if (*scl == 'n'
7877       && (*(scl + 1) == 'o' || (*(scl + 1) == 'u'
7878                                 && (wp->w_p_nu || wp->w_p_rnu)))) {
7879     return 0;
7880   }
7881   needed_signcols = buf_signcols(wp->w_buffer);
7882 
7883   // yes or yes
7884   if (!strncmp(scl, "yes:", 4)) {
7885     // Fixed amount of columns
7886     return scl[4] - '0';
7887   }
7888   if (*scl == 'y') {
7889     return 1;
7890   }
7891 
7892   if (is_fixed) {
7893     // auto or auto:<NUM>
7894     *is_fixed = 0;
7895   }
7896 
7897   if (!strncmp(scl, "auto:", 5)) {
7898     // Variable depending on a configuration
7899     maximum = scl[5] - '0';
7900     // auto:<NUM>-<NUM>
7901     if (strlen(scl) == 8 && *(scl + 6) == '-') {
7902       minimum = maximum;
7903       maximum = scl[7] - '0';
7904     }
7905   }
7906 
7907   int ret = MAX(minimum, MIN(maximum, needed_signcols));
7908   assert(ret <= SIGN_SHOW_MAX);
7909   return ret;
7910 }
7911 
7912 /// Get window or buffer local options
get_winbuf_options(const int bufopt)7913 dict_T *get_winbuf_options(const int bufopt)
7914   FUNC_ATTR_WARN_UNUSED_RESULT
7915 {
7916   dict_T *const d = tv_dict_alloc();
7917 
7918   for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
7919     struct vimoption *opt = &options[opt_idx];
7920 
7921     if ((bufopt && (opt->indir & PV_BUF))
7922         || (!bufopt && (opt->indir & PV_WIN))) {
7923       char_u *varp = get_varp(opt);
7924 
7925       if (varp != NULL) {
7926         if (opt->flags & P_STRING) {
7927           tv_dict_add_str(d, opt->fullname, strlen(opt->fullname),
7928                           *(const char **)varp);
7929         } else if (opt->flags & P_NUM) {
7930           tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname),
7931                          *(long *)varp);
7932         } else {
7933           tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp);
7934         }
7935       }
7936     }
7937   }
7938 
7939   return d;
7940 }
7941 
7942 /// Return the effective 'scrolloff' value for the current window, using the
7943 /// global value when appropriate.
get_scrolloff_value(win_T * wp)7944 long get_scrolloff_value(win_T *wp)
7945 {
7946   // Disallow scrolloff in terminal-mode. #11915
7947   if (State & TERM_FOCUS) {
7948     return 0;
7949   }
7950   return wp->w_p_so < 0 ? p_so : wp->w_p_so;
7951 }
7952 
7953 /// Return the effective 'sidescrolloff' value for the current window, using the
7954 /// global value when appropriate.
get_sidescrolloff_value(win_T * wp)7955 long get_sidescrolloff_value(win_T *wp)
7956 {
7957   return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso;
7958 }
7959 
get_vimoption(String name,Error * err)7960 Dictionary get_vimoption(String name, Error *err)
7961 {
7962   int opt_idx = findoption_len((const char *)name.data, name.size);
7963   if (opt_idx < 0) {
7964     api_set_error(err, kErrorTypeValidation, "no such option: '%s'", name.data);
7965     return (Dictionary)ARRAY_DICT_INIT;
7966   }
7967   return vimoption2dict(&options[opt_idx]);
7968 }
7969 
get_all_vimoptions(void)7970 Dictionary get_all_vimoptions(void)
7971 {
7972   Dictionary retval = ARRAY_DICT_INIT;
7973   for (size_t i = 0; options[i].fullname != NULL; i++) {
7974     Dictionary opt_dict = vimoption2dict(&options[i]);
7975     PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict));
7976   }
7977   return retval;
7978 }
7979 
vimoption2dict(vimoption_T * opt)7980 static Dictionary vimoption2dict(vimoption_T *opt)
7981 {
7982   Dictionary dict = ARRAY_DICT_INIT;
7983 
7984   PUT(dict, "name", CSTR_TO_OBJ(opt->fullname));
7985   PUT(dict, "shortname", CSTR_TO_OBJ(opt->shortname));
7986 
7987   const char *scope;
7988   if (opt->indir & PV_BUF) {
7989     scope = "buf";
7990   } else if (opt->indir & PV_WIN) {
7991     scope = "win";
7992   } else {
7993     scope = "global";
7994   }
7995 
7996   PUT(dict, "scope", CSTR_TO_OBJ(scope));
7997 
7998   // welcome to the jungle
7999   PUT(dict, "global_local", BOOL(opt->indir & PV_BOTH));
8000   PUT(dict, "commalist", BOOL(opt->flags & P_COMMA));
8001   PUT(dict, "flaglist", BOOL(opt->flags & P_FLAGLIST));
8002 
8003   PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET));
8004 
8005   PUT(dict, "last_set_sid", INTEGER_OBJ(opt->last_set.script_ctx.sc_sid));
8006   PUT(dict, "last_set_linenr", INTEGER_OBJ(opt->last_set.script_ctx.sc_lnum));
8007   PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)opt->last_set.channel_id));
8008 
8009   const char *type;
8010   Object def;
8011   // TODO(bfredl): do you even nocp?
8012   char_u *def_val = opt->def_val;
8013   if (opt->flags & P_STRING) {
8014     type = "string";
8015     def = CSTR_TO_OBJ(def_val ? (char *)def_val : "");
8016   } else if (opt->flags & P_NUM) {
8017     type = "number";
8018     def = INTEGER_OBJ((Integer)(intptr_t)def_val);
8019   } else if (opt->flags & P_BOOL) {
8020     type = "boolean";
8021     def = BOOL((intptr_t)def_val);
8022   } else {
8023     type = ""; def = NIL;
8024   }
8025   PUT(dict, "type", CSTR_TO_OBJ(type));
8026   PUT(dict, "default", def);
8027   PUT(dict, "allows_duplicates", BOOL(!(opt->flags & P_NODUP)));
8028 
8029   return dict;
8030 }
8031