Riyyi
3 years ago
2 changed files with 206 additions and 0 deletions
@ -0,0 +1,159 @@
|
||||
#include <cerrno> // errno, EAGAIN, EINTR |
||||
#include <cstddef> // size_t |
||||
#include <cstdio> // perror |
||||
#include <cstdlib> // exit, WEXITSTATUS |
||||
#include <cstring> // strcpy, strtok |
||||
#include <functional> // function |
||||
#include <string> |
||||
#include <string_view> |
||||
#include <sys/wait.h> // waitpid |
||||
#include <unistd.h> // close, dup2, execvp, fork, pipe, read |
||||
#include <vector> |
||||
|
||||
#include "util/system.h" |
||||
|
||||
namespace Util { |
||||
|
||||
void System::operator()(const char* command) |
||||
{ |
||||
// Split modifies the string, so two copies are needed
|
||||
char charCommand[strlen(command)]; |
||||
strcpy(charCommand, command); |
||||
char charCommand2[strlen(command)]; |
||||
strcpy(charCommand2, command); |
||||
|
||||
size_t index = split(charCommand, " "); |
||||
|
||||
// Preallocation is roughly ~10% faster even with just a single space.
|
||||
std::vector<char*> arguments(index + 1, 0); |
||||
|
||||
split(charCommand2, " ", [&arguments](int index, char* pointer) { |
||||
arguments[index] = pointer; |
||||
}); |
||||
|
||||
operator()(arguments); |
||||
} |
||||
|
||||
void System::operator()(std::string command) |
||||
{ |
||||
operator()(command.c_str()); |
||||
} |
||||
|
||||
void System::operator()(std::string_view command) |
||||
{ |
||||
operator()(command.data()); |
||||
} |
||||
|
||||
void System::operator()(const std::vector<const char*>& arguments) |
||||
{ |
||||
std::vector<char*> nonConstArguments(arguments.size() + 1, 0); |
||||
for (size_t i = 0; i < arguments.size(); ++i) { |
||||
nonConstArguments[i] = const_cast<char*>(arguments[i]); |
||||
} |
||||
operator()(nonConstArguments); |
||||
} |
||||
|
||||
void System::operator()(const std::vector<std::string>& arguments) |
||||
{ |
||||
std::vector<char*> nonConstArguments(arguments.size() + 1, 0); |
||||
for (size_t i = 0; i < arguments.size(); ++i) { |
||||
nonConstArguments[i] = const_cast<char*>(arguments[i].c_str()); |
||||
} |
||||
|
||||
operator()(nonConstArguments); |
||||
} |
||||
|
||||
void System::operator()(const std::vector<std::string_view>& arguments) |
||||
{ |
||||
std::vector<char*> nonConstArguments(arguments.size() + 1, 0); |
||||
for (size_t i = 0; i < arguments.size(); ++i) { |
||||
nonConstArguments[i] = const_cast<char*>(arguments[i].data()); |
||||
} |
||||
operator()(nonConstArguments); |
||||
} |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void System::operator()(const std::vector<char*>& arguments) |
||||
{ |
||||
int stdoutFd[2]; |
||||
int stderrFd[2]; |
||||
if (pipe(stdoutFd) < 0) { |
||||
perror("\033[31;1mError\033[0m"); |
||||
} |
||||
if (pipe(stderrFd) < 0) { |
||||
perror("\033[31;1mError\033[0m"); |
||||
} |
||||
|
||||
pid_t pid = fork(); |
||||
switch (pid) { |
||||
// Failed
|
||||
case -1: |
||||
perror("\033[31;1mError\033[0m"); |
||||
break; |
||||
// Child
|
||||
case 0: { |
||||
close(stdoutFd[ReadFileDescriptor]); |
||||
dup2(stdoutFd[WriteFileDescriptor], fileno(stdout)); |
||||
close(stdoutFd[WriteFileDescriptor]); |
||||
|
||||
close(stderrFd[ReadFileDescriptor]); |
||||
dup2(stderrFd[WriteFileDescriptor], fileno(stderr)); |
||||
close(stderrFd[WriteFileDescriptor]); |
||||
|
||||
execvp(arguments[0], &arguments[0]); |
||||
exit(0); |
||||
} |
||||
// Parent
|
||||
default: |
||||
break; |
||||
} |
||||
|
||||
readFromFileDescriptor(stdoutFd, m_output); |
||||
readFromFileDescriptor(stderrFd, m_error); |
||||
|
||||
int result; |
||||
do { |
||||
result = waitpid(pid, &m_status, 0); |
||||
} while (result == -1 && errno == EINTR); |
||||
m_status = WEXITSTATUS(m_status); |
||||
} |
||||
|
||||
void System::readFromFileDescriptor(int fileDescriptor[2], std::string& output) |
||||
{ |
||||
close(fileDescriptor[WriteFileDescriptor]); |
||||
|
||||
constexpr int bufferSize = 4096; |
||||
char buffer[bufferSize]; |
||||
|
||||
do { |
||||
const ssize_t result = read(fileDescriptor[ReadFileDescriptor], buffer, bufferSize); |
||||
// FIXME: also handle failure cases
|
||||
if (result > 0) { |
||||
output.append(buffer, result); |
||||
} |
||||
} while (errno == EAGAIN || errno == EINTR); |
||||
|
||||
if (!output.empty() && output.find_last_of('\n') == output.size() - 1) { |
||||
output.pop_back(); |
||||
} |
||||
|
||||
close(fileDescriptor[ReadFileDescriptor]); |
||||
} |
||||
|
||||
size_t System::split(char* split, const char* delimiters, SplitCallback callback) |
||||
{ |
||||
size_t index = 0; |
||||
char* pointer = strtok(split, delimiters); |
||||
while (pointer != nullptr) { |
||||
if (callback) { |
||||
callback(index, pointer); |
||||
} |
||||
index++; |
||||
pointer = strtok(nullptr, delimiters); |
||||
} |
||||
|
||||
return index; |
||||
} |
||||
|
||||
} // namespace Util
|
@ -0,0 +1,47 @@
|
||||
#ifndef SYSTEM_H |
||||
#define SYSTEM_H |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <functional> // function |
||||
#include <string> |
||||
#include <string_view> |
||||
#include <vector> |
||||
|
||||
namespace Util { |
||||
|
||||
using SplitCallback = std::function<void(size_t, char*)>; |
||||
|
||||
class System { |
||||
public: |
||||
System() {} |
||||
virtual ~System() {} |
||||
|
||||
enum FileDescriptor { |
||||
ReadFileDescriptor, |
||||
WriteFileDescriptor, |
||||
}; |
||||
|
||||
void operator()(const char* command); |
||||
void operator()(std::string command); |
||||
void operator()(std::string_view command); |
||||
void operator()(const std::vector<const char*>& arguments); |
||||
void operator()(const std::vector<std::string>& arguments); |
||||
void operator()(const std::vector<std::string_view>& arguments); |
||||
|
||||
std::string output() const { return m_output; } |
||||
std::string error() const { return m_error; } |
||||
int status() const { return m_status; } |
||||
|
||||
private: |
||||
void operator()(const std::vector<char*>& arguments); |
||||
void readFromFileDescriptor(int fileDescriptor[2], std::string& output); |
||||
size_t split(char* split, const char* delimiters, SplitCallback callback = {}); |
||||
|
||||
std::string m_output; |
||||
std::string m_error; |
||||
int m_status { 0 }; |
||||
}; |
||||
|
||||
} // namespace Util
|
||||
|
||||
#endif // SYSTEM_H
|
Loading…
Reference in new issue