Mercurial > vim
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) |