Merge #10341 from jamessan/modeline-backports

Modeline fix backports
This commit is contained in:
Justin M. Keyes 2019-06-26 21:02:24 +02:00 committed by GitHub
commit 5b47e4dc38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 443 additions and 80 deletions

View File

@ -478,14 +478,17 @@ backslash in front of the ':' will be removed. Example:
/* vi:set dir=c\:\tmp: */ ~
This sets the 'dir' option to "c:\tmp". Only a single backslash before the
':' is removed. Thus to include "\:" you have to specify "\\:".
*E992*
No other commands than "set" are supported, for security reasons (somebody
might create a Trojan horse text file with modelines). And not all options
can be set. For some options a flag is set, so that when it's used the
|sandbox| is effective. Still, there is always a small risk that a modeline
causes trouble. E.g., when some joker sets 'textwidth' to 5 all your lines
are wrapped unexpectedly. So disable modelines before editing untrusted text.
The mail ftplugin does this, for example.
can be set. For some options a flag is set, so that when the value is used
the |sandbox| is effective. Some options can only be set from the modeline
when 'modelineexpr' is set (the default is off).
Still, there is always a small risk that a modeline causes trouble. E.g.,
when some joker sets 'textwidth' to 5 all your lines are wrapped unexpectedly.
So disable modelines before editing untrusted text. The mail ftplugin does
this, for example.
Hint: If you would like to do something else than setting an option, you could
define an autocommand that checks the file for a specific string. For
@ -2439,7 +2442,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The expression will be evaluated in the |sandbox| if set from a
modeline, see |sandbox-option|.
This option can't be set from a |modeline| when the 'diff' option is
on.
on or the 'modelineexpr' option is off.
It is not allowed to change text or jump to another window while
evaluating 'foldexpr' |textlock|.
@ -2554,6 +2557,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The expression will be evaluated in the |sandbox| if set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
It is not allowed to change text or jump to another window while
evaluating 'foldtext' |textlock|.
@ -2589,16 +2593,8 @@ A jump table for the options with a short description can be found at |Q_op|.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|. That stops the option from working,
since changing the buffer text is not allowed.
*'formatoptions'* *'fo'*
'formatoptions' 'fo' string (default: "tcqj", Vi default: "vt")
local to buffer
This is a sequence of letters which describes how automatic
formatting is to be done. See |fo-table|. When the 'paste' option is
on, no formatting is done (like 'formatoptions' is empty). Commas can
be inserted for readability.
To avoid problems with flags that are added in the future, use the
"+=" and "-=" feature of ":set" |add-option-flags|.
This option cannot be set in a modeline when 'modelineexpr' is off.
NOTE: This option is set to "" when 'compatible' is set.
*'formatlistpat'* *'flp'*
'formatlistpat' 'flp' string (default: "^\s*\d\+[\]:.)}\t ]\s*")
@ -2613,6 +2609,16 @@ A jump table for the options with a short description can be found at |Q_op|.
The default recognizes a number, followed by an optional punctuation
character and white space.
*'formatoptions'* *'fo'*
'formatoptions' 'fo' string (default: "tcqj", Vi default: "vt")
local to buffer
This is a sequence of letters which describes how automatic
formatting is to be done. See |fo-table|. When the 'paste' option is
on, no formatting is done (like 'formatoptions' is empty). Commas can
be inserted for readability.
To avoid problems with flags that are added in the future, use the
"+=" and "-=" feature of ":set" |add-option-flags|.
*'formatprg'* *'fp'*
'formatprg' 'fp' string (default "")
global or local to buffer |global-local|
@ -2643,6 +2649,9 @@ A jump table for the options with a short description can be found at |Q_op|.
- system signals low battery life
- Nvim exits abnormally
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
*'gdefault'* *'gd'* *'nogdefault'* *'nogd'*
'gdefault' 'gd' boolean (default off)
global
@ -2978,6 +2987,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'guitabtooltip' is used for the tooltip, see below.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
Only used when the GUI tab pages line is displayed. 'e' must be
present in 'guioptions'. For the non-GUI tab pages line 'tabline' is
@ -3106,6 +3116,7 @@ A jump table for the options with a short description can be found at |Q_op|.
When this option contains printf-style '%' items, they will be
expanded according to the rules used for 'statusline'. See
'titlestring' for example settings.
This option cannot be set in a modeline when 'modelineexpr' is off.
*'ignorecase'* *'ic'* *'noignorecase'* *'noic'*
'ignorecase' 'ic' boolean (default off)
@ -3209,6 +3220,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
It is not allowed to change text or jump to another window while
evaluating 'includeexpr' |textlock|.
@ -3277,6 +3289,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
It is not allowed to change text or jump to another window while
evaluating 'indentexpr' |textlock|.
@ -3879,10 +3892,23 @@ A jump table for the options with a short description can be found at |Q_op|.
< If you have less than 512 Mbyte |:mkspell| may fail for some
languages, no matter what you set 'mkspellmem' to.
This option cannot be set from a |modeline| or in the |sandbox|.
*'modeline'* *'ml'* *'nomodeline'* *'noml'*
'modeline' 'ml' boolean (Vim default: on (off for root),
Vi default: off)
local to buffer
If 'modeline' is on 'modelines' gives the number of lines that is
checked for set commands. If 'modeline' is off or 'modelines' is zero
no lines are checked. See |modeline|.
*'modelineexpr'* *'mle'* *'nomodelineexpr'* *'nomle'*
'modelineexpr' 'mle' boolean (default: off)
global
When on allow some options that are an expression to be set in the
modeline. Check the option for whether it is affected by
'modelineexpr'. Also see |modeline|.
*'modelines'* *'mls'*
'modelines' 'mls' number (default 5)
global
@ -4622,6 +4648,8 @@ A jump table for the options with a short description can be found at |Q_op|.
When this option is not empty, it determines the content of the ruler
string, as displayed for the 'ruler' option.
The format of this option is like that of 'statusline'.
This option cannot be set in a modeline when 'modelineexpr' is off.
The default ruler width is 17 characters. To make the ruler 15
characters wide, put "%15(" at the start and "%)" at the end.
Example: >
@ -5252,7 +5280,8 @@ A jump table for the options with a short description can be found at |Q_op|.
"Pattern not found", "Back at original", etc.
q use "recording" instead of "recording @a"
F don't give the file info when editing a file, like `:silent`
was used for the command
was used for the command; note that this also affects messages
from autocommands
This gives you the opportunity to avoid that a change between buffers
requires you to hit <Enter>, but still gives as useful a message as
@ -5527,7 +5556,7 @@ A jump table for the options with a short description can be found at |Q_op|.
After this option has been set successfully, Vim will source the files
"spell/LANG.vim" in 'runtimepath'. "LANG" is the value of 'spelllang'
up to the first comma, dot or underscore.
up to the first character that is not an ASCII letter and not a dash.
Also see |set-spc-auto|.
@ -5773,6 +5802,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'statusline' option will be evaluated in the |sandbox| if set from
a modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
It is not allowed to change text or jump to another window while
evaluating 'statusline' |textlock|.
@ -5927,6 +5957,8 @@ A jump table for the options with a short description can be found at |Q_op|.
the text to be displayed. Use "%1T" for the first label, "%2T" for
the second one, etc. Use "%X" items for closing labels.
This option cannot be set in a modeline when 'modelineexpr' is off.
Keep in mind that only one of the tab pages is the current one, others
are invisible and you can't jump to their windows.
@ -6203,8 +6235,11 @@ A jump table for the options with a short description can be found at |Q_op|.
global
When this option is not empty, it will be used for the title of the
window. This happens only when the 'title' option is on.
When this option contains printf-style '%' items, they will be
expanded according to the rules used for 'statusline'.
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >
:auto BufEnter * let &titlestring = hostname() . "/" . expand("%:p")
:set title titlestring=%<%F%=%l/%L-%P titlelen=70
@ -6238,6 +6273,8 @@ A jump table for the options with a short description can be found at |Q_op|.
undo file that exists is used. When it cannot be read an error is
given, no further entry is used.
See |undo-persistence|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
*'undofile'* *'noundofile'* *'udf'* *'noudf'*
'undofile' 'udf' boolean (default off)

