patch 8.1.0453: MS-Windows: executable() is not reliable

Problem:    MS-Windows: executable() is not reliable.
Solution:   Use $PATHEXT properly. (Yasuhiro Matsumoto, closes #3412)
diff --git a/src/os_win32.c b/src/os_win32.c
index 4ba060b..9d1d8cb 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -3535,21 +3535,44 @@
 {
     char_u	buf[_MAX_PATH];
     int		len = (int)STRLEN(name);
-    char_u	*p;
+    char_u	*p, *saved;
 
     if (len >= _MAX_PATH)	/* safety check */
 	return FALSE;
 
-    /* If there already is an extension try using the name directly.  Also do
-     * this with a Unix-shell like 'shell'. */
-    if (vim_strchr(gettail(name), '.') != NULL
-			       || strstr((char *)gettail(p_sh), "sh") != NULL)
+    /* Ty 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, _MAX_PATH - 1);
     p = mch_getenv("PATHEXT");
     if (p == NULL)
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index cc82f81..17f7c19 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -800,6 +800,19 @@
   bw!
 endfunc
 
+func Test_Executable()
+  if has('win32')
+    call assert_equal(1, executable('notepad'))
+    call assert_equal(1, executable('notepad.exe'))
+    call assert_equal(0, executable('notepad.exe.exe'))
+    call assert_equal(0, executable('shell32.dll'))
+    call assert_equal(0, executable('win.ini'))
+  elseif has('unix')
+    call assert_equal(1, executable('cat'))
+    call assert_equal(0, executable('dog'))
+  endif
+endfunc
+
 func Test_hostname()
   let hostname_vim = hostname()
   if has('unix')
diff --git a/src/version.c b/src/version.c
index b50ce81..dee74d3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -793,6 +793,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    453,
+/**/
     452,
 /**/
     451,