patch 9.1.1460: MS-Windows: too many strlen() calls in os_win32.c Commit: https://github.com/vim/vim/commit/e5297e39b3b7fcc1da55ef7869cc0c7714b01bc2 Author: John Marriott <basil...@internode.on.net> Date: Sun Jun 15 16:50:38 2025 +0200
patch 9.1.1460: MS-Windows: too many strlen() calls in os_win32.c Problem: MS-Windows: too many strlen() calls in os_win32.c Solution: refactor code and remove calls to strlen() and wcscat() (John Marriott) This commit does the following changes: - in mch_get_exe_name(): - refactor to remove call to wcsrchr(). - refactor to replace calls to wcscat() with wcscpy(). - move variables closer to where they are used. - change test to make sure that concatenating path and exe_pathw will fit inside the environment string (taking into account that path may be NULL). - in executable_exists(): - add namelen argument. - use string_T to store some strings. - refactor to remove calls to STRLEN() (via STRCAT()). - in mch_getperm(): - move call to mch_stat() into return statement and drop unneeded variable. - in mch_wrename(): - refactor to use wide character comparisons. - some cosmetic code styling changes (removing extraneous spaces, etc). closes: 17542 Signed-off-by: John Marriott <basil...@internode.on.net> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/os_win32.c b/src/os_win32.c index bc8b2a914..0f71a3cd9 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -431,20 +431,19 @@ wait_for_single_object( void mch_get_exe_name(void) { - // Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned - // as the maximum length that works. Add 1 for a NUL byte and 5 for - // "PATH=". -#define MAX_ENV_PATH_LEN (8191 + 1 + 5) - WCHAR temp[MAX_ENV_PATH_LEN]; - WCHAR buf[MAX_PATH]; int updated = FALSE; static int enc_prev = -1; + WCHAR *path; + size_t pathlen; if (exe_name == NULL || exe_pathw == NULL || enc_prev != enc_codepage) { + WCHAR buf[MAX_PATH]; + WCHAR *p; + // store the name of the executable, may be used for $VIM - GetModuleFileNameW(NULL, buf, MAX_PATH); - if (*buf != NUL) + p = buf + GetModuleFileNameW(NULL, buf, MAX_PATH); + if (p > buf) { if (enc_codepage == -1) enc_codepage = GetACP(); @@ -452,9 +451,18 @@ mch_get_exe_name(void) exe_name = utf16_to_enc(buf, NULL); enc_prev = enc_codepage; - WCHAR *wp = wcsrchr(buf, '\'); - if (wp != NULL) - *wp = NUL; + // truncate the buffer at the last path separator + // to isolate the path. + do + { + --p; + if (*p == L'\') + { + *p = L' + break; + } + } while (p > buf); + vim_free(exe_pathw); exe_pathw = _wcsdup(buf); updated = TRUE; @@ -467,33 +475,49 @@ mch_get_exe_name(void) // Append our starting directory to $PATH, so that when doing // "!xxd" it's found in our starting directory. Needed because // SearchPath() also looks there. - WCHAR *p = _wgetenv(L"PATH"); - if (p == NULL || wcslen(p) + wcslen(exe_pathw) + 2 + 5 < MAX_ENV_PATH_LEN) + path = _wgetenv(L"PATH"); + pathlen = (path == NULL) ? 0 : wcslen(path); + + // Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned + // as the maximum length that works. Add an extra 7 characters (5 for + // "PATH=", 1 for a potential ";" and 1 for the NUL byte). +#define EXTRA_LEN 7 +#define MAX_ENV_STRING_LEN 8191 + + if (pathlen + wcslen(exe_pathw) < MAX_ENV_STRING_LEN) { - wcscpy(temp, L"PATH="); + WCHAR temp[MAX_ENV_STRING_LEN + EXTRA_LEN] = L"PATH="; + size_t templen = 5; - if (p == NULL || *p == NUL) - wcscat(temp, exe_pathw); + if (pathlen == 0) + wcscpy(temp + templen, exe_pathw); else { - wcscat(temp, p); + wcscpy(temp + templen, path); // Check if exe_path is already included in $PATH. if (wcsstr(temp, exe_pathw) == NULL) { + templen += pathlen; + // Append ';' if $PATH doesn't end with it. - size_t len = wcslen(temp); - if (temp[len - 1] != L';') - wcscat(temp, L";"); + if (temp[templen - 1] != L';') + { + wcscpy(temp + templen, L";"); + ++templen; + } - wcscat(temp, exe_pathw); + wcscpy(temp + templen, exe_pathw); } } + _wputenv(temp); #ifdef libintl_wputenv libintl_wputenv(temp); #endif } +#undef EXTRA_LEN +#undef MAX_ENV_PATH_LEN } /* @@ -734,7 +758,7 @@ get_forwarded_dll(HINSTANCE hInst) if (p - name + 1 > sizeof(buf)) return NULL; strncpy(buf, name, p - name); - buf[p - name] = ' + buf[p - name] = NUL; return GetModuleHandleA(buf); } #endif @@ -1093,7 +1117,6 @@ win32_kbd_patch_key( static WORD awAnsiCode[2]; static BYTE abKeystate[256]; - if (s_iIsDead == 2) { pker->uChar.UnicodeChar = (WCHAR) awAnsiCode[1]; @@ -1174,7 +1197,7 @@ decode_key_event( return TRUE; } - for (i = ARRAY_LENGTH(VirtKeyMap); --i >= 0; ) + for (i = ARRAY_LENGTH(VirtKeyMap); --i >= 0; ) { if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode) { @@ -2787,23 +2810,22 @@ executable_file(char *name, char_u **path) * the allocated memory. */ static int -executable_exists(char *name, char_u **path, int use_path, int use_pathext) +executable_exists(char *name, size_t namelen, 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; + char_u *p; + string_T pathbuf = {NULL, 0}; + int pathbuf_allocated = FALSE; + string_T pathext = {NULL, 0}; + int pathext_allocated = FALSE; char_u *shname = NULL; int noext = FALSE; int retval = FALSE; - if (len >= sizeof(buf)) // safety check + if (namelen >= sizeof(buf)) // safety check return FALSE; // Using the name directly when a Unix-shell like 'shell'. @@ -2815,17 +2837,25 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) if (use_pathext) { - pathext = mch_getenv("PATHEXT"); - if (pathext == NULL) - pathext = (char_u *)".com;.exe;.bat;.cmd"; + pathext.string = mch_getenv("PATHEXT"); + if (pathext.string == NULL) + { + pathext.string = (char_u *)".com;.exe;.bat;.cmd"; + pathext.length = 19; + } + else + pathext.length = STRLEN(pathext.string); if (noext == FALSE) { + char_u *e; + size_t plen; + /* * Loop over all extensions in $PATHEXT. * Check "name" ends with extension. */ - p = pathext; + p = pathext.string; while (*p) { if (p[0] == ';' @@ -2837,10 +2867,10 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) } e = vim_strchr(p, ';'); if (e == NULL) - e = p + STRLEN(p); - tmplen = e - p; + e = pathext.string + pathext.length; + plen = (size_t)(e - p); - if (_strnicoll(name + len - tmplen, (char *)p, tmplen) == 0) + if (_strnicoll(name + namelen - plen, (char *)p, plen) == 0) { noext = TRUE; break; @@ -2852,20 +2882,27 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) } // Prepend single "." to pathext, it means no extension added. - if (pathext == NULL) - pathext = (char_u *)"."; + if (pathext.string == NULL) + { + pathext.string = (char_u *)"."; + pathext.length = 1; + } else if (noext == TRUE) { - if (pathextbuf == NULL) - pathextbuf = alloc(STRLEN(pathext) + 3); - if (pathextbuf == NULL) + char_u *tmp; + + tmp = alloc(pathext.length + 3); + if (tmp == NULL) { retval = FALSE; goto theend; } - STRCPY(pathextbuf, ".;"); - STRCAT(pathextbuf, pathext); - pathext = pathextbuf; + + STRCPY(tmp, ".;"); + STRCPY(tmp + 2, pathext.string); + pathext.string = tmp; + pathext.length += 2; + pathext_allocated = TRUE; } // Use $PATH when "use_path" is TRUE and "name" is basename. @@ -2874,18 +2911,23 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) p = mch_getenv("PATH"); if (p != NULL) { - pathbuf = alloc(STRLEN(p) + 3); - if (pathbuf == NULL) + size_t plen = STRLEN(p); + + pathbuf.string = alloc(plen + 3); + if (pathbuf.string == NULL) { retval = FALSE; goto theend; } if (mch_getenv("NoDefaultCurrentDirectoryInExePath") == NULL) - STRCPY(pathbuf, ".;"); - else - *pathbuf = NUL; - STRCAT(pathbuf, p); + { + STRCPY(pathbuf.string, ".;"); + pathbuf.length = 2; + } + STRCPY(pathbuf.string + pathbuf.length, p); + pathbuf.length += plen; + pathbuf_allocated = TRUE; } } @@ -2893,9 +2935,17 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) * Walk through all entries in $PATH to check if "name" exists there and * is an executable file. */ - p = (pathbuf != NULL) ? pathbuf : (char_u *)"."; + if (pathbuf.string == NULL) + { + pathbuf.string = (char_u *)"."; + pathbuf.length = 1; + } + p = pathbuf.string; while (*p) { + char_u *e; + size_t buflen; + if (*p == ';') // Skip empty entry { ++p; @@ -2903,50 +2953,57 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) } e = vim_strchr(p, ';'); if (e == NULL) - e = p + STRLEN(p); + e = pathbuf.string + pathbuf.length; - if (e - p + len + 2 > sizeof(buf)) + if (e - p + namelen + 2 > sizeof(buf)) { retval = FALSE; goto theend; } // A single "." that means current dir. if (e - p == 1 && *p == '.') + { STRCPY(buf, name); + buflen = namelen; + } else { - vim_strncpy(buf, p, e - p); - add_pathsep(buf); - STRCAT(buf, name); + buflen = vim_snprintf_safelen( + (char *)buf, + sizeof(buf), + "%.*s%s%s", (int)(e - p), p, + !after_pathsep(p, e - 1) ? PATHSEPSTR : "", + name); } - tmplen = STRLEN(buf); /* * Loop over all extensions in $PATHEXT. * Check "name" with extension added. */ - p = pathext; + p = pathext.string; while (*p) { + char_u *e2; + if (*p == ';') { // Skip empty entry ++p; continue; } - e2 = vim_strchr(p, (int)';'); + e2 = vim_strchr(p, ';'); if (e2 == NULL) - e2 = p + STRLEN(p); + e2 = pathext.string + pathext.length; if (!(p[0] == '.' && (p[1] == NUL || p[1] == ';'))) { // Not a single "." that means no extension is added. - if (e2 - p + tmplen + 1 > sizeof(buf)) + if (e2 - p + buflen + 1 > sizeof(buf)) { retval = FALSE; goto theend; } - vim_strncpy(buf + tmplen, p, e2 - p); + vim_strncpy(buf + buflen, p, e2 - p); } if (executable_file((char *)buf, path)) { @@ -2961,8 +3018,10 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) } theend: - free(pathextbuf); - free(pathbuf); + if (pathbuf_allocated) + free(pathbuf.string); + if (pathext_allocated) + free(pathext.string); return retval; } @@ -3028,7 +3087,8 @@ mch_init_g(void) // Note: 10 is length of 'vimrun.exe'. if (exe_pathlen + 10 >= sizeof(vimrun_location)) { - if (executable_exists("vimrun.exe", NULL, TRUE, FALSE)) + if (executable_exists("vimrun.exe", STRLEN_LITERAL("vimrun.exe"), + NULL, TRUE, FALSE)) s_dont_use_vimrun = FALSE; } else @@ -3069,7 +3129,8 @@ mch_init_g(void) s_dont_use_vimrun = FALSE; } } - else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE)) + else if (executable_exists("vimrun.exe", STRLEN_LITERAL("vimrun.exe"), + NULL, TRUE, FALSE)) s_dont_use_vimrun = FALSE; } @@ -3084,7 +3145,8 @@ mch_init_g(void) * If "findstr.exe" doesn't exist, use "grep -n" for 'grepprg'. * Otherwise the default "findstr /n" is used. */ - if (!executable_exists("findstr.exe", NULL, TRUE, FALSE)) + if (!executable_exists("findstr.exe", STRLEN_LITERAL("findstr.exe"), + NULL, TRUE, FALSE)) set_option_value_give_err((char_u *)"grepprg", 0, (char_u *)"grep -n", 0); @@ -3323,7 +3385,6 @@ RestoreConsoleBuffer( SMALL_RECT WriteRegion; int i; - if (cb == NULL || !cb->IsValid) return FALSE; @@ -3745,10 +3806,10 @@ fname_case( else { size_t namelen = STRLEN(name); + if (namelen >= STRLEN(q)) vim_strncpy(name, q, namelen); } - vim_free(q); } } @@ -3827,7 +3888,7 @@ mch_process_running(long pid) if (hProcess == NULL) return FALSE; // might not have access - if (GetExitCodeProcess(hProcess, &status) ) + if (GetExitCodeProcess(hProcess, &status)) ret = status == STILL_ACTIVE; CloseHandle(hProcess); return ret; @@ -3887,10 +3948,10 @@ mch_dirname( mch_getperm(char_u *name) { stat_T st; - int n; - n = mch_stat((char *)name, &st); - return n == 0 ? (long)(unsigned short)st.st_mode : -1L; + return (mch_stat((char *)name, &st) == 0) + ? (long)(unsigned short)st.st_mode + : -1L; } @@ -4184,7 +4245,7 @@ mch_writable(char_u *name) int mch_can_exe(char_u *name, char_u **path, int use_path UNUSED) { - return executable_exists((char *)name, path, TRUE, TRUE); + return executable_exists((char *)name, STRLEN(name), path, TRUE, TRUE); } /* @@ -7645,19 +7706,33 @@ mch_total_mem(int special UNUSED) mch_wrename(WCHAR *wold, WCHAR *wnew) { WCHAR *p; - int i; + WCHAR *q; WCHAR szTempFile[_MAX_PATH + 1]; WCHAR szNewPath[_MAX_PATH + 1]; HANDLE hf; // No need to play tricks unless the file name contains a "~" as the // seventh character. - p = wold; - for (i = 0; wold[i] != NUL; ++i) - if ((wold[i] == '/' || wold[i] == '\' || wold[i] == ':') - && wold[i + 1] != 0) - p = wold + i + 1; - if ((int)(wold + i - p) < 8 || p[6] != '~') + for (p = q = wold; *p != L' + { + switch (*p) + { + case L'/': + case L'\': + case L':': + if (*(p + 1) != L' + q = p + 1; // point to the character after the path + // separator. + break; + + default: + break; + } + } + + // If the length of the file name is less than 8 characters or the seventh + // character is not a "~', do a normal move. + if ((int)(p - q) < 8 || q[6] != L'~') return (MoveFileW(wold, wnew) == 0); // Get base path of new file name. Undocumented feature: If pszNewFile is @@ -9021,7 +9096,7 @@ GetWin32Error(void) // remove trailing char *pcrlf = strstr(msg, " "); if (pcrlf != NULL) - *pcrlf = ' + *pcrlf = NUL; oldmsg = msg; return msg; } diff --git a/src/version.c b/src/version.c index 327e8c85a..64e0e7807 100644 --- a/src/version.c +++ b/src/version.c @@ -709,6 +709,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1460, /**/ 1459, /**/ -- -- 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 vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1uQoq3-000EfE-Dw%40256bit.org.