View File

@ -4924,9 +4924,15 @@ chk_modeline (
*e = NUL; /* truncate the set command */
if (*s != NUL) { /* skip over an empty "::" */
const int secure_save = secure;
save_SID = current_SID;
current_SID = SID_MODELINE;
// Make sure no risky things are executed as a side effect.
secure = 1;
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
secure = secure_save;
current_SID = save_SID;
if (retval == FAIL) /* stop if error found */
break;

View File

@ -241,13 +241,14 @@ typedef enum {
///< the value (prevents error message).
} GetLvalFlags;
// function flags
// flags used in uf_flags
#define FC_ABORT 0x01 // abort function on error
#define FC_RANGE 0x02 // function accepts range
#define FC_DICT 0x04 // Dict function, uses "self"
#define FC_CLOSURE 0x08 // closure, uses outer scope variables
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
#define FC_SANDBOX 0x40 // function defined in the sandbox
// The names of packages that once were loaded are remembered.
static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL };
@ -5853,6 +5854,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
if (prof_def_func()) {
func_do_profile(fp);
}
if (sandbox) {
flags |= FC_SANDBOX;
}
fp->uf_varargs = true;
fp->uf_flags = flags;
fp->uf_calls = 0;
@ -20315,6 +20319,9 @@ void ex_function(exarg_T *eap)
if (prof_def_func())
func_do_profile(fp);
fp->uf_varargs = varargs;
if (sandbox) {
flags |= FC_SANDBOX;
}
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
@ -21305,6 +21312,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
scid_T save_current_SID;
bool using_sandbox = false;
funccall_T *fc;
int save_did_emsg;
static int depth = 0;
@ -21462,6 +21470,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
save_sourcing_name = sourcing_name;
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 1;
if (fp->uf_flags & FC_SANDBOX) {
using_sandbox = true;
sandbox++;
}
// need space for new sourcing_name:
// * save_sourcing_name
// * "["number"].." or "function "
@ -21622,6 +21636,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (do_profiling_yes) {
script_prof_restore(&wait_start);
}
if (using_sandbox) {
sandbox--;
}
if (p_verbose >= 12 && sourcing_name != NULL) {
++no_wait_return;

View File

@ -1004,7 +1004,7 @@ return {
},
{
command='function',
flags=bit.bor(EXTRA, BANG, CMDWIN),
flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN),
addr_type=ADDR_LINES,
func='ex_function',
},

View File

@ -79,6 +79,7 @@ local get_flags = function(o)
{'pri_mkrc'},
{'deny_in_modelines', 'P_NO_ML'},
{'deny_duplicates', 'P_NODUP'},
{'modelineexpr', 'P_MLE'},
}) do
local key_name = flag_desc[1]
local def_name = flag_desc[2] or ('P_' .. key_name:upper())

