Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix resolution of executable names on Windows. #1257

Closed
wants to merge 10 commits into from
54 changes: 54 additions & 0 deletions src/runtime/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Author: Jared Roesch
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <shellapi.h>
#else
#include <unistd.h>
#include <fcntl.h>
Expand Down Expand Up @@ -108,6 +109,29 @@ static void setup_stdio(SECURITY_ATTRIBUTES * saAttr, HANDLE * theirs, object **
lean_unreachable();
}


const char* ws = " \t\n\r\f\v";

// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}

// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
s.erase(0, s.find_first_not_of(t));
return s;
}

// trim from both ends of string (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
return ltrim(rtrim(s, t), t);
}

// This code is adapted from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
static obj_res spawn(string_ref const & proc_name, array_ref<string_ref> const & args, stdio stdin_mode, stdio stdout_mode,
stdio stderr_mode, option_ref<string_ref> const & cwd, array_ref<pair_ref<string_ref, option_ref<string_ref>>> const & env) {
Expand All @@ -128,6 +152,36 @@ static obj_res spawn(string_ref const & proc_name, array_ref<string_ref> const &

std::string command = proc_name.to_std_string();

// remove any whitespace and quotes so we can check if the file exists.
trim(command, ws);
trim(command, "\"");

const char* extensions[] = { ".exe", ".com", ".cmd" };
const DWORD MAX_SIZE = 32767;
char buffer[MAX_SIZE];
LPSTR lpFilePart = NULL;
DWORD rc = 0;
for (auto ext : extensions) {
rc = SearchPath(NULL, command.c_str(), ext, MAX_SIZE, buffer, &lpFilePart);
if (rc != 0) {
break;
}
}

if (rc != 0) {
// buffer now contains the executable that Windows would have executed if you
// typed that name in a terminal window. This can change the name of the command.
// For example, "npm" becomes "C:\Program Files\nodejs\npm.cmd" and not the bash
// shell script named "C:\Program Files\nodejs\npm".
command = buffer;
}

if (command.find(' ') != std::string::npos || command.find('\t') != std::string::npos) {
// path contains whitespace so it needs quotes.
command.insert(command.begin(), '"');
command += "\"";
}

// This needs some thought, on Windows we must pass a command string
// which is a valid command, that is a fully assembled command to be executed.
//
Expand Down