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