// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "execute.h" #include #if !defined(_WIN32) #include #include #include #include #else #include #include #include #endif #include /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ https://github.com/famzah/popen-noshell/commit/1f9eaf4eeef348d1efe0f3c7fe8ab670653cfbb1 https://blog.famzah.net/2018/12/19/posix_spawn-performance-benchmarks-and-usage-examples/ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork/5207945#5207945 */ #if !defined(_WIN32) namespace { int parent_waitpid(pid_t pid) { int status; if(waitpid(pid, &status, 0) != pid) { return 1; } return WEXITSTATUS(status); } } // namespace :: #endif int execute(const std::string& command, const std::vector& args, const std::map& env, bool verbose, bool terminate) { std::vector argv; argv.push_back(command.data()); for(const auto& arg : args) { argv.push_back(arg.data()); } argv.push_back(nullptr); std::string cmd; for(const auto& arg : argv) { if(arg == nullptr) { break; } if(!cmd.empty()) { cmd += " "; } cmd += arg; } if(verbose) { std::cout << cmd << "\n"; } #if !defined(_WIN32) #if 1 auto pid = vfork(); if(pid == 0) { for(const auto& [key, value] : env) { setenv(key, value); } // TODO: use execvpe to set LD_LIBRARY_PATH execv(command.data(), (char**)argv.data()); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; _exit(1); // execv only returns if an error occurred } return parent_waitpid(pid); #elif 0 pid_t pid; const char * envp[] = {nullptr}; // TODO: use this to set LD_LIBRARY_PATH if(posix_spawn(&pid, command.data(), nullptr, nullptr, (char**)argv.data(), envp)) { return 1; } return parent_waitpid(pid); #else return system(cmd.data()); #endif #else std::map 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'; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si,sizeof(si)); si.cb=sizeof(si); ZeroMemory(&pi,sizeof(pi)); // TODO: Use SetDllDirectory(...) to set DLL search directory if(!CreateProcess(nullptr, //"C:/WINDOWS/notepad.exe", //"notepad.exe c:/readme.txt",0,0,0,0,0,0,&si,&pi)) (char*)cmd.data(),0,0,0,0,env_str.data(),0,&si,&pi)) { return 1; // Could not start process; } if(terminate) { ExitProcess(0); } // 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 }