summaryrefslogtreecommitdiffstats
path: root/src/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ex_docmd.c')
-rw-r--r--src/ex_docmd.c339
1 files changed, 232 insertions, 107 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 19b1d85..a588f26 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -19,6 +19,10 @@ static int ex_pressedreturn = FALSE;
# define ex_hardcopy ex_ni
#endif
+#if defined(FEAT_EVAL) || defined(PROTO)
+static int cmp_cmdmod_info(const void *a, const void *b);
+#endif
+
#ifdef FEAT_EVAL
static char_u *do_one_cmd(char_u **, int, cstack_T *, char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
#else
@@ -82,7 +86,7 @@ static void correct_range(exarg_T *eap);
#ifdef FEAT_QUICKFIX
static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep);
#endif
-static char_u *repl_cmdline(exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep);
+static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl, char_u **cmdlinep);
static void ex_highlight(exarg_T *eap);
static void ex_colorscheme(exarg_T *eap);
static void ex_cquit(exarg_T *eap);
@@ -461,6 +465,40 @@ restore_dbg_stuff(struct dbg_stuff *dsp)
#endif
/*
+ * Check if ffname differs from fnum.
+ * fnum is a buffer number. 0 == current buffer, 1-or-more must be a valid buffer ID.
+ * ffname is a full path to where a buffer lives on-disk or would live on-disk.
+ *
+ */
+ static int
+is_other_file(int fnum, char_u *ffname)
+{
+ if (fnum != 0)
+ {
+ if (fnum == curbuf->b_fnum)
+ return FALSE;
+
+ return TRUE;
+ }
+
+ if (ffname == NULL)
+ return TRUE;
+
+ if (*ffname == NUL)
+ return FALSE;
+
+ // TODO: Need a reliable way to know whether a buffer is meant to live on-disk
+ // !curbuf->b_dev_valid is not always available (example: missing on Windows)
+ if (curbuf->b_sfname != NULL
+ && *curbuf->b_sfname != NUL)
+ // This occurs with unsaved buffers. In which case `ffname`
+ // actually corresponds to curbuf->b_sfname
+ return fnamecmp(ffname, curbuf->b_sfname) != 0;
+
+ return otherfile(ffname);
+}
+
+/*
* do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi"
* command is given.
*/
@@ -2911,7 +2949,7 @@ parse_command_modifiers(
switch (*p)
{
- // When adding an entry, also modify cmdmods[].
+ // When adding an entry, also modify cmdmod_info_tab[].
case 'a': if (!checkforcmd_noparen(&eap->cmd, "aboveleft", 3))
break;
cmod->cmod_split |= WSP_ABOVE;
@@ -3921,7 +3959,7 @@ find_ex_command(
if (eap->cmdidx == CMD_mode || eap->cmdidx == CMD_Print)
eap->cmdidx = CMD_SIZE;
else if ((cmdnames[eap->cmdidx].cmd_argt & EX_WHOLE)
- && len < (int)STRLEN(cmdnames[eap->cmdidx].cmd_name))
+ && len < (int)cmdnames[eap->cmdidx].cmd_namelen)
{
semsg(_(e_command_cannot_be_shortened_str), eap->cmd);
eap->cmdidx = CMD_SIZE;
@@ -3970,12 +4008,14 @@ find_ex_command(
}
#if defined(FEAT_EVAL) || defined(PROTO)
-static struct cmdmod
+typedef struct
{
char *name;
int minlen;
int has_count; // :123verbose :3tab
-} cmdmods[] = {
+} cmdmod_info_T;
+
+static cmdmod_info_T cmdmod_info_tab[] = {
{"aboveleft", 3, FALSE},
{"belowright", 3, FALSE},
{"botright", 2, FALSE},
@@ -4001,8 +4041,18 @@ static struct cmdmod
{"unsilent", 3, FALSE},
{"verbose", 4, TRUE},
{"vertical", 4, FALSE},
- {"vim9cmd", 4, FALSE},
-};
+ {"vim9cmd", 4, FALSE}
+}; // cmdmod_info_tab
+
+// compare two cmdmod_info_T structs by case sensitive name with length
+ static int
+cmp_cmdmod_info(const void *a, const void *b)
+{
+ cmdmod_info_T *cm1 = (cmdmod_info_T *)a;
+ cmdmod_info_T *cm2 = (cmdmod_info_T *)b;
+
+ return STRNCMP(cm1->name, cm2->name, cm2->minlen);
+}
/*
* Return length of a command modifier (including optional count).
@@ -4011,20 +4061,36 @@ static struct cmdmod
int
modifier_len(char_u *cmd)
{
- int i, j;
char_u *p = cmd;
+ cmdmod_info_T target;
+ cmdmod_info_T *entry;
if (VIM_ISDIGIT(*cmd))
p = skipwhite(skipdigits(cmd + 1));
- for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
+
+ // only lowercase characters can match
+ if (!ASCII_ISLOWER(*p))
+ return 0;
+
+ target.name = (char *)p;
+ target.minlen = 0; // not used, see cmp_cmdmod_info()
+ target.has_count = FALSE;
+
+ entry = (cmdmod_info_T *)bsearch(&target, &cmdmod_info_tab, ARRAY_LENGTH(cmdmod_info_tab), sizeof(cmdmod_info_tab[0]), cmp_cmdmod_info);
+ if (entry != NULL)
{
- for (j = 0; p[j] != NUL; ++j)
- if (p[j] != cmdmods[i].name[j])
+ int i;
+
+ for (i = entry->minlen; p[i] != NUL; ++i)
+ {
+ if (p[i] != entry->name[i])
break;
- if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen
- && (p == cmd || cmdmods[i].has_count))
- return j + (int)(p - cmd);
+ }
+
+ if (!ASCII_ISALPHA(p[i]) && i >= entry->minlen && (p == cmd || entry->has_count))
+ return i + (int)(p - cmd);
}
+
return 0;
}
@@ -4038,18 +4104,33 @@ cmd_exists(char_u *name)
{
exarg_T ea;
int full = FALSE;
- int i;
- int j;
char_u *p;
- // Check command modifiers.
- for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
+ // only lowercase characters can match
+ if (ASCII_ISLOWER(*name))
{
- for (j = 0; name[j] != NUL; ++j)
- if (name[j] != cmdmods[i].name[j])
- break;
- if (name[j] == NUL && j >= cmdmods[i].minlen)
- return (cmdmods[i].name[j] == NUL ? 2 : 1);
+ cmdmod_info_T target;
+ cmdmod_info_T *entry;
+
+ target.name = (char *)name;
+ target.minlen = 0; // not used, see cmp_cmdmod_info()
+ target.has_count = FALSE;
+
+ // Check command modifiers.
+ entry = (cmdmod_info_T *)bsearch(&target, &cmdmod_info_tab, ARRAY_LENGTH(cmdmod_info_tab), sizeof(cmdmod_info_tab[0]), cmp_cmdmod_info);
+ if (entry != NULL)
+ {
+ int i;
+
+ for (i = entry->minlen; name[i] != NUL; ++i)
+ {
+ if (name[i] != entry->name[i])
+ break;
+ }
+
+ if (name[i] == NUL && i >= entry->minlen)
+ return (entry->name[i] == NUL ? 2 : 1);
+ }
}
// Check built-in commands and user defined commands.
@@ -4963,12 +5044,14 @@ replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
}
else
{
- new_cmdline = alloc(STRLEN(program) + STRLEN(p) + 2);
+ size_t program_len = STRLEN(program);
+
+ new_cmdline = alloc(program_len + STRLEN(p) + 2);
if (new_cmdline == NULL)
return NULL; // out of memory
STRCPY(new_cmdline, program);
- STRCAT(new_cmdline, " ");
- STRCAT(new_cmdline, p);
+ STRCPY(new_cmdline + program_len, " ");
+ STRCPY(new_cmdline + program_len + 1, p);
}
msg_make(p);
@@ -5118,7 +5201,7 @@ expand_filename(
}
}
- p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
+ p = repl_cmdline(eap, p, (size_t)srclen, repl, cmdlinep);
vim_free(repl);
if (p == NULL)
return FAIL;
@@ -5189,7 +5272,7 @@ expand_filename(
}
if (p != NULL)
{
- (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg),
+ (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg),
p, cmdlinep);
if (n == 2) // p came from ExpandOne()
vim_free(p);
@@ -5212,23 +5295,26 @@ expand_filename(
repl_cmdline(
exarg_T *eap,
char_u *src,
- int srclen,
+ size_t srclen,
char_u *repl,
char_u **cmdlinep)
{
- int len;
- int i;
+ size_t repllen;
+ size_t taillen;
+ size_t i;
char_u *new_cmdline;
+ size_t new_cmdlinelen;
/*
* The new command line is build in new_cmdline[].
* First allocate it.
* Careful: a "+cmd" argument may have been NUL terminated.
*/
- len = (int)STRLEN(repl);
- i = (int)(src - *cmdlinep) + (int)STRLEN(src + srclen) + len + 3;
+ repllen = STRLEN(repl);
+ taillen = STRLEN(src + srclen);
+ i = (src - *cmdlinep) + repllen + taillen + 3;
if (eap->nextcmd != NULL)
- i += (int)STRLEN(eap->nextcmd);// add space for next command
+ i += STRLEN(eap->nextcmd); // add space for next command
if ((new_cmdline = alloc(i)) == NULL)
return NULL; // out of memory!
@@ -5238,19 +5324,19 @@ repl_cmdline(
* Copy what came after the expanded part.
* Copy the next commands, if there are any.
*/
- i = (int)(src - *cmdlinep); // length of part before match
- mch_memmove(new_cmdline, *cmdlinep, (size_t)i);
+ new_cmdlinelen = src - *cmdlinep; // length of part before replacement
+ mch_memmove(new_cmdline, *cmdlinep, new_cmdlinelen);
- mch_memmove(new_cmdline + i, repl, (size_t)len);
- i += len; // remember the end of the string
- STRCPY(new_cmdline + i, src + srclen);
- src = new_cmdline + i; // remember where to continue
+ mch_memmove(new_cmdline + new_cmdlinelen, repl, repllen);
+ new_cmdlinelen += repllen; // remember the end of the string
+ STRCPY(new_cmdline + new_cmdlinelen, src + srclen);
+ src = new_cmdline + new_cmdlinelen; // remember where to continue
if (eap->nextcmd != NULL) // append next command
{
- i = (int)STRLEN(new_cmdline) + 1;
- STRCPY(new_cmdline + i, eap->nextcmd);
- eap->nextcmd = new_cmdline + i;
+ new_cmdlinelen += taillen + 1;
+ STRCPY(new_cmdline + new_cmdlinelen, eap->nextcmd);
+ eap->nextcmd = new_cmdline + new_cmdlinelen;
}
eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
eap->arg = new_cmdline + (eap->arg - *cmdlinep);
@@ -5421,9 +5507,10 @@ get_bad_name(expand_T *xp UNUSED, int idx)
"drop",
};
- if (idx < (int)ARRAY_LENGTH(p_bad_values))
- return (char_u*)p_bad_values[idx];
- return NULL;
+ if (idx < 0 || idx >= (int)ARRAY_LENGTH(p_bad_values))
+ return NULL;
+
+ return (char_u*)p_bad_values[idx];
}
/*
@@ -5537,9 +5624,10 @@ get_argopt_name(expand_T *xp UNUSED, int idx)
"edit",
};
- if (idx < (int)ARRAY_LENGTH(p_opt_values))
- return (char_u*)p_opt_values[idx];
- return NULL;
+ if (idx < 0 || idx >= (int)ARRAY_LENGTH(p_opt_values))
+ return NULL;
+
+ return (char_u*)p_opt_values[idx];
}
/*
@@ -6317,11 +6405,19 @@ get_tabpage_arg(exarg_T *eap)
else
{
tab_number = eap->line2;
- if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-')
+ if (!unaccept_arg0)
{
- --tab_number;
- if (tab_number < unaccept_arg0)
- eap->errmsg = _(e_invalid_range);
+ char_u *cmdp = eap->cmd;
+
+ while (--cmdp > *eap->cmdlinep
+ && (VIM_ISWHITE(*cmdp) || VIM_ISDIGIT(*cmdp)))
+ ;
+ if (*cmdp == '-')
+ {
+ --tab_number;
+ if (tab_number < unaccept_arg0)
+ eap->errmsg = _(e_invalid_range);
+ }
}
}
}
@@ -7248,12 +7344,15 @@ ex_open(exarg_T *eap)
static void
ex_edit(exarg_T *eap)
{
+ char_u *ffname = eap->cmdidx == CMD_enew ? NULL : eap->arg;
+
// Exclude commands which keep the window's current buffer
if (
eap->cmdidx != CMD_badd
&& eap->cmdidx != CMD_balt
// All other commands must obey 'winfixbuf' / ! rules
- && !check_can_set_curbuf_forceit(eap->forceit))
+ && (is_other_file(0, ffname) && !check_can_set_curbuf_forceit(eap->forceit))
+ )
return;
do_exedit(eap, NULL);
@@ -9237,6 +9336,27 @@ ex_tag_cmd(exarg_T *eap, char_u *name)
eap->forceit, TRUE);
}
+enum {
+ SPEC_PERC = 0,
+ SPEC_HASH,
+ SPEC_CWORD, // cursor word
+ SPEC_CCWORD, // cursor WORD
+ SPEC_CEXPR, // expr under cursor
+ SPEC_CFILE, // cursor path name
+ SPEC_SFILE, // ":so" file name
+ SPEC_SLNUM, // ":so" file line number
+ SPEC_STACK, // call stack
+ SPEC_SCRIPT, // script file name
+ SPEC_AFILE, // autocommand file name
+ SPEC_ABUF, // autocommand buffer number
+ SPEC_AMATCH, // autocommand match name
+ SPEC_SFLNUM, // script file line number
+ SPEC_SID // script ID: <SNR>123_
+#ifdef FEAT_CLIENTSERVER
+ , SPEC_CLIENT
+#endif
+};
+
/*
* Check "str" for starting with a special cmdline variable.
* If found return one of the SPEC_ values and set "*usedlen" to the length of
@@ -9245,54 +9365,54 @@ ex_tag_cmd(exarg_T *eap, char_u *name)
int
find_cmdline_var(char_u *src, int *usedlen)
{
- int len;
- int i;
- static char *(spec_str[]) = {
- "%",
-#define SPEC_PERC 0
- "#",
-#define SPEC_HASH (SPEC_PERC + 1)
- "<cword>", // cursor word
-#define SPEC_CWORD (SPEC_HASH + 1)
- "<cWORD>", // cursor WORD
-#define SPEC_CCWORD (SPEC_CWORD + 1)
- "<cexpr>", // expr under cursor
-#define SPEC_CEXPR (SPEC_CCWORD + 1)
- "<cfile>", // cursor path name
-#define SPEC_CFILE (SPEC_CEXPR + 1)
- "<sfile>", // ":so" file name
-#define SPEC_SFILE (SPEC_CFILE + 1)
- "<slnum>", // ":so" file line number
-#define SPEC_SLNUM (SPEC_SFILE + 1)
- "<stack>", // call stack
-#define SPEC_STACK (SPEC_SLNUM + 1)
- "<script>", // script file name
-#define SPEC_SCRIPT (SPEC_STACK + 1)
- "<afile>", // autocommand file name
-#define SPEC_AFILE (SPEC_SCRIPT + 1)
- "<abuf>", // autocommand buffer number
-#define SPEC_ABUF (SPEC_AFILE + 1)
- "<amatch>", // autocommand match name
-#define SPEC_AMATCH (SPEC_ABUF + 1)
- "<sflnum>", // script file line number
-#define SPEC_SFLNUM (SPEC_AMATCH + 1)
- "<SID>", // script ID: <SNR>123_
-#define SPEC_SID (SPEC_SFLNUM + 1)
+ // must be sorted by the 'value' field because it is used by bsearch()!
+ static keyvalue_T spec_str_tab[] = {
+ KEYVALUE_ENTRY(SPEC_SID, "SID>"), // script ID: <SNR>123_
+ KEYVALUE_ENTRY(SPEC_ABUF, "abuf>"), // autocommand buffer number
+ KEYVALUE_ENTRY(SPEC_AFILE, "afile>"), // autocommand file name
+ KEYVALUE_ENTRY(SPEC_AMATCH, "amatch>"), // autocommand match name
+ KEYVALUE_ENTRY(SPEC_CCWORD, "cWORD>"), // cursor WORD
+ KEYVALUE_ENTRY(SPEC_CEXPR, "cexpr>"), // expr under cursor
+ KEYVALUE_ENTRY(SPEC_CFILE, "cfile>"), // cursor path name
#ifdef FEAT_CLIENTSERVER
- "<client>"
-# define SPEC_CLIENT (SPEC_SID + 1)
-#endif
+ KEYVALUE_ENTRY(SPEC_CLIENT, "client>"),
+#endif
+ KEYVALUE_ENTRY(SPEC_CWORD, "cword>"), // cursor word
+ KEYVALUE_ENTRY(SPEC_SCRIPT, "script>"), // script file name
+ KEYVALUE_ENTRY(SPEC_SFILE, "sfile>"), // ":so" file name
+ KEYVALUE_ENTRY(SPEC_SFLNUM, "sflnum>"), // script file line number
+ KEYVALUE_ENTRY(SPEC_SLNUM, "slnum>"), // ":so" file line number
+ KEYVALUE_ENTRY(SPEC_STACK, "stack>") // call stack
};
+ keyvalue_T target;
+ keyvalue_T *entry;
- for (i = 0; i < (int)ARRAY_LENGTH(spec_str); ++i)
+ switch (*src)
{
- len = (int)STRLEN(spec_str[i]);
- if (STRNCMP(src, spec_str[i], len) == 0)
- {
- *usedlen = len;
- return i;
- }
+ case '%':
+ *usedlen = 1;
+ return SPEC_PERC;
+
+ case '#':
+ *usedlen = 1;
+ return SPEC_HASH;
+
+ case '<':
+ target.key = 0;
+ target.value = (char *)src + 1; // skip over '<'
+ target.length = 0; // not used, see cmp_keyvalue_value_n()
+
+ entry = (keyvalue_T *)bsearch(&target, &spec_str_tab, ARRAY_LENGTH(spec_str_tab), sizeof(spec_str_tab[0]), cmp_keyvalue_value_n);
+ if (entry == NULL)
+ return -1;
+
+ *usedlen = entry->length + 1;
+ return entry->key;
+
+ default:
+ break;
}
+
return -1;
}
@@ -9655,14 +9775,17 @@ eval_vars(
expand_sfile(char_u *arg)
{
char *errormsg;
- int len;
char_u *result;
+ size_t resultlen;
char_u *newres;
+ size_t len;
char_u *repl;
+ size_t repllen;
int srclen;
char_u *p;
- result = vim_strsave(arg);
+ resultlen = STRLEN(arg);
+ result = vim_strnsave(arg, resultlen);
if (result == NULL)
return NULL;
@@ -9686,18 +9809,20 @@ expand_sfile(char_u *arg)
p += srclen;
continue;
}
- len = (int)STRLEN(result) - srclen + (int)STRLEN(repl) + 1;
- newres = alloc(len);
+ repllen = STRLEN(repl);
+ resultlen += repllen - srclen;
+ newres = alloc(resultlen + 1);
if (newres == NULL)
{
vim_free(repl);
vim_free(result);
return NULL;
}
- mch_memmove(newres, result, (size_t)(p - result));
- STRCPY(newres + (p - result), repl);
- len = (int)STRLEN(newres);
- STRCAT(newres, p + srclen);
+ len = p - result;
+ mch_memmove(newres, result, len);
+ STRCPY(newres + len, repl);
+ len += repllen;
+ STRCPY(newres + len, p + srclen);
vim_free(repl);
vim_free(result);
result = newres;
@@ -9853,7 +9978,7 @@ ex_filetype(exarg_T *eap)
static void
ex_setfiletype(exarg_T *eap)
{
- if (did_filetype)
+ if (curbuf->b_did_filetype)
return;
char_u *arg = eap->arg;
@@ -9862,7 +9987,7 @@ ex_setfiletype(exarg_T *eap)
set_option_value_give_err((char_u *)"filetype", 0L, arg, OPT_LOCAL);
if (arg != eap->arg)
- did_filetype = FALSE;
+ curbuf->b_did_filetype = FALSE;
}
static void