diff options
Diffstat (limited to 'deps/uv/src/win/util.c')
-rw-r--r-- | deps/uv/src/win/util.c | 91 |
1 files changed, 65 insertions, 26 deletions
diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 87a999ff5..be43d50d2 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -154,7 +154,7 @@ int uv_exepath(char* buffer, size_t* size_ptr) { uv_err_t uv_cwd(char* buffer, size_t size) { DWORD utf16_len; - WCHAR utf16_buffer[MAX_PATH + 1]; + WCHAR utf16_buffer[MAX_PATH]; int r; if (buffer == NULL || size == 0) { @@ -164,6 +164,10 @@ uv_err_t uv_cwd(char* buffer, size_t size) { utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); if (utf16_len == 0) { return uv__new_sys_error(GetLastError()); + } else if (utf16_len > MAX_PATH) { + /* This should be impossible; however the CRT has a code path to deal */ + /* with this scenario, so I added a check anyway. */ + return uv__new_artificial_error(UV_EIO); } /* utf16_len contains the length, *not* including the terminating null. */ @@ -195,45 +199,80 @@ uv_err_t uv_cwd(char* buffer, size_t size) { uv_err_t uv_chdir(const char* dir) { - uv_err_t err; - wchar_t* utf16Buffer = NULL; - size_t utf16Size; + WCHAR utf16_buffer[MAX_PATH]; + size_t utf16_len; + WCHAR drive_letter, env_var[4]; - if (!dir) { - err.code = UV_EINVAL; - goto done; + if (dir == NULL) { + return uv__new_artificial_error(UV_EINVAL); } - utf16Size = uv_utf8_to_utf16(dir, NULL, 0); - if (!utf16Size) { - err = uv__new_sys_error(GetLastError()); - goto done; + if (MultiByteToWideChar(CP_UTF8, + 0, + dir, + -1, + utf16_buffer, + MAX_PATH) == 0) { + DWORD error = GetLastError(); + /* The maximum length of the current working directory is 260 chars, */ + /* including terminating null. If it doesn't fit, the path name must be */ + /* too long. */ + if (error == ERROR_INSUFFICIENT_BUFFER) { + return uv__new_artificial_error(UV_ENAMETOOLONG); + } else { + return uv__new_sys_error(error); + } } - utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * utf16Size); - if (!utf16Buffer) { - err.code = UV_ENOMEM; - goto done; + if (!SetCurrentDirectoryW(utf16_buffer)) { + return uv__new_sys_error(GetLastError()); } - if (!uv_utf8_to_utf16(dir, utf16Buffer, utf16Size)) { - err = uv__new_sys_error(GetLastError()); - goto done; + /* Windows stores the drive-local path in an "hidden" environment variable, */ + /* which has the form "=C:=C:\Windows". SetCurrentDirectory does not */ + /* update this, so we'll have to do it. */ + utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + if (utf16_len == 0) { + return uv__new_sys_error(GetLastError()); + } else if (utf16_len > MAX_PATH) { + return uv__new_artificial_error(UV_EIO); } - if (_wchdir(utf16Buffer) == -1) { - err = uv__new_sys_error(_doserrno); - goto done; + /* The returned directory should not have a trailing slash, unless it */ + /* points at a drive root, like c:\. Remove it if needed. */ + if (utf16_buffer[utf16_len - 1] == L'\\' && + !(utf16_len == 3 && utf16_buffer[1] == L':')) { + utf16_len--; + utf16_buffer[utf16_len] = L'\0'; } - err = uv_ok_; + if (utf16_len < 2 || utf16_buffer[1] != L':') { + /* Doesn't look like a drive letter could be there - probably an UNC */ + /* path. TODO: Need to handle win32 namespaces like \\?\C:\ ? */ + drive_letter = 0; + } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') { + drive_letter = utf16_buffer[0]; + } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') { + /* Convert to uppercase. */ + drive_letter = utf16_buffer[0] - L'a' + L'A'; + } else { + /* Not valid. */ + drive_letter = 0; + } -done: - if (utf16Buffer) { - free(utf16Buffer); + if (drive_letter != 0) { + /* Construct the environment variable name and set it. */ + env_var[0] = L'='; + env_var[1] = drive_letter; + env_var[2] = L':'; + env_var[3] = L'\0'; + + if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { + return uv__new_sys_error(GetLastError()); + } } - return err; + return uv_ok_; } |