View File

@ -251,6 +251,7 @@ typedef struct vimoption {
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed
#define P_UI_OPTION 0x40000000U ///< send option to remote ui
#define P_MLE 0x80000000U ///< under control of 'modelineexpr'
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
@ -1209,7 +1210,7 @@ do_set (
}
len++;
if (opt_idx == -1) {
key = find_key_option(arg + 1);
key = find_key_option(arg + 1, true);
}
} else {
len = 0;
@ -1223,7 +1224,7 @@ do_set (
}
opt_idx = findoption_len((const char *)arg, (size_t)len);
if (opt_idx == -1) {
key = find_key_option(arg);
key = find_key_option(arg, false);
}
}
@ -1290,6 +1291,11 @@ do_set (
errmsg = (char_u *)_("E520: Not allowed in a modeline");
goto skip;
}
if ((flags & P_MLE) && !p_mle) {
errmsg = (char_u *)_(
"E992: Not allowed in a modeline when 'modelineexpr' is off");
goto skip;
}
/* In diff mode some options are overruled. This avoids that
* 'foldmethod' becomes "marker" instead of "diff" and that
* "wrap" gets set. */
@ -1364,6 +1370,10 @@ do_set (
&& nextchar != NUL && !ascii_iswhite(afterchar))
errmsg = e_trailing;
} else {
int value_is_replaced = !prepending && !adding && !removing;
int value_checked = false;
if (flags & P_BOOL) { /* boolean */
if (nextchar == '=' || nextchar == ':') {
errmsg = e_invarg;
@ -1783,12 +1793,32 @@ do_set (
// buffer is closed by autocommands.
saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0;
// Handle side effects, and set the global value for
// ":set" on local options. Note: when setting 'syntax'
// or 'filetype' autocommands may be triggered that can
// cause havoc.
errmsg = did_set_string_option(opt_idx, (char_u **)varp,
new_value_alloced, oldval, errbuf, opt_flags);
{
uint32_t *p = insecure_flag(opt_idx, opt_flags);
const int secure_saved = secure;
// When an option is set in the sandbox, from a
// modeline or in secure mode, then deal with side
// effects in secure mode. Also when the value was
// set with the P_INSECURE flag and is not
// completely replaced.
if ((opt_flags & OPT_MODELINE)
|| sandbox != 0
|| (!value_is_replaced && (*p & P_INSECURE))) {
secure = 1;
}
// Handle side effects, and set the global value
// for ":set" on local options. Note: when setting
// 'syntax' or 'filetype' autocommands may be
// triggered that can cause havoc.
errmsg = did_set_string_option(opt_idx, (char_u **)varp,
new_value_alloced, oldval,
errbuf, sizeof(errbuf),
opt_flags, &value_checked);
secure = secure_saved;
}
if (errmsg == NULL) {
if (!starting) {
@ -1815,8 +1845,7 @@ do_set (
}
if (opt_idx >= 0)
did_set_option(opt_idx, opt_flags,
!prepending && !adding && !removing);
did_set_option(opt_idx, opt_flags, value_is_replaced, value_checked);
}
skip:
@ -1881,7 +1910,9 @@ static void
did_set_option (
int opt_idx,
int opt_flags, /* possibly with OPT_MODELINE */
int new_value /* value was replaced completely */
int new_value, /* value was replaced completely */
int value_checked /* value was checked to be safe, no need to
set P_INSECURE */
)
{
options[opt_idx].flags |= P_WAS_SET;
@ -1890,20 +1921,22 @@ did_set_option (
* set the P_INSECURE flag. Otherwise, if a new value is stored reset the
* flag. */
uint32_t *p = insecure_flag(opt_idx, opt_flags);
if (secure
|| sandbox != 0
|| (opt_flags & OPT_MODELINE))
if (!value_checked && (secure
|| sandbox != 0
|| (opt_flags & OPT_MODELINE))) {
*p = *p | P_INSECURE;
else if (new_value)
} else if (new_value) {
*p = *p & ~P_INSECURE;
}
}
static char_u *illegal_char(char_u *errbuf, int c)
static char_u *illegal_char(char_u *errbuf, size_t errbuflen, int c)
{
if (errbuf == NULL)
if (errbuf == NULL) {
return (char_u *)"";
sprintf((char *)errbuf, _("E539: Illegal character <%s>"),
(char *)transchar(c));
}
vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"),
(char *)transchar(c));
return errbuf;
}
@ -1913,10 +1946,12 @@ static char_u *illegal_char(char_u *errbuf, int c)
*/
static int string_to_key(char_u *arg)
{
if (*arg == '<')
return find_key_option(arg + 1);
if (*arg == '^')
if (*arg == '<') {
return find_key_option(arg + 1, true);
}
if (*arg == '^') {
return Ctrl_chr(arg[1]);
}
return *arg;
}
@ -2392,10 +2427,12 @@ static char *set_string_option(const int opt_idx, const char *const value,
char *const saved_oldval = xstrdup(oldval);
char *const saved_newval = xstrdup(s);
int value_checked = false;
char *const r = (char *)did_set_string_option(
opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags);
opt_idx, (char_u **)varp, (int)true, (char_u *)oldval,
NULL, 0, opt_flags, &value_checked);
if (r == NULL) {
did_set_option(opt_idx, opt_flags, true);
did_set_option(opt_idx, opt_flags, true, value_checked);
}
// call autocommand after handling side effects
@ -2431,13 +2468,16 @@ static bool valid_filetype(char_u *val)
* Returns NULL for success, or an error message for an error.
*/
static char_u *
did_set_string_option (
int opt_idx, /* index in options[] table */
char_u **varp, /* pointer to the option variable */
int new_value_alloced, /* new value was allocated */
char_u *oldval, /* previous value of the option */
char_u *errbuf, /* buffer for errors, or NULL */
int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
did_set_string_option(
int opt_idx, // index in options[] table
char_u **varp, // pointer to the option variable
int new_value_alloced, // new value was allocated
char_u *oldval, // previous value of the option
char_u *errbuf, // buffer for errors, or NULL
size_t errbuflen, // length of errors buffer
int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
int *value_checked // value was checked to be safe, no
// need to set P_INSECURE
)
{
char_u *errmsg = NULL;
@ -2655,8 +2695,20 @@ did_set_string_option (
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
int secure_save = secure;
// Reset the secure flag, since the value of 'keymap' has
// been checked to be safe.
secure = 0;
// load or unload key mapping tables
errmsg = keymap_init();
secure = secure_save;
// Since we check the value, there is no need to set P_INSECURE,
// even when the value comes from a modeline.
*value_checked = true;
}
if (errmsg == NULL) {
@ -2742,7 +2794,7 @@ did_set_string_option (
while (*s && *s != ':') {
if (vim_strchr((char_u *)COM_ALL, *s) == NULL
&& !ascii_isdigit(*s) && *s != '-') {
errmsg = illegal_char(errbuf, *s);
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
++s;
@ -2794,7 +2846,7 @@ did_set_string_option (
for (s = p_shada; *s; ) {
/* Check it's a valid character */
if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
errmsg = illegal_char(errbuf, *s);
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
if (*s == 'n') { /* name is always last one */
@ -2814,9 +2866,9 @@ did_set_string_option (
if (!ascii_isdigit(*(s - 1))) {
if (errbuf != NULL) {
sprintf((char *)errbuf,
_("E526: Missing number after <%s>"),
transchar_byte(*(s - 1)));
vim_snprintf((char *)errbuf, errbuflen,
_("E526: Missing number after <%s>"),
transchar_byte(*(s - 1)));
errmsg = errbuf;
} else
errmsg = (char_u *)"";
@ -2994,7 +3046,7 @@ did_set_string_option (
if (!*s)
break;
if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) {
errmsg = illegal_char(errbuf, *s);
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
if (*++s != NUL && *s != ',' && *s != ' ') {
@ -3008,9 +3060,9 @@ did_set_string_option (
}
} else {
if (errbuf != NULL) {
sprintf((char *)errbuf,
_("E535: Illegal character after <%c>"),
*--s);
vim_snprintf((char *)errbuf, errbuflen,
_("E535: Illegal character after <%c>"),
*--s);
errmsg = errbuf;
} else
errmsg = (char_u *)"";
@ -3167,12 +3219,20 @@ did_set_string_option (
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
// Since we check the value, there is no need to set P_INSECURE,
// even when the value comes from a modeline.
*value_checked = true;
}
} else if (gvarp == &p_syn) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
// Since we check the value, there is no need to set P_INSECURE,
// even when the value comes from a modeline.
*value_checked = true;
}
} else if (varp == &curwin->w_p_winhl) {
if (!parse_winhl_opt(curwin)) {
@ -3198,7 +3258,7 @@ did_set_string_option (
if (p != NULL) {
for (s = *varp; *s; ++s)
if (vim_strchr(p, *s) == NULL) {
errmsg = illegal_char(errbuf, *s);
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
}
@ -3262,6 +3322,11 @@ did_set_string_option (
// already set to this value.
if (!(opt_flags & OPT_MODELINE) || value_changed) {
static int ft_recursive = 0;
int secure_save = secure;
// Reset the secure flag, since the value of 'filetype' has
// been checked to be safe.
secure = 0;
ft_recursive++;
did_filetype = true;
@ -3274,6 +3339,7 @@ did_set_string_option (
if (varp != &(curbuf->b_p_ft)) {
varp = NULL;
}
secure = secure_save;
}
}
if (varp == &(curwin->w_s->b_p_spl)) {
@ -3291,11 +3357,13 @@ did_set_string_option (
* '.encoding'.
*/
for (p = q; *p != NUL; ++p)
if (vim_strchr((char_u *)"_.,", *p) != NULL)
if (!ASCII_ISALNUM(*p) && *p != '-')
break;
vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
(int)(p - q), q);
source_runtime(fname, DIP_ALL);
if (p > q) {
vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
(int)(p - q), q);
source_runtime(fname, DIP_ALL);
}
}
}
@ -3554,7 +3622,7 @@ char_u *check_stl_option(char_u *s)
continue;
}
if (vim_strchr(STL_ALL, *s) == NULL) {
return illegal_char(errbuf, *s);
return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
s++;
@ -4893,19 +4961,20 @@ char *set_option_value(const char *const name, const long number,
return NULL;
}
/*
* Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
*/
int find_key_option_len(const char_u *arg, size_t len)
// Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
// When "has_lt" is true there is a '<' before "*arg_arg".
// Returns 0 when the key is not recognized.
int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
{
int key;
int key = 0;
int modifiers;
const char_u *arg = arg_arg;
// Don't use get_special_key_code() for t_xx, we don't want it to call
// add_termcap_entry().
if (len >= 4 && arg[0] == 't' && arg[1] == '_') {
key = TERMCAP2KEY(arg[2], arg[3]);
} else {
} else if (has_lt) {
arg--; // put arg at the '<'
modifiers = 0;
key = find_special_key(&arg, len + 1, &modifiers, true, true, false);
@ -4916,9 +4985,9 @@ int find_key_option_len(const char_u *arg, size_t len)
return key;
}
static int find_key_option(const char_u *arg)
static int find_key_option(const char_u *arg, bool has_lt)
{
return find_key_option_len(arg, STRLEN(arg));
return find_key_option_len(arg, STRLEN(arg), has_lt);
}
/*

View File

@ -495,6 +495,7 @@ EXTERN long p_mmd; // 'maxmapdepth'
EXTERN long p_mmp; // 'maxmempattern'
EXTERN long p_mis; // 'menuitems'
EXTERN char_u *p_msm; // 'mkspellmem'
EXTERN long p_mle; // 'modelineexpr'
EXTERN long p_mls; // 'modelines'
EXTERN char_u *p_mouse; // 'mouse'
EXTERN char_u *p_mousem; // 'mousemodel'

View File

@ -8,6 +8,7 @@
-- defaults={condition=nil, if_true={vi=224, vim=0}, if_false=nil},
-- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil,
-- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil,
-- modelineexpr=nil,
-- expand=nil, nodefault=nil, no_mkrc=nil, vi_def=true, vim=true,
-- alloced=nil,
-- save_pv_indir=nil,
@ -286,6 +287,7 @@ return {
deny_duplicates=true,
vi_def=true,
expand=true,
secure=true,
varname='p_cdpath',
defaults={if_true={vi=",,"}}
},
@ -856,6 +858,7 @@ return {
type='string', scope={'window'},
vi_def=true,
vim=true,
modelineexpr=true,
alloced=true,
redraw={'current_window'},
defaults={if_true={vi="0"}}
@ -931,6 +934,7 @@ return {
type='string', scope={'window'},
vi_def=true,
vim=true,
modelineexpr=true,
alloced=true,
redraw={'current_window'},
defaults={if_true={vi="foldtext()"}}
@ -940,6 +944,7 @@ return {
type='string', scope={'buffer'},
vi_def=true,
vim=true,
modelineexpr=true,
alloced=true,
varname='p_fex',
defaults={if_true={vi=""}}
@ -1053,6 +1058,7 @@ return {
full_name='guitablabel', abbreviation='gtl',
type='string', scope={'global'},
vi_def=true,
modelineexpr=true,
redraw={'current_window'},
enable_if=false,
},
@ -1143,6 +1149,7 @@ return {
full_name='iconstring',
type='string', scope={'global'},
vi_def=true,
modelineexpr=true,
varname='p_iconstring',
defaults={if_true={vi=""}}
},
@ -1209,6 +1216,7 @@ return {
full_name='includeexpr', abbreviation='inex',
type='string', scope={'buffer'},
vi_def=true,
modelineexpr=true,
alloced=true,
varname='p_inex',
defaults={if_true={vi=""}}
@ -1225,6 +1233,7 @@ return {
type='string', scope={'buffer'},
vi_def=true,
vim=true,
modelineexpr=true,
alloced=true,
varname='p_inde',
defaults={if_true={vi=""}}
@ -1538,6 +1547,14 @@ return {
varname='p_ml',
defaults={if_true={vi=false, vim=true}}
},
{
full_name='modelineexpr', abbreviation='mle',
type='bool', scope={'global'},
vi_def=true,
secure=true,
varname='p_mle',
defaults={if_true={vi=false}}
},
{
full_name='modelines', abbreviation='mls',
type='number', scope={'global'},
@ -1898,6 +1915,7 @@ return {
type='string', scope={'global'},
vi_def=true,
alloced=true,
modelineexpr=true,
redraw={'statuslines'},
varname='p_ruf',
defaults={if_true={vi=""}}
@ -2293,6 +2311,7 @@ return {
type='string', scope={'global', 'window'},
vi_def=true,
alloced=true,
modelineexpr=true,
redraw={'statuslines'},
varname='p_stl',
defaults={if_true={vi=""}}
@ -2352,6 +2371,7 @@ return {
full_name='tabline', abbreviation='tal',
type='string', scope={'global'},
vi_def=true,
modelineexpr=true,
redraw={'all_windows'},
varname='p_tal',
defaults={if_true={vi=""}}
@ -2511,6 +2531,7 @@ return {
full_name='titlestring',
type='string', scope={'global'},
vi_def=true,
modelineexpr=true,
varname='p_titlestring',
defaults={if_true={vi=""}}
},

View File

@ -1,6 +1,6 @@
" Vim script language tests
" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>
" Last Change: 2016 Feb 07
" Last Change: 2019 May 24
"-------------------------------------------------------------------------------
" Test environment {{{1
@ -9005,5 +9005,4 @@ Xcheck 50443995
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=4 tw=80 fdm=marker
" vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "")
"-------------------------------------------------------------------------------

View File

@ -28,6 +28,7 @@ source test_lambda.vim
source test_mapping.vim
source test_menu.vim
source test_messages.vim
source test_modeline.vim
source test_move.vim
source test_partial.vim
source test_popup.vim

View File

@ -652,6 +652,29 @@ func Test_OptionSet_diffmode_close()
"delfunc! AutoCommandOptionSet
endfunc
func Test_OptionSet_modeline()
throw 'skipped: Nvim does not support test_override()'
call test_override('starting', 1)
au! OptionSet
augroup set_tabstop
au OptionSet tabstop call timer_start(1, {-> execute("echo 'Handler called'", "")})
augroup END
call writefile(['vim: set ts=7 sw=5 :', 'something'], 'XoptionsetModeline')
set modeline
let v:errmsg = ''
call assert_fails('split XoptionsetModeline', 'E12:')
call assert_equal(7, &ts)
call assert_equal('', v:errmsg)
augroup set_tabstop
au!
augroup END
bwipe!
set ts&
call delete('XoptionsetModeline')
call test_override('starting', 0)
endfunc
" Test for Bufleave autocommand that deletes the buffer we are about to edit.
func Test_BufleaveWithDelete()
new | edit Xfile1

View File

@ -1037,3 +1037,19 @@ func Test_func_range_with_edit()
call delete('Xfuncrange2')
bwipe!
endfunc
sandbox function Fsandbox()
normal ix
endfunc
func Test_func_sandbox()
sandbox let F = {-> 'hello'}
call assert_equal('hello', F())
sandbox let F = {-> execute("normal ix\<Esc>")}
call assert_fails('call F()', 'E48:')
unlet F
call assert_fails('call Fsandbox()', 'E48:')
delfunc Fsandbox
endfunc

View File

@ -1,12 +1,12 @@
" Test glob2regpat()
func Test_invalid()
func Test_glob2regpat_invalid()
call assert_fails('call glob2regpat(1.33)', 'E806:')
call assert_fails('call glob2regpat("}")', 'E219:')
call assert_fails('call glob2regpat("{")', 'E220:')
endfunc
func Test_valid()
func Test_glob2regpat_valid()
call assert_equal('^foo\.', glob2regpat('foo.*'))
call assert_equal('^foo.$', glob2regpat('foo?'))
call assert_equal('\.vim$', glob2regpat('*.vim'))

View File

@ -0,0 +1,173 @@
" Tests for parsing the modeline.
func Test_modeline_invalid()
" This was reading allocated memory in the past.
call writefile(['vi:0', 'nothing'], 'Xmodeline')
let modeline = &modeline
set modeline
call assert_fails('set Xmodeline', 'E518:')
let &modeline = modeline
bwipe!
call delete('Xmodeline')
endfunc
func Test_modeline_filetype()
call writefile(['vim: set ft=c :', 'nothing'], 'Xmodeline_filetype')
let modeline = &modeline
set modeline
filetype plugin on
split Xmodeline_filetype
call assert_equal("c", &filetype)
call assert_equal(1, b:did_ftplugin)
call assert_equal("ccomplete#Complete", &ofu)
bwipe!
call delete('Xmodeline_filetype')
let &modeline = modeline
filetype plugin off
endfunc
func Test_modeline_syntax()
call writefile(['vim: set syn=c :', 'nothing'], 'Xmodeline_syntax')
let modeline = &modeline
set modeline
syntax enable
split Xmodeline_syntax
call assert_equal("c", &syntax)
call assert_equal("c", b:current_syntax)
bwipe!
call delete('Xmodeline_syntax')
let &modeline = modeline
syntax off
endfunc
func Test_modeline_keymap()
if !has('keymap')
return
endif
call writefile(['vim: set keymap=greek :', 'nothing'], 'Xmodeline_keymap')
let modeline = &modeline
set modeline
split Xmodeline_keymap
call assert_equal("greek", &keymap)
call assert_match('greek\|grk', b:keymap_name)
bwipe!
call delete('Xmodeline_keymap')
let &modeline = modeline
set keymap= iminsert=0 imsearch=-1
endfunc
func s:modeline_fails(what, text, error)
if !exists('+' . a:what)
return
endif
let fname = "Xmodeline_fails_" . a:what
call writefile(['vim: set ' . a:text . ' :', 'nothing'], fname)
let modeline = &modeline
set modeline
filetype plugin on
syntax enable
call assert_fails('split ' . fname, a:error)
call assert_equal("", &filetype)
call assert_equal("", &syntax)
bwipe!
call delete(fname)
let &modeline = modeline
filetype plugin off
syntax off
endfunc
func Test_modeline_filetype_fails()
call s:modeline_fails('filetype', 'ft=evil$CMD', 'E474:')
endfunc
func Test_modeline_syntax_fails()
call s:modeline_fails('syntax', 'syn=evil$CMD', 'E474:')
endfunc
func Test_modeline_keymap_fails()
call s:modeline_fails('keymap', 'keymap=evil$CMD', 'E474:')
endfunc
func Test_modeline_fails_always()
call s:modeline_fails('backupdir', 'backupdir=Something()', 'E520:')
call s:modeline_fails('cdpath', 'cdpath=Something()', 'E520:')
call s:modeline_fails('charconvert', 'charconvert=Something()', 'E520:')
call s:modeline_fails('completefunc', 'completefunc=Something()', 'E520:')
call s:modeline_fails('cscopeprg', 'cscopeprg=Something()', 'E520:')
call s:modeline_fails('diffexpr', 'diffexpr=Something()', 'E520:')
call s:modeline_fails('directory', 'directory=Something()', 'E520:')
call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
call s:modeline_fails('helpfile', 'helpfile=Something()', 'E520:')
call s:modeline_fails('imactivatefunc', 'imactivatefunc=Something()', 'E520:')
call s:modeline_fails('imstatusfunc', 'imstatusfunc=Something()', 'E520:')
call s:modeline_fails('imstyle', 'imstyle=Something()', 'E520:')
call s:modeline_fails('keywordprg', 'keywordprg=Something()', 'E520:')
call s:modeline_fails('langmap', 'langmap=Something()', 'E520:')
call s:modeline_fails('luadll', 'luadll=Something()', 'E520:')
call s:modeline_fails('makeef', 'makeef=Something()', 'E520:')
call s:modeline_fails('makeprg', 'makeprg=Something()', 'E520:')
call s:modeline_fails('mkspellmem', 'mkspellmem=Something()', 'E520:')
call s:modeline_fails('mzschemedll', 'mzschemedll=Something()', 'E520:')
call s:modeline_fails('mzschemegcdll', 'mzschemegcdll=Something()', 'E520:')
call s:modeline_fails('modelineexpr', 'modelineexpr=Something()', 'E520:')
call s:modeline_fails('omnifunc', 'omnifunc=Something()', 'E520:')
call s:modeline_fails('operatorfunc', 'operatorfunc=Something()', 'E520:')
call s:modeline_fails('perldll', 'perldll=Something()', 'E520:')
call s:modeline_fails('printdevice', 'printdevice=Something()', 'E520:')
call s:modeline_fails('patchexpr', 'patchexpr=Something()', 'E520:')
call s:modeline_fails('printexpr', 'printexpr=Something()', 'E520:')
call s:modeline_fails('pythondll', 'pythondll=Something()', 'E520:')
call s:modeline_fails('pythonhome', 'pythonhome=Something()', 'E520:')
call s:modeline_fails('pythonthreedll', 'pythonthreedll=Something()', 'E520:')
call s:modeline_fails('pythonthreehome', 'pythonthreehome=Something()', 'E520:')
call s:modeline_fails('pyxversion', 'pyxversion=Something()', 'E520:')
call s:modeline_fails('rubydll', 'rubydll=Something()', 'E520:')
call s:modeline_fails('runtimepath', 'runtimepath=Something()', 'E520:')
call s:modeline_fails('secure', 'secure=Something()', 'E520:')
call s:modeline_fails('shell', 'shell=Something()', 'E520:')
call s:modeline_fails('shellcmdflag', 'shellcmdflag=Something()', 'E520:')
call s:modeline_fails('shellpipe', 'shellpipe=Something()', 'E520:')
call s:modeline_fails('shellquote', 'shellquote=Something()', 'E520:')
call s:modeline_fails('shellredir', 'shellredir=Something()', 'E520:')
call s:modeline_fails('shellxquote', 'shellxquote=Something()', 'E520:')
call s:modeline_fails('spellfile', 'spellfile=Something()', 'E520:')
call s:modeline_fails('spellsuggest', 'spellsuggest=Something()', 'E520:')
call s:modeline_fails('tcldll', 'tcldll=Something()', 'E520:')
call s:modeline_fails('titleold', 'titleold=Something()', 'E520:')
call s:modeline_fails('viewdir', 'viewdir=Something()', 'E520:')
call s:modeline_fails('viminfo', 'viminfo=Something()', 'E520:')
call s:modeline_fails('viminfofile', 'viminfofile=Something()', 'E520:')
call s:modeline_fails('winptydll', 'winptydll=Something()', 'E520:')
call s:modeline_fails('undodir', 'undodir=Something()', 'E520:')
" only check a few terminal options
" Skip these since nvim doesn't support termcodes as options
"call s:modeline_fails('t_AB', 't_AB=Something()', 'E520:')
"call s:modeline_fails('t_ce', 't_ce=Something()', 'E520:')
"call s:modeline_fails('t_sr', 't_sr=Something()', 'E520:')
"call s:modeline_fails('t_8b', 't_8b=Something()', 'E520:')
endfunc
func Test_modeline_fails_modelineexpr()
call s:modeline_fails('balloonexpr', 'balloonexpr=Something()', 'E992:')
call s:modeline_fails('foldexpr', 'foldexpr=Something()', 'E992:')
call s:modeline_fails('foldtext', 'foldtext=Something()', 'E992:')
call s:modeline_fails('formatexpr', 'formatexpr=Something()', 'E992:')
call s:modeline_fails('guitablabel', 'guitablabel=Something()', 'E992:')
call s:modeline_fails('iconstring', 'iconstring=Something()', 'E992:')
call s:modeline_fails('includeexpr', 'includeexpr=Something()', 'E992:')
call s:modeline_fails('indentexpr', 'indentexpr=Something()', 'E992:')
call s:modeline_fails('rulerformat', 'rulerformat=Something()', 'E992:')
call s:modeline_fails('statusline', 'statusline=Something()', 'E992:')
call s:modeline_fails('tabline', 'tabline=Something()', 'E992:')
call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:')
endfunc

View File

@ -1297,5 +1297,4 @@ endfunc
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=4 tw=80 fdm=marker
" vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "")
"-------------------------------------------------------------------------------