From 86fba381fdeadc00921d34024bdef357e6a56e6e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 24 Nov 2011 02:38:34 +0100 Subject: [PATCH] Windows: correctly resolve drive-relative paths --- lib/path.js | 28 ++++++++++------------- src/node.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 16 deletions(-) diff --git a/lib/path.js b/lib/path.js index c843d53..162a15f 100644 --- a/lib/path.js +++ b/lib/path.js @@ -86,7 +86,18 @@ if (isWindows) { resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1; i--) { - var path = (i >= 0) ? arguments[i] : process.cwd(); + var path; + if (i >= 0) { + path = arguments[i]; + } else if (!resolvedDevice) { + path = process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive. We're sure the device is not + // an unc path at this points, because unc paths are always absolute. + path = process._cwdForDrive(resolvedDevice[0]); + } // Skip empty and invalid entries if (typeof path !== 'string' || !path) { @@ -119,21 +130,6 @@ if (isWindows) { } } - if (!resolvedAbsolute && resolvedDevice) { - // If we still don't have an absolute path, - // prepend the current path for the device found. - - // TODO - // Windows stores the current directories for 'other' drives - // as hidden environment variables like =C:=c:\windows (literally) - // var deviceCwd = os.getCwdForDrive(resolvedDevice); - var deviceCwd = ''; - - // If there is no cwd set for the drive, it is at root - resolvedTail = deviceCwd + '\\' + resolvedTail; - resolvedAbsolute = true; - } - // Replace slashes (in UNC share name) by backslashes resolvedDevice = resolvedDevice.replace(/\//g, '\\'); diff --git a/src/node.cc b/src/node.cc index 1f5e2c9..d61fd82 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1178,6 +1178,76 @@ static Handle Cwd(const Arguments& args) { return scope.Close(cwd); } +#ifdef _WIN32 +static Handle CwdForDrive(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 1) { + Local exception = Exception::Error( + String::New("process._cwdForDrive takes exactly 1 argument.")); + return ThrowException(exception); + } + + Local driveLetter = args[0]->ToString(); + if (driveLetter->Length() != 1) { + Local exception = Exception::Error( + String::New("Drive name should be 1 character.")); + return ThrowException(exception); + } + + char drive; + + driveLetter->WriteAscii(&drive, 0, 1, 0); + if (drive >= 'a' && drive <= 'z') { + // Convert to uppercase + drive += 'A' - 'a'; + } else if (drive < 'A' || drive > 'Z') { + // Not a letter + Local exception = Exception::Error( + String::New("Drive name should be a letter.")); + return ThrowException(exception); + } + + WCHAR env_key[] = L"=X:"; + env_key[1] = (WCHAR) drive; + + DWORD len = GetEnvironmentVariableW(env_key, NULL, 0); + if (len == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + // There is no current directory for that drive. Default to drive + ":\". + Local cwd = String::Concat(String::New(&drive, 1), + String::New(":\\")); + return scope.Close(cwd); + + } else if (len == 0) { + // Error + Local exception = Exception::Error( + String::New(winapi_strerror(GetLastError()))); + return ThrowException(exception); + } + + WCHAR* buffer = new WCHAR[len]; + if (buffer == NULL) { + Local exception = Exception::Error( + String::New("Out of memory.")); + return ThrowException(exception); + } + + DWORD len2 = GetEnvironmentVariableW(env_key, buffer, len); + if (len2 == 0 || len2 >= len) { + // Error + delete[] buffer; + Local exception = Exception::Error( + String::New(winapi_strerror(GetLastError()))); + return ThrowException(exception); + } + + Local cwd = String::New(reinterpret_cast(buffer), len2); + delete[] buffer; + return scope.Close(cwd); +} +#endif + + static Handle Umask(const Arguments& args) { HandleScope scope; unsigned int old; @@ -1931,6 +2001,10 @@ Handle SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "chdir", Chdir); NODE_SET_METHOD(process, "cwd", Cwd); +#ifdef _WIN32 + NODE_SET_METHOD(process, "_cwdForDrive", CwdForDrive); +#endif + NODE_SET_METHOD(process, "umask", Umask); #ifdef __POSIX__ -- 2.7.4