comparison src/os_win32.c @ 20593:89b0f161e6a6 v8.2.0850

patch 8.2.0850: MS-Windows: exepath() works different from cmd.exe Commit: https://github.com/vim/vim/commit/95da136142628e06425f9d9eb2d1ca56a9e48feb Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 30 18:37:55 2020 +0200 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 https://github.com/vim/vim/issues/6115)
author Bram Moolenaar <Bram@vim.org>
date Sat, 30 May 2020 18:45:04 +0200
parents ecaceb5c5644
children 9064044fd4f6
comparison
equal deleted inserted replaced
20592:3b819401b347 20593:89b0f161e6a6
2078 # include <shellapi.h> // required for FindExecutable() 2078 # include <shellapi.h> // required for FindExecutable()
2079 # endif 2079 # endif
2080 #endif 2080 #endif
2081 2081
2082 /* 2082 /*
2083 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH. 2083 * Return TRUE if "name" is an executable file, FALSE if not or it doesn't exist.
2084 * If "use_path" is FALSE: Return TRUE if "name" exists.
2085 * When returning TRUE and "path" is not NULL save the path and set "*path" to 2084 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2086 * the allocated memory. 2085 * the allocated memory.
2087 * TODO: Should somehow check if it's really executable. 2086 * TODO: Should somehow check if it's really executable.
2088 */ 2087 */
2089 static int 2088 static int
2090 executable_exists(char *name, char_u **path, int use_path) 2089 executable_file(char *name, char_u **path)
2091 { 2090 {
2092 WCHAR *p; 2091 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
2093 WCHAR fnamew[_MAX_PATH]; 2092 {
2094 WCHAR *dumw; 2093 if (path != NULL)
2095 WCHAR *wcurpath, *wnewpath; 2094 *path = FullName_save((char_u *)name, FALSE);
2096 long n; 2095 return TRUE;
2097 2096 }
2098 if (!use_path) 2097 return FALSE;
2099 { 2098 }
2100 if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name)) 2099
2101 { 2100 /*
2102 if (path != NULL) 2101 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
2102 * If "use_path" is FALSE: Return TRUE if "name" exists.
2103 * If "use_pathext" is TRUE search "name" with extensions in $PATHEXT.
2104 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2105 * the allocated memory.
2106 */
2107 static int
2108 executable_exists(char *name, char_u **path, int use_path, int use_pathext)
2109 {
2110 // WinNT and later can use _MAX_PATH wide characters for a pathname, which
2111 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
2112 // UTF-8.
2113 char_u buf[_MAX_PATH * 3];
2114 size_t len = STRLEN(name);
2115 size_t tmplen;
2116 char_u *p, *e, *e2;
2117 char_u *pathbuf = NULL;
2118 char_u *pathext = NULL;
2119 char_u *pathextbuf = NULL;
2120 int noext = FALSE;
2121 int retval = FALSE;
2122
2123 if (len >= sizeof(buf)) // safety check
2124 return FALSE;
2125
2126 // Using the name directly when a Unix-shell like 'shell'.
2127 if (strstr((char *)gettail(p_sh), "sh") != NULL)
2128 noext = TRUE;
2129
2130 if (use_pathext)
2131 {
2132 pathext = mch_getenv("PATHEXT");
2133 if (pathext == NULL)
2134 pathext = (char_u *)".com;.exe;.bat;.cmd";
2135
2136 if (noext == FALSE)
2137 {
2138 /*
2139 * Loop over all extensions in $PATHEXT.
2140 * Check "name" ends with extension.
2141 */
2142 p = pathext;
2143 while (*p)
2103 { 2144 {
2104 if (mch_isFullName((char_u *)name)) 2145 if (p[0] == ';'
2105 *path = vim_strsave((char_u *)name); 2146 || (p[0] == '.' && (p[1] == NUL || p[1] == ';')))
2106 else 2147 {
2107 *path = FullName_save((char_u *)name, FALSE); 2148 // Skip empty or single ".".
2149 ++p;
2150 continue;
2151 }
2152 e = vim_strchr(p, ';');
2153 if (e == NULL)
2154 e = p + STRLEN(p);
2155 tmplen = e - p;
2156
2157 if (_strnicoll(name + len - tmplen, (char *)p, tmplen) == 0)
2158 {
2159 noext = TRUE;
2160 break;
2161 }
2162
2163 p = e;
2108 } 2164 }
2109 return TRUE; 2165 }
2110 } 2166 }
2111 return FALSE; 2167
2112 } 2168 // Prepend single "." to pathext, it's means no extension added.
2113 2169 if (pathext == NULL)
2114 p = enc_to_utf16((char_u *)name, NULL); 2170 pathext = (char_u *)".";
2115 if (p == NULL) 2171 else if (noext == TRUE)
2116 return FALSE; 2172 {
2117 2173 if (pathextbuf == NULL)
2118 wcurpath = _wgetenv(L"PATH"); 2174 pathextbuf = alloc(STRLEN(pathext) + 3);
2119 wnewpath = ALLOC_MULT(WCHAR, wcslen(wcurpath) + 3); 2175 if (pathextbuf == NULL)
2120 if (wnewpath == NULL) 2176 {
2121 return FALSE; 2177 retval = FALSE;
2122 wcscpy(wnewpath, L".;"); 2178 goto theend;
2123 wcscat(wnewpath, wcurpath); 2179 }
2124 n = (long)SearchPathW(wnewpath, p, NULL, _MAX_PATH, fnamew, &dumw); 2180 STRCPY(pathextbuf, ".;");
2125 vim_free(wnewpath); 2181 STRCAT(pathextbuf, pathext);
2126 vim_free(p); 2182 pathext = pathextbuf;
2127 if (n == 0) 2183 }
2128 return FALSE; 2184
2129 if (GetFileAttributesW(fnamew) & FILE_ATTRIBUTE_DIRECTORY) 2185 // Use $PATH when "use_path" is TRUE and "name" is basename.
2130 return FALSE; 2186 if (use_path && gettail((char_u *)name) == (char_u *)name)
2131 if (path != NULL) 2187 {
2132 *path = utf16_to_enc(fnamew, NULL); 2188 p = mch_getenv("PATH");
2133 return TRUE; 2189 if (p != NULL)
2190 {
2191 pathbuf = alloc(STRLEN(p) + 3);
2192 if (pathbuf == NULL)
2193 {
2194 retval = FALSE;
2195 goto theend;
2196 }
2197 STRCPY(pathbuf, ".;");
2198 STRCAT(pathbuf, p);
2199 }
2200 }
2201
2202 /*
2203 * Walk through all entries in $PATH to check if "name" exists there and
2204 * is an executable file.
2205 */
2206 p = (pathbuf != NULL) ? pathbuf : (char_u *)".";
2207 while (*p)
2208 {
2209 if (*p == ';') // Skip empty entry
2210 {
2211 ++p;
2212 continue;
2213 }
2214 e = vim_strchr(p, ';');
2215 if (e == NULL)
2216 e = p + STRLEN(p);
2217
2218 if (e - p + len + 2 > sizeof(buf))
2219 {
2220 retval = FALSE;
2221 goto theend;
2222 }
2223 // A single "." that means current dir.
2224 if (e - p == 1 && *p == '.')
2225 STRCPY(buf, name);
2226 else
2227 {
2228 vim_strncpy(buf, p, e - p);
2229 add_pathsep(buf);
2230 STRCAT(buf, name);
2231 }
2232 tmplen = STRLEN(buf);
2233
2234 /*
2235 * Loop over all extensions in $PATHEXT.
2236 * Check "name" with extension added.
2237 */
2238 p = pathext;
2239 while (*p)
2240 {
2241 if (*p == ';')
2242 {
2243 // Skip empty entry
2244 ++p;
2245 continue;
2246 }
2247 e2 = vim_strchr(p, (int)';');
2248 if (e2 == NULL)
2249 e2 = p + STRLEN(p);
2250
2251 if (!(p[0] == '.' && (p[1] == NUL || p[1] == ';')))
2252 {
2253 // Not a single "." that means no extension is added.
2254 if (e2 - p + tmplen + 1 > sizeof(buf))
2255 {
2256 retval = FALSE;
2257 goto theend;
2258 }
2259 vim_strncpy(buf + tmplen, p, e2 - p);
2260 }
2261 if (executable_file((char *)buf, path))
2262 {
2263 retval = TRUE;
2264 goto theend;
2265 }
2266
2267 p = e2;
2268 }
2269
2270 p = e;
2271 }
2272
2273 theend:
2274 free(pathextbuf);
2275 free(pathbuf);
2276 return retval;
2134 } 2277 }
2135 2278
2136 #if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \ 2279 #if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
2137 (defined(_MSC_VER) && _MSC_VER >= 1400) 2280 (defined(_MSC_VER) && _MSC_VER >= 1400)
2138 /* 2281 /*
2208 STRCPY(gettail(vimrun_location), "vimrun "); 2351 STRCPY(gettail(vimrun_location), "vimrun ");
2209 2352
2210 vimrun_path = (char *)vim_strsave(vimrun_location); 2353 vimrun_path = (char *)vim_strsave(vimrun_location);
2211 s_dont_use_vimrun = FALSE; 2354 s_dont_use_vimrun = FALSE;
2212 } 2355 }
2213 else if (executable_exists("vimrun.exe", NULL, TRUE)) 2356 else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE))
2214 s_dont_use_vimrun = FALSE; 2357 s_dont_use_vimrun = FALSE;
2215 2358
2216 // Don't give the warning for a missing vimrun.exe right now, but only 2359 // Don't give the warning for a missing vimrun.exe right now, but only
2217 // when vimrun was supposed to be used. Don't bother people that do 2360 // when vimrun was supposed to be used. Don't bother people that do
2218 // not need vimrun.exe. 2361 // not need vimrun.exe.
2222 2365
2223 /* 2366 /*
2224 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'. 2367 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2225 * Otherwise the default "findstr /n" is used. 2368 * Otherwise the default "findstr /n" is used.
2226 */ 2369 */
2227 if (!executable_exists("findstr.exe", NULL, TRUE)) 2370 if (!executable_exists("findstr.exe", NULL, TRUE, FALSE))
2228 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0); 2371 set_option_value((char_u *)"grepprg", 0, (char_u *)"grep -n", 0);
2229 2372
2230 # ifdef FEAT_CLIPBOARD 2373 # ifdef FEAT_CLIPBOARD
2231 win_clip_init(); 2374 win_clip_init();
2232 # endif 2375 # endif
3304 * the allocated memory. 3447 * the allocated memory.
3305 */ 3448 */
3306 int 3449 int
3307 mch_can_exe(char_u *name, char_u **path, int use_path) 3450 mch_can_exe(char_u *name, char_u **path, int use_path)
3308 { 3451 {
3309 // WinNT and later can use _MAX_PATH wide characters for a pathname, which 3452 return executable_exists((char *)name, path, TRUE, TRUE);
3310 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
3311 // UTF-8.
3312 char_u buf[_MAX_PATH * 3];
3313 int len = (int)STRLEN(name);
3314 char_u *p, *saved;
3315
3316 if (len >= sizeof(buf)) // safety check
3317 return FALSE;
3318
3319 // Try using the name directly when a Unix-shell like 'shell'.
3320 if (strstr((char *)gettail(p_sh), "sh") != NULL)
3321 if (executable_exists((char *)name, path, use_path))
3322 return TRUE;
3323
3324 /*
3325 * Loop over all extensions in $PATHEXT.
3326 */
3327 p = mch_getenv("PATHEXT");
3328 if (p == NULL)
3329 p = (char_u *)".com;.exe;.bat;.cmd";
3330 saved = vim_strsave(p);
3331 if (saved == NULL)
3332 return FALSE;
3333 p = saved;
3334 while (*p)
3335 {
3336 char_u *tmp = vim_strchr(p, ';');
3337
3338 if (tmp != NULL)
3339 *tmp = NUL;
3340 if (_stricoll((char *)name + len - STRLEN(p), (char *)p) == 0
3341 && executable_exists((char *)name, path, use_path))
3342 {
3343 vim_free(saved);
3344 return TRUE;
3345 }
3346 if (tmp == NULL)
3347 break;
3348 p = tmp + 1;
3349 }
3350 vim_free(saved);
3351
3352 vim_strncpy(buf, name, sizeof(buf) - 1);
3353 p = mch_getenv("PATHEXT");
3354 if (p == NULL)
3355 p = (char_u *)".com;.exe;.bat;.cmd";
3356 while (*p)
3357 {
3358 if (p[0] == '.' && (p[1] == NUL || p[1] == ';'))
3359 {
3360 // A single "." means no extension is added.
3361 buf[len] = NUL;
3362 ++p;
3363 if (*p)
3364 ++p;
3365 }
3366 else
3367 copy_option_part(&p, buf + len, sizeof(buf) - len, ";");
3368 if (executable_exists((char *)buf, path, use_path))
3369 return TRUE;
3370 }
3371 return FALSE;
3372 } 3453 }
3373 3454
3374 /* 3455 /*
3375 * Check what "name" is: 3456 * Check what "name" is:
3376 * NODE_NORMAL: file or directory (or doesn't exist) 3457 * NODE_NORMAL: file or directory (or doesn't exist)