mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 8.2.0850: MS-Windows: exepath() works different from cmd.exe
Problem: MS-Windows: exepath() works different from cmd.exe. Solution: Make exepath() work better on MS-Windows. (closes #6115)
This commit is contained in:
@@ -4034,7 +4034,7 @@ executable({expr}) *executable()*
|
|||||||
On MS-Windows the ".exe", ".bat", etc. can optionally be
|
On MS-Windows the ".exe", ".bat", etc. can optionally be
|
||||||
included. Then the extensions in $PATHEXT are tried. Thus if
|
included. Then the extensions in $PATHEXT are tried. Thus if
|
||||||
"foo.exe" does not exist, "foo.exe.bat" can be found. If
|
"foo.exe" does not exist, "foo.exe.bat" can be found. If
|
||||||
$PATHEXT is not set then ".exe;.com;.bat;.cmd" is used. A dot
|
$PATHEXT is not set then ".com;.exe;.bat;.cmd" is used. A dot
|
||||||
by itself can be used in $PATHEXT to try using the name
|
by itself can be used in $PATHEXT to try using the name
|
||||||
without an extension. When 'shell' looks like a Unix shell,
|
without an extension. When 'shell' looks like a Unix shell,
|
||||||
then the name is also tried without adding an extension.
|
then the name is also tried without adding an extension.
|
||||||
|
291
src/os_win32.c
291
src/os_win32.c
@@ -2080,57 +2080,200 @@ theend:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
|
* Return TRUE if "name" is an executable file, FALSE if not or it doesn't exist.
|
||||||
* If "use_path" is FALSE: Return TRUE if "name" exists.
|
|
||||||
* When returning TRUE and "path" is not NULL save the path and set "*path" to
|
* When returning TRUE and "path" is not NULL save the path and set "*path" to
|
||||||
* the allocated memory.
|
* the allocated memory.
|
||||||
* TODO: Should somehow check if it's really executable.
|
* TODO: Should somehow check if it's really executable.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
executable_exists(char *name, char_u **path, int use_path)
|
executable_file(char *name, char_u **path)
|
||||||
{
|
{
|
||||||
WCHAR *p;
|
if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
|
||||||
WCHAR fnamew[_MAX_PATH];
|
|
||||||
WCHAR *dumw;
|
|
||||||
WCHAR *wcurpath, *wnewpath;
|
|
||||||
long n;
|
|
||||||
|
|
||||||
if (!use_path)
|
|
||||||
{
|
{
|
||||||
if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
|
if (path != NULL)
|
||||||
{
|
*path = FullName_save((char_u *)name, FALSE);
|
||||||
if (path != NULL)
|
return TRUE;
|
||||||
{
|
}
|
||||||
if (mch_isFullName((char_u *)name))
|
return FALSE;
|
||||||
*path = vim_strsave((char_u *)name);
|
}
|
||||||
else
|
|
||||||
*path = FullName_save((char_u *)name, FALSE);
|
/*
|
||||||
}
|
* If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
|
||||||
return TRUE;
|
* If "use_path" is FALSE: Return TRUE if "name" exists.
|
||||||
}
|
* If "use_pathext" is TRUE search "name" with extensions in $PATHEXT.
|
||||||
|
* When returning TRUE and "path" is not NULL save the path and set "*path" to
|
||||||
|
* the allocated memory.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
executable_exists(char *name, char_u **path, int use_path, int use_pathext)
|
||||||
|
{
|
||||||
|
// WinNT and later can use _MAX_PATH wide characters for a pathname, which
|
||||||
|
// means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
|
||||||
|
// UTF-8.
|
||||||
|
char_u buf[_MAX_PATH * 3];
|
||||||
|
size_t len = STRLEN(name);
|
||||||
|
size_t tmplen;
|
||||||
|
char_u *p, *e, *e2;
|
||||||
|
char_u *pathbuf = NULL;
|
||||||
|
char_u *pathext = NULL;
|
||||||
|
char_u *pathextbuf = NULL;
|
||||||
|
int noext = FALSE;
|
||||||
|
int retval = FALSE;
|
||||||
|
|
||||||
|
if (len >= sizeof(buf)) // safety check
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
// Using the name directly when a Unix-shell like 'shell'.
|
||||||
|
if (strstr((char *)gettail(p_sh), "sh") != NULL)
|
||||||
|
noext = TRUE;
|
||||||
|
|
||||||
|
if (use_pathext)
|
||||||
|
{
|
||||||
|
pathext = mch_getenv("PATHEXT");
|
||||||
|
if (pathext == NULL)
|
||||||
|
pathext = (char_u *)".com;.exe;.bat;.cmd";
|
||||||
|
|
||||||
|
if (noext == FALSE)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Loop over all extensions in $PATHEXT.
|
||||||
|
* Check "name" ends with extension.
|
||||||
|
*/
|
||||||
|
p = pathext;
|
||||||
|
while (*p)
|
||||||
|
{
|
||||||
|
if (p[0] == ';'
|
||||||
|
|| (p[0] == '.' && (p[1] == NUL || p[1] == ';')))
|
||||||
|
{
|
||||||
|
// Skip empty or single ".".
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
e = vim_strchr(p, ';');
|
||||||
|
if (e == NULL)
|
||||||
|
e = p + STRLEN(p);
|
||||||
|
tmplen = e - p;
|
||||||
|
|
||||||
|
if (_strnicoll(name + len - tmplen, (char *)p, tmplen) == 0)
|
||||||
|
{
|
||||||
|
noext = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p = enc_to_utf16((char_u *)name, NULL);
|
// Prepend single "." to pathext, it's means no extension added.
|
||||||
if (p == NULL)
|
if (pathext == NULL)
|
||||||
return FALSE;
|
pathext = (char_u *)".";
|
||||||
|
else if (noext == TRUE)
|
||||||
|
{
|
||||||
|
if (pathextbuf == NULL)
|
||||||
|
pathextbuf = alloc(STRLEN(pathext) + 3);
|
||||||
|
if (pathextbuf == NULL)
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
STRCPY(pathextbuf, ".;");
|
||||||
|
STRCAT(pathextbuf, pathext);
|
||||||
|
pathext = pathextbuf;
|
||||||
|
}
|
||||||
|
|
||||||
wcurpath = _wgetenv(L"PATH");
|
// Use $PATH when "use_path" is TRUE and "name" is basename.
|
||||||
wnewpath = ALLOC_MULT(WCHAR, wcslen(wcurpath) + 3);
|
if (use_path && gettail((char_u *)name) == (char_u *)name)
|
||||||
if (wnewpath == NULL)
|
{
|
||||||
return FALSE;
|
p = mch_getenv("PATH");
|
||||||
wcscpy(wnewpath, L".;");
|
if (p != NULL)
|
||||||
wcscat(wnewpath, wcurpath);
|
{
|
||||||
n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
|
pathbuf = alloc(STRLEN(p) + 3);
|
||||||
vim_free(wnewpath);
|
if (pathbuf == NULL)
|
||||||
vim_free(p);
|
{
|
||||||
if (n == 0)
|
retval = FALSE;
|
||||||
return FALSE;
|
goto theend;
|
||||||
if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
|
}
|
||||||
return FALSE;
|
STRCPY(pathbuf, ".;");
|
||||||
if (path != NULL)
|
STRCAT(pathbuf, p);
|
||||||
*path = utf16_to_enc(fnamew, NULL);
|
}
|
||||||
return TRUE;
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk through all entries in $PATH to check if "name" exists there and
|
||||||
|
* is an executable file.
|
||||||
|
*/
|
||||||
|
p = (pathbuf != NULL) ? pathbuf : (char_u *)".";
|
||||||
|
while (*p)
|
||||||
|
{
|
||||||
|
if (*p == ';') // Skip empty entry
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
e = vim_strchr(p, ';');
|
||||||
|
if (e == NULL)
|
||||||
|
e = p + STRLEN(p);
|
||||||
|
|
||||||
|
if (e - p + len + 2 > sizeof(buf))
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
// A single "." that means current dir.
|
||||||
|
if (e - p == 1 && *p == '.')
|
||||||
|
STRCPY(buf, name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vim_strncpy(buf, p, e - p);
|
||||||
|
add_pathsep(buf);
|
||||||
|
STRCAT(buf, name);
|
||||||
|
}
|
||||||
|
tmplen = STRLEN(buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop over all extensions in $PATHEXT.
|
||||||
|
* Check "name" with extension added.
|
||||||
|
*/
|
||||||
|
p = pathext;
|
||||||
|
while (*p)
|
||||||
|
{
|
||||||
|
if (*p == ';')
|
||||||
|
{
|
||||||
|
// Skip empty entry
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
e2 = vim_strchr(p, (int)';');
|
||||||
|
if (e2 == NULL)
|
||||||
|
e2 = p + STRLEN(p);
|
||||||
|
|
||||||
|
if (!(p[0] == '.' && (p[1] == NUL || p[1] == ';')))
|
||||||
|
{
|
||||||
|
// Not a single "." that means no extension is added.
|
||||||
|
if (e2 - p + tmplen + 1 > sizeof(buf))
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
vim_strncpy(buf + tmplen, p, e2 - p);
|
||||||
|
}
|
||||||
|
if (executable_file((char *)buf, path))
|
||||||
|
{
|
||||||
|
retval = TRUE;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
theend:
|
||||||
|
free(pathextbuf);
|
||||||
|
free(pathbuf);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
|
#if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
|
||||||
@@ -2210,7 +2353,7 @@ mch_init_g(void)
|
|||||||
vimrun_path = (char *)vim_strsave(vimrun_location);
|
vimrun_path = (char *)vim_strsave(vimrun_location);
|
||||||
s_dont_use_vimrun = FALSE;
|
s_dont_use_vimrun = FALSE;
|
||||||
}
|
}
|
||||||
else if (executable_exists("vimrun.exe", NULL, TRUE))
|
else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE))
|
||||||
s_dont_use_vimrun = FALSE;
|
s_dont_use_vimrun = FALSE;
|
||||||
|
|
||||||
// Don't give the warning for a missing vimrun.exe right now, but only
|
// Don't give the warning for a missing vimrun.exe right now, but only
|
||||||
@@ -2224,7 +2367,7 @@ mch_init_g(void)
|
|||||||
* If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
|
* If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
|
||||||
* Otherwise the default "findstr /n" is used.
|
* Otherwise the default "findstr /n" is used.
|
||||||
*/
|
*/
|
||||||
if (!executable_exists("findstr.exe", NULL, TRUE))
|
if (!executable_exists("findstr.exe", NULL, TRUE, FALSE))
|
||||||
set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
|
set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
|
||||||
|
|
||||||
# ifdef FEAT_CLIPBOARD
|
# ifdef FEAT_CLIPBOARD
|
||||||
@@ -3306,69 +3449,7 @@ mch_writable(char_u *name)
|
|||||||
int
|
int
|
||||||
mch_can_exe(char_u *name, char_u **path, int use_path)
|
mch_can_exe(char_u *name, char_u **path, int use_path)
|
||||||
{
|
{
|
||||||
// WinNT and later can use _MAX_PATH wide characters for a pathname, which
|
return executable_exists((char *)name, path, TRUE, TRUE);
|
||||||
// means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
|
|
||||||
// UTF-8.
|
|
||||||
char_u buf[_MAX_PATH * 3];
|
|
||||||
int len = (int)STRLEN(name);
|
|
||||||
char_u *p, *saved;
|
|
||||||
|
|
||||||
if (len >= sizeof(buf)) // safety check
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
// Try using the name directly when a Unix-shell like 'shell'.
|
|
||||||
if (strstr((char *)gettail(p_sh), "sh") != NULL)
|
|
||||||
if (executable_exists((char *)name, path, use_path))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Loop over all extensions in $PATHEXT.
|
|
||||||
*/
|
|
||||||
p = mch_getenv("PATHEXT");
|
|
||||||
if (p == NULL)
|
|
||||||
p = (char_u *)".com;.exe;.bat;.cmd";
|
|
||||||
saved = vim_strsave(p);
|
|
||||||
if (saved == NULL)
|
|
||||||
return FALSE;
|
|
||||||
p = saved;
|
|
||||||
while (*p)
|
|
||||||
{
|
|
||||||
char_u *tmp = vim_strchr(p, ';');
|
|
||||||
|
|
||||||
if (tmp != NULL)
|
|
||||||
*tmp = NUL;
|
|
||||||
if (_stricoll((char *)name + len - STRLEN(p), (char *)p) == 0
|
|
||||||
&& executable_exists((char *)name, path, use_path))
|
|
||||||
{
|
|
||||||
vim_free(saved);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (tmp == NULL)
|
|
||||||
break;
|
|
||||||
p = tmp + 1;
|
|
||||||
}
|
|
||||||
vim_free(saved);
|
|
||||||
|
|
||||||
vim_strncpy(buf, name, sizeof(buf) - 1);
|
|
||||||
p = mch_getenv("PATHEXT");
|
|
||||||
if (p == NULL)
|
|
||||||
p = (char_u *)".com;.exe;.bat;.cmd";
|
|
||||||
while (*p)
|
|
||||||
{
|
|
||||||
if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
|
|
||||||
{
|
|
||||||
// A single "." means no extension is added.
|
|
||||||
buf[len] = NUL;
|
|
||||||
++p;
|
|
||||||
if (*p)
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
copy_option_part(&p, buf + len, sizeof(buf) - len, ";");
|
|
||||||
if (executable_exists((char *)buf, path, use_path))
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -1187,6 +1187,30 @@ func Test_Executable()
|
|||||||
call assert_equal(0, executable('notepad.exe.exe'))
|
call assert_equal(0, executable('notepad.exe.exe'))
|
||||||
call assert_equal(0, executable('shell32.dll'))
|
call assert_equal(0, executable('shell32.dll'))
|
||||||
call assert_equal(0, executable('win.ini'))
|
call assert_equal(0, executable('win.ini'))
|
||||||
|
|
||||||
|
" get "notepad" path and remove the leading drive and sep. (ex. 'C:\')
|
||||||
|
let notepadcmd = exepath('notepad.exe')
|
||||||
|
let driveroot = notepadcmd[:2]
|
||||||
|
let notepadcmd = notepadcmd[3:]
|
||||||
|
new
|
||||||
|
" check that the relative path works in /
|
||||||
|
execute 'lcd' driveroot
|
||||||
|
call assert_equal(1, executable(notepadcmd))
|
||||||
|
call assert_equal(driveroot .. notepadcmd, notepadcmd->exepath())
|
||||||
|
bwipe
|
||||||
|
|
||||||
|
" create "notepad.bat"
|
||||||
|
call mkdir('Xdir')
|
||||||
|
let notepadbat = fnamemodify('Xdir/notepad.bat', ':p')
|
||||||
|
call writefile([], notepadbat)
|
||||||
|
new
|
||||||
|
" check that the path and the pathext order is valid
|
||||||
|
lcd Xdir
|
||||||
|
let [pathext, $PATHEXT] = [$PATHEXT, '.com;.exe;.bat;.cmd']
|
||||||
|
call assert_equal(notepadbat, exepath('notepad'))
|
||||||
|
let $PATHEXT = pathext
|
||||||
|
bwipe
|
||||||
|
eval 'Xdir'->delete('rf')
|
||||||
elseif has('unix')
|
elseif has('unix')
|
||||||
call assert_equal(1, 'cat'->executable())
|
call assert_equal(1, 'cat'->executable())
|
||||||
call assert_equal(0, executable('nodogshere'))
|
call assert_equal(0, executable('nodogshere'))
|
||||||
|
@@ -746,6 +746,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
850,
|
||||||
/**/
|
/**/
|
||||||
849,
|
849,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user