diff options
Diffstat (limited to 'src/execute.cc')
-rw-r--r-- | src/execute.cc | 227 |
1 files changed, 222 insertions, 5 deletions
diff --git a/src/execute.cc b/src/execute.cc index cbae899..58e0051 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -5,12 +5,22 @@ #include "ctor.h" -#include <unistd.h> -#include <cstring> -#include <sys/types.h> -#include <sys/wait.h> -#include <spawn.h> +#if !defined(_WIN32) + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> + #include <spawn.h> +#else + #define WINDOWS_LEAN_AND_MEAN + #include <windows.h> + #undef max +#endif + #include <iostream> +#include <cstring> +#include <vector> +#include <deque> +#include <filesystem> /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ @@ -30,7 +40,11 @@ public: { for(const auto& arg : args) { +#if !defined(_WIN32) push_back(strdup(arg.data())); +#else + push_back(_strdup(arg.data())); +#endif } push_back(nullptr); } @@ -44,6 +58,7 @@ public: } }; +#if !defined(_WIN32) int parent_waitpid(pid_t pid) { int status{}; @@ -55,9 +70,44 @@ int parent_waitpid(pid_t pid) return WEXITSTATUS(status); } +#endif //_WIN32 } // namespace :: +#if !defined(_WIN32) extern char **environ; // see 'man environ' +#endif + +#if defined(_WIN32) +//Returns the last Win32 error, in string format. Returns an empty string if there is no error. +std::string GetLastErrorAsString() +{ + //Get the error message ID, if any. + DWORD errorMessageID = ::GetLastError(); + if(errorMessageID == 0) + { + return std::string(); //No error message has been recorded + } + + LPSTR messageBuffer = nullptr; + + //Ask Win32 to give us the string version of that message ID. + //The parameters we pass in, tell Win32 to create the buffer that holds the message for us + // (because we don't yet know how long the message string will be). + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + + //Copy the error message into a std::string. + std::string message(messageBuffer, size); + + //Free the Win32's string's buffer. + LocalFree(messageBuffer); + + return message; +} +#endif // _WIN32 int execute(const ctor::settings& settings, const std::string& command, @@ -92,6 +142,8 @@ int execute(const ctor::settings& settings, std::cout << cmd << std::endl; } +#if !defined(_WIN32) + #if 1 auto pid = vfork(); if(pid == 0) @@ -133,5 +185,170 @@ int execute(const ctor::settings& settings, return system(cmd.data()); #endif +#else // _WIN32 + std::map<std::string, std::string> new_env; + { + auto env_strings = GetEnvironmentStrings(); + const char* ptr = env_strings; + std::string key; + std::string value; + bool key_state{true}; + while(true) + { + if(key_state) // searching for key + { + if(*ptr == '\0') + { + break; + } + + if(*ptr != '=') + { + key += *ptr; + } + else + { + key_state = false; + } + } + else + { + if(*ptr != '\0') + { + value += *ptr; + } + else + { + new_env.insert({key, value}); + key = {}; + value = {}; + key_state = true; + } + } + ++ptr; + } + FreeEnvironmentStrings(env_strings); + } + + // add new env vars (if any) + for(const auto& [key, value] : env) + { + //if(key == "CL" || key == "LINK" || key == "LIB") + { + new_env[key] = value; + } + } + + std::string env_str; + for(const auto& [k,v] : new_env) + { + env_str += k + "=" + v; + env_str += '\0'; + } + env_str += '\0'; + +// std::cout <<"#### Length of new env: " << env_str.size() << "\n"; + + STARTUPINFO si{}; +// si.hStdInput = GetStdHandle(((DWORD)-10)/*STD_INPUT_HANDLE*/); +// si.hStdOutput = GetStdHandle(((DWORD)-11)/*STD_OUTPUT_HANDLE*/); +// si.hStdError = GetStdHandle(((DWORD)-12)/*STD_ERROR_HANDLE*/); +// si.dwFlags = /*STARTF_USESTDHANDLES*/0x00000100; + + PROCESS_INFORMATION pi{}; + + si.cb = sizeof(si); + + // TODO: Use SetDllDirectory(...) to set DLL search directory + + if(terminate) + { + char tmpdir[MAX_PATH+1]; + int cnt{0}; + + // The returned string ends with a backslash + if(GetTempPathA(sizeof(tmpdir), tmpdir) == 0) + { + std::cerr << "Could not read TMP folder\n"; + return GetLastError(); + } + + char source[MAX_PATH]; + HMODULE module = GetModuleHandle(0); + GetModuleFileNameA(module, source, MAX_PATH); + + while(true) + { + if(cnt > 10) // If we need to try more than 10 times something is wrong + { + return 1; + } + + std::filesystem::path tmp_file = settings.builddir; + tmp_file /= "tmp"; + std::string target = tmp_file.string() + "-" + std::to_string(cnt); + std::cout << "move " << source << " => " << target <<std::endl; + if(MoveFileA(source, target.data())) + { + break; // success + } + + auto err = GetLastError(); + if(err == ERROR_ALREADY_EXISTS) + { + if(DeleteFileA(target.data())) + { + continue; // Try again + } + + err = GetLastError(); + if(err != ERROR_ACCESS_DENIED) + { + std::cerr << "Could not delete file\n"; + return err; + } + + cnt++; + continue; // Increment and try again + } + else + { + std::cerr << "Could not move file\n"; + return err; + } + } + } + + if(!CreateProcess(nullptr, // lpApplicationName + (char*)cmd.data(), // lpCommandLine + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + TRUE, // bInheritHandles + INHERIT_PARENT_AFFINITY | + //DETACHED_PROCESS | + ///*DEBUG_PROCESS*/ 0x00000001 | + ///*CREATE_NO_WINDOW*/ 0x08000000 | + ///*CREATE_BREAKAWAY_FROM_JOB*/ 0x01000000 | + /*CREATE_NEW_PROCESS_GROUP*/ 0x00000200 | + 0, // dwCreationFlags + env_str.data(), // lpEnvironment + nullptr, // lpCurrentDirectory + &si, // lpStartupInfo + &pi)) // lpProcessInformation + { + std::cout << "Could not execute " << command << ": " << + GetLastErrorAsString() << "\n"; + return 1;//_exit(1); // execve only returns if an error occurred + } + + // Now 'pi.hProcess' contains the process HANDLE, which you can use to + // wait for it like this: + const DWORD infinite = 0xFFFFFFFF; + WaitForSingleObject(pi.hProcess, infinite); + DWORD exitCode{0}; + GetExitCodeProcess(pi.hProcess, &exitCode); + return exitCode; +#endif // _WIN32 + return 1; } |