fix(cwd): validate getcwd(…, -1)

This commit is contained in:
Justin M. Keyes
2026-08-04 15:45:45 +02:00
parent db2e86fba4
commit 46ca236525
10 changed files with 99 additions and 73 deletions

View File

@@ -4116,9 +4116,8 @@ M.funcs = {
directory of the window indicated by {winnr}.
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
working directory of that buffer is returned. If {bufnr} is
-1, it is ignored, and the global working directory is
returned.
working directory of that buffer is returned. An argument may
be -1 only if all preceding arguments are -1.
Examples of buffer usage: >vim
getcwd(-1, -1, 0) " Get current buffer's directory
getcwd(-1, -1, 3) " Get directory of buffer #3
@@ -5429,15 +5428,15 @@ M.funcs = {
args = { 0, 3 },
base = 1,
desc = [=[
Checks whether the window, tabpage or buffer has set a local
Checks whether the tabpage, window or buffer has set a local
working directory. Returns 1 when the window has set a local
path via |:lcd|, or when {winnr} is -1 and the tabpage has set
a local path via |:tcd|, or when {winnr} and {tabnr} are -1
and {bufnr} has set a local path via |:bcd|, otherwise 0.
Tabs, windows and buffers are identified by their respective
numbers, 0 means current tab or window. Missing argument
implies 0. Thus the following are equivalent: >vim
numbers, 0 means current tab, window or buffer. Missing
argument implies 0. Thus the following are equivalent: >vim
echo haslocaldir()
echo haslocaldir(0)
echo haslocaldir(0, 0)
@@ -5446,7 +5445,8 @@ M.funcs = {
{winnr} is a |window-number| or |window-ID|.
If {winnr} is -1 it is ignored, only the tab is resolved.
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and
only the buffer is resolved.
only the buffer is resolved. An argument may be -1 only if
all preceding arguments are -1.
Examples of buffer usage: >vim
haslocaldir(-1, -1, 0) " Current buf has a local directory?
haslocaldir(-1, -1, 3) " Buf #3 has a local directory?

View File

@@ -681,6 +681,16 @@ static bool getcwd_scope_args(typval_T *argvars, CdScope default_scope, CdScope
}
}
// An argument may only be -1 if all preceding arguments are -1: `(2, 3, -1)` is an error.
bool explicit_scope = false;
for (int i = 0; i < argc; i++) {
explicit_scope = explicit_scope || argv[i] >= 0;
if (explicit_scope && argv[i] < 0) {
emsg(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return false;
}
}
// Narrowest requested scope. Imagine X >= 0:
switch (argc) {
case 0:
@@ -705,7 +715,7 @@ static bool getcwd_scope_args(typval_T *argvars, CdScope default_scope, CdScope
if (argv[kBufArg] >= 0) {
*scope = kCdScopeBuffer; // (-1, -1, X)
} else {
*scope = kCdScopeGlobal; // (..., ..., -1)
*scope = kCdScopeGlobal; // (-1, -1, -1)
}
break;
}
@@ -737,10 +747,6 @@ static bool getcwd_scope_args(typval_T *argvars, CdScope default_scope, CdScope
// Find the window in `tp` by number.
if (argv[kWinArg] >= 0) {
if (argv[kTabArg] < 0) {
emsg(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return false;
}
if (argv[kWinArg] > 0) {
*win = find_win_by_nr(&argvars[0], *tp);
if (*win == NULL) {

View File

@@ -6222,7 +6222,7 @@ static char **get_prevdir(CdScope scope)
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab, buffer or window).
/// @param scope Scope of the function call (global, tab, window or buffer).
static void post_chdir(CdScope scope, bool trigger_dirchanged)
{
// Always overwrite the window-local CWD.
@@ -6275,9 +6275,9 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
}
}
/// Change directory function used by :cd/:tcd/:bcd/:lcd Ex commands and the chdir() function.
/// Change directory function used by :cd/:tcd/:lcd/:bcd Ex commands and the chdir() function.
/// @param new_dir The directory to change to.
/// @param scope Scope of the function call (global, tab, buffer or window).
/// @param scope Scope of the function call (global, tab, window or buffer).
/// @return true if the directory is successfully changed.
bool changedir_func(char *new_dir, CdScope scope)
{
@@ -6333,7 +6333,7 @@ bool changedir_func(char *new_dir, CdScope scope)
return true;
}
/// ":cd", ":tcd", ":bcd", ":lcd", ":chdir", "tchdir", ":bchdir", and ":lchdir".
/// ":cd", ":tcd", ":lcd", ":bcd", ":chdir", ":tchdir", ":lchdir", and ":bchdir".
void ex_cd(exarg_T *eap)
{
char *new_dir = eap->arg;

View File

@@ -45,7 +45,7 @@ typedef enum {
/// What caused the current directory to change.
typedef enum {
kCdCauseOther = -1,
kCdCauseManual, ///< Using `:cd`, `:bcd`, `:tcd`, `:lcd` or `chdir()`.
kCdCauseManual, ///< Using `:cd`, `:tcd`, `:lcd`, `:bcd` or `chdir()`.
kCdCauseWindow, ///< Switching to another window.
kCdCauseBuffer, ///< Switching to another buffer.
kCdCauseAuto, ///< On 'autochdir'.