2016-01-07 08:14:33 +00:00
|
|
|
#pragma once
|
2015-12-21 09:16:47 +00:00
|
|
|
|
|
|
|
//auto execute(const string& name, const string& args...) -> string;
|
|
|
|
//[[synchronous]]
|
|
|
|
//executes program, waits for completion, and returns data written to stdout
|
|
|
|
|
|
|
|
//auto invoke(const string& name, const string& args...) -> void;
|
|
|
|
//[[asynchronous]]
|
|
|
|
//if a program is specified, it is executed with the arguments provided
|
|
|
|
//if a file is specified, the file is opened using the program associated with said file type
|
|
|
|
//if a folder is specified, the folder is opened using the associated file explorer
|
|
|
|
//if a URL is specified, the default web browser is opened and pointed at the URL requested
|
|
|
|
|
|
|
|
#include <nall/intrinsics.hpp>
|
|
|
|
#include <nall/string.hpp>
|
|
|
|
|
|
|
|
namespace nall {
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
struct execute_result_t {
|
|
|
|
explicit operator bool() const { return code == EXIT_SUCCESS; }
|
|
|
|
|
|
|
|
int code = EXIT_FAILURE;
|
|
|
|
string output;
|
|
|
|
string error;
|
|
|
|
};
|
|
|
|
|
2017-09-29 10:36:35 +00:00
|
|
|
#if defined(PLATFORM_MACOS) || defined(PLATFORM_LINUX) || defined(PLATFORM_BSD)
|
2015-12-21 09:16:47 +00:00
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
|
|
|
|
int fdout[2];
|
|
|
|
int fderr[2];
|
|
|
|
if(pipe(fdout) == -1) return {};
|
|
|
|
if(pipe(fderr) == -1) return {};
|
2015-12-21 09:16:47 +00:00
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
if(pid == 0) {
|
|
|
|
const char* argv[1 + sizeof...(p) + 1];
|
|
|
|
const char** argp = argv;
|
2018-07-25 12:24:03 +00:00
|
|
|
vector<string> argl(forward<P>(p)...);
|
2015-12-21 09:16:47 +00:00
|
|
|
*argp++ = (const char*)name;
|
|
|
|
for(auto& arg : argl) *argp++ = (const char*)arg;
|
|
|
|
*argp++ = nullptr;
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
dup2(fdout[1], STDOUT_FILENO);
|
|
|
|
dup2(fderr[1], STDERR_FILENO);
|
|
|
|
close(fdout[0]);
|
|
|
|
close(fderr[0]);
|
|
|
|
close(fdout[1]);
|
|
|
|
close(fderr[1]);
|
2015-12-21 09:16:47 +00:00
|
|
|
execvp(name, (char* const*)argv);
|
Update to v098r03 release.
byuu says:
It took several hours, but I've rebuilt much of the SNES' bus memory
mapping architecture.
The new design unifies the cartridge string-based mapping
("00-3f,80-bf:8000-ffff") and internal bus.map calls. The map() function
now has an accompanying unmap() function, and instead of a fixed 256
callbacks, it'll scan to find the first available slot. unmap() will
free slots up when zero addresses reference a given slot.
The controllers and expansion port are now both entirely dynamic.
Instead of load/unload/power/reset, they only have the constructor
(power/reset/load) and destructor (unload). What this means is you can
now dynamically change even expansion port devices after the system is
loaded.
Note that this is incredibly dangerous and stupid, but ... oh well. The
whole point of this was for 21fx. There's no way to change the expansion
port device prior to loading a game, but if the 21fx isn't active, then
the reset vector hijack won't work. Now you can load a 21fx game, change
the expansion port device, and simply reset the system to active the
device.
The unification of design between controller port devices and expansion
port devices is nice, and overall this results in a reduction of code
(all of the Mapping stuff in Cartridge is gone, replaced with direct bus
mapping.) And there's always the potential to expand this system more in
the future now.
The big missing feature right now is the ability to push/pop mappings.
So if you look at how the 21fx does the reset vector, you might vomit
a little bit. But ... it works.
Also changed exit(0) to _exit(0) in the POSIX version of nall::execute.
[The _exit(0) thing is an attempt to make higan not crash when it tries
to launch icarus and it's not on $PATH. The theory is that higan forks,
then the child tries to exec icarus and fails, so it exits, all the
unique_ptrs clean up their resources and tell the X server to free
things the parent process is still using. Calling _exit() prevents
destructors from running, and seems to prevent the problem. -Ed.]
2016-04-09 10:21:18 +00:00
|
|
|
//this is called only if execvp fails:
|
|
|
|
//use _exit instead of exit, to avoid destroying key shared file descriptors
|
2016-05-16 09:51:12 +00:00
|
|
|
_exit(EXIT_FAILURE);
|
2015-12-21 09:16:47 +00:00
|
|
|
} else {
|
2016-05-16 09:51:12 +00:00
|
|
|
close(fdout[1]);
|
|
|
|
close(fderr[1]);
|
|
|
|
|
|
|
|
char buffer[256];
|
|
|
|
execute_result_t result;
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
auto size = read(fdout[0], buffer, sizeof(buffer));
|
|
|
|
if(size <= 0) break;
|
|
|
|
|
|
|
|
auto offset = result.output.size();
|
|
|
|
result.output.resize(offset + size);
|
|
|
|
memory::copy(result.output.get() + offset, buffer, size);
|
|
|
|
}
|
2015-12-21 09:16:47 +00:00
|
|
|
|
|
|
|
while(true) {
|
2016-05-16 09:51:12 +00:00
|
|
|
auto size = read(fderr[0], buffer, sizeof(buffer));
|
2015-12-21 09:16:47 +00:00
|
|
|
if(size <= 0) break;
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
auto offset = result.error.size();
|
|
|
|
result.error.resize(offset + size);
|
|
|
|
memory::copy(result.error.get() + offset, buffer, size);
|
2015-12-21 09:16:47 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
close(fdout[0]);
|
|
|
|
close(fderr[0]);
|
|
|
|
|
|
|
|
int status = 0;
|
|
|
|
waitpid(pid, &status, 0);
|
|
|
|
if(!WIFEXITED(status)) return {};
|
|
|
|
result.code = WEXITSTATUS(status);
|
2015-12-21 09:16:47 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
|
|
|
|
pid_t pid = fork();
|
|
|
|
if(pid == 0) {
|
|
|
|
const char* argv[1 + sizeof...(p) + 1];
|
|
|
|
const char** argp = argv;
|
2018-07-25 12:24:03 +00:00
|
|
|
vector<string> argl(forward<P>(p)...);
|
2015-12-21 09:16:47 +00:00
|
|
|
*argp++ = (const char*)name;
|
|
|
|
for(auto& arg : argl) *argp++ = (const char*)arg;
|
|
|
|
*argp++ = nullptr;
|
|
|
|
|
|
|
|
if(execvp(name, (char* const*)argv) < 0) {
|
|
|
|
execlp("xdg-open", "xdg-open", (const char*)name, nullptr);
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined(PLATFORM_WINDOWS)
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
|
2018-07-25 12:24:03 +00:00
|
|
|
vector<string> argl(name, forward<P>(p)...);
|
2015-12-21 09:16:47 +00:00
|
|
|
for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
|
|
|
|
string arguments = argl.merge(" ");
|
|
|
|
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
|
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
sa.bInheritHandle = true;
|
|
|
|
sa.lpSecurityDescriptor = nullptr;
|
|
|
|
|
|
|
|
HANDLE stdoutRead;
|
|
|
|
HANDLE stdoutWrite;
|
2016-05-16 09:51:12 +00:00
|
|
|
if(!CreatePipe(&stdoutRead, &stdoutWrite, &sa, 0)) return {};
|
|
|
|
if(!SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0)) return {};
|
|
|
|
|
|
|
|
HANDLE stderrRead;
|
|
|
|
HANDLE stderrWrite;
|
|
|
|
if(!CreatePipe(&stderrRead, &stderrWrite, &sa, 0)) return {};
|
|
|
|
if(!SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0)) return {};
|
2015-12-21 09:16:47 +00:00
|
|
|
|
|
|
|
HANDLE stdinRead;
|
|
|
|
HANDLE stdinWrite;
|
2016-05-16 09:51:12 +00:00
|
|
|
if(!CreatePipe(&stdinRead, &stdinWrite, &sa, 0)) return {};
|
|
|
|
if(!SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0)) return {};
|
2015-12-21 09:16:47 +00:00
|
|
|
|
|
|
|
STARTUPINFO si;
|
|
|
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
si.hStdOutput = stdoutWrite;
|
2016-05-16 09:51:12 +00:00
|
|
|
si.hStdError = stderrWrite;
|
2015-12-21 09:16:47 +00:00
|
|
|
si.hStdInput = stdinRead;
|
|
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
|
|
|
|
|
|
if(!CreateProcess(
|
|
|
|
nullptr, utf16_t(arguments),
|
|
|
|
nullptr, nullptr, true, CREATE_NO_WINDOW,
|
|
|
|
nullptr, nullptr, &si, &pi
|
2016-05-16 09:51:12 +00:00
|
|
|
)) return {};
|
2015-12-21 09:16:47 +00:00
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
DWORD exitCode = EXIT_FAILURE;
|
|
|
|
if(WaitForSingleObject(pi.hProcess, INFINITE)) return {};
|
|
|
|
if(!GetExitCodeProcess(pi.hProcess, &exitCode)) return {};
|
2016-01-23 07:29:34 +00:00
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
CloseHandle(pi.hProcess);
|
2015-12-21 09:16:47 +00:00
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
char buffer[256];
|
|
|
|
execute_result_t result;
|
|
|
|
result.code = exitCode;
|
|
|
|
|
2015-12-21 09:16:47 +00:00
|
|
|
while(true) {
|
|
|
|
DWORD read, available, remaining;
|
|
|
|
if(!PeekNamedPipe(stdoutRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break;
|
|
|
|
if(read == 0) break;
|
|
|
|
|
|
|
|
if(!ReadFile(stdoutRead, buffer, sizeof(buffer), &read, nullptr)) break;
|
|
|
|
if(read == 0) break;
|
|
|
|
|
2016-05-16 09:51:12 +00:00
|
|
|
auto offset = result.output.size();
|
|
|
|
result.output.resize(offset + read);
|
|
|
|
memory::copy(result.output.get() + offset, buffer, read);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
DWORD read, available, remaining;
|
|
|
|
if(!PeekNamedPipe(stderrRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break;
|
|
|
|
if(read == 0) break;
|
|
|
|
|
|
|
|
if(!ReadFile(stderrRead, buffer, sizeof(buffer), &read, nullptr)) break;
|
|
|
|
if(read == 0) break;
|
|
|
|
|
|
|
|
auto offset = result.error.size();
|
|
|
|
result.error.resize(offset + read);
|
|
|
|
memory::copy(result.error.get() + offset, buffer, read);
|
2015-12-21 09:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
|
2018-07-25 12:24:03 +00:00
|
|
|
vector<string> argl(forward<P>(p)...);
|
2015-12-21 09:16:47 +00:00
|
|
|
for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
|
|
|
|
string arguments = argl.merge(" ");
|
2017-09-06 02:38:00 +00:00
|
|
|
string directory = Path::program().replace("/", "\\");
|
|
|
|
ShellExecute(nullptr, nullptr, utf16_t(name), utf16_t(arguments), utf16_t(directory), SW_SHOWNORMAL);
|
2015-12-21 09:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
template<typename... P> inline auto execute(const string& name, P&&... p) -> string {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|