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.

Raspunde prin e-mail lui