Patch 8.2.0850
Problem: MS-Windows: exepath() works different from cmd.exe.
Solution: Make exepath() work better on MS-Windows. (closes #6115)
Files: runtime/doc/eval.txt, src/os_win32.c,
src/testdir/test_functions.vim
*** ../vim-8.2.0849/runtime/doc/eval.txt 2020-05-24 13:10:14.303617017
+0200
--- runtime/doc/eval.txt 2020-05-30 18:26:55.161581298 +0200
***************
*** 4027,4033 ****
On MS-Windows the ".exe", ".bat", etc. can optionally be
included. Then the extensions in $PATHEXT are tried. Thus 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
by itself can be used in $PATHEXT to try using the name
without an extension. When 'shell' looks like a Unix shell,
then the name is also tried without adding an extension.
--- 4034,4040 ----
On MS-Windows the ".exe", ".bat", etc. can optionally be
included. Then the extensions in $PATHEXT are tried. Thus if
"foo.exe" does not exist, "foo.exe.bat" can be found. If
! $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
without an extension. When 'shell' looks like a Unix shell,
then the name is also tried without adding an extension.
*** ../vim-8.2.0849/src/os_win32.c 2020-05-30 17:49:21.755140563 +0200
--- src/os_win32.c 2020-05-30 18:30:24.792906407 +0200
***************
*** 2080,2136 ****
#endif
/*
* If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
* 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
* the allocated memory.
- * TODO: Should somehow check if it's really executable.
*/
static int
! executable_exists(char *name, char_u **path, int use_path)
{
! WCHAR *p;
! 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)
{
! if (mch_isFullName((char_u *)name))
! *path = vim_strsave((char_u *)name);
! else
! *path = FullName_save((char_u *)name, FALSE);
}
- return TRUE;
}
- return FALSE;
}
! p = enc_to_utf16((char_u *)name, NULL);
! if (p == NULL)
! return FALSE;
! wcurpath = _wgetenv(L"PATH");
! wnewpath = ALLOC_MULT(WCHAR, wcslen(wcurpath) + 3);
! if (wnewpath == NULL)
! return FALSE;
! wcscpy(wnewpath, L".;");
! wcscat(wnewpath, wcurpath);
! n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw);
! vim_free(wnewpath);
! vim_free(p);
! if (n == 0)
! return FALSE;
! if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY)
! return FALSE;
! if (path != NULL)
! *path = utf16_to_enc(fnamew, NULL);
! return TRUE;
}
#if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
--- 2080,2279 ----
#endif
/*
+ * Return TRUE if "name" is an executable file, FALSE if not or it doesn't
exist.
+ * When returning TRUE and "path" is not NULL save the path and set "*path" to
+ * the allocated memory.
+ * TODO: Should somehow check if it's really executable.
+ */
+ static int
+ executable_file(char *name, char_u **path)
+ {
+ if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
+ {
+ if (path != NULL)
+ *path = FullName_save((char_u *)name, FALSE);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /*
* If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
* 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;
!
! // 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;
}
}
}
! // Prepend single "." to pathext, it's means no extension added.
! if (pathext == NULL)
! 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;
! }
! // Use $PATH when "use_path" is TRUE and "name" is basename.
! if (use_path && gettail((char_u *)name) == (char_u *)name)
! {
! p = mch_getenv("PATH");
! if (p != NULL)
! {
! pathbuf = alloc(STRLEN(p) + 3);
! if (pathbuf == NULL)
! {
! retval = FALSE;
! goto theend;
! }
! STRCPY(pathbuf, ".;");
! STRCAT(pathbuf, p);
! }
! }
!
! /*
! * 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) || \
***************
*** 2210,2216 ****
vimrun_path = (char *)vim_strsave(vimrun_location);
s_dont_use_vimrun = FALSE;
}
! else if (executable_exists("vimrun.exe", NULL, TRUE))
s_dont_use_vimrun = FALSE;
// Don't give the warning for a missing vimrun.exe right now, but only
--- 2353,2359 ----
vimrun_path = (char *)vim_strsave(vimrun_location);
s_dont_use_vimrun = FALSE;
}
! else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE))
s_dont_use_vimrun = FALSE;
// Don't give the warning for a missing vimrun.exe right now, but only
***************
*** 2224,2230 ****
* If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
* Otherwise the default "findstr /n" is used.
*/
! if (!executable_exists("findstr.exe", NULL, TRUE))
set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
# ifdef FEAT_CLIPBOARD
--- 2367,2373 ----
* If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
* Otherwise the default "findstr /n" is used.
*/
! if (!executable_exists("findstr.exe", NULL, TRUE, FALSE))
set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
# ifdef FEAT_CLIPBOARD
***************
*** 3306,3374 ****
int
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
! // 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;
}
/*
--- 3449,3455 ----
int
mch_can_exe(char_u *name, char_u **path, int use_path)
{
! return executable_exists((char *)name, path, TRUE, TRUE);
}
/*
*** ../vim-8.2.0849/src/testdir/test_functions.vim 2020-05-30
18:14:37.828521058 +0200
--- src/testdir/test_functions.vim 2020-05-30 18:26:55.161581298 +0200
***************
*** 1187,1192 ****
--- 1187,1216 ----
call assert_equal(0, executable('notepad.exe.exe'))
call assert_equal(0, executable('shell32.dll'))
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')
call assert_equal(1, 'cat'->executable())
call assert_equal(0, executable('nodogshere'))
*** ../vim-8.2.0849/src/version.c 2020-05-30 18:14:37.828521058 +0200
--- src/version.c 2020-05-30 18:29:37.805062059 +0200
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 850,
/**/
--
>From the classified section of a city newspaper:
Dog for sale: eats anything and is fond of children.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202005301638.04UGcSUP388774%40masaka.moolenaar.net.