You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
6.4 KiB
328 lines
6.4 KiB
#include <cerrno> // errno, EAGAIN, EINTR |
|
#include <cstddef> // size_t |
|
#include <cstdio> // perror, ssize_t |
|
#include <cstdlib> // exit, WEXITSTATUS |
|
#include <cstring> // strcpy, strtok |
|
#include <functional> // function |
|
#include <sstream> // istringstream |
|
#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 { |
|
|
|
System::System() |
|
{ |
|
} |
|
|
|
System::System(const std::vector<std::string>& arguments) |
|
: m_arguments(arguments) |
|
{ |
|
} |
|
|
|
System System::operator()() |
|
{ |
|
return exec(); |
|
} |
|
|
|
System System::operator()(const char* command) |
|
{ |
|
return operator()(std::string { command }); |
|
} |
|
|
|
System System::operator()(std::string command) |
|
{ |
|
std::vector<std::string> arguments; |
|
|
|
size_t index = 0; |
|
while (index != std::string::npos) { |
|
index = command.find_first_of(" "); |
|
arguments.push_back(command.substr(0, index)); |
|
command = command.substr(index + 1); |
|
} |
|
|
|
return { arguments }; |
|
} |
|
|
|
System System::operator()(std::string_view command) |
|
{ |
|
return operator()(std::string { command }); |
|
} |
|
|
|
System System::operator()(const std::vector<const char*>& arguments) |
|
{ |
|
std::vector<std::string> stringArguments(arguments.size(), ""); |
|
for (size_t i = 0; i < arguments.size(); ++i) { |
|
stringArguments[i] = arguments[i]; |
|
} |
|
|
|
return { stringArguments }; |
|
} |
|
|
|
System System::operator()(const std::vector<std::string>& arguments) |
|
{ |
|
return { arguments }; |
|
} |
|
|
|
System System::operator()(const std::vector<std::string_view>& arguments) |
|
{ |
|
std::vector<std::string> stringArguments(arguments.size(), ""); |
|
for (size_t i = 0; i < arguments.size(); ++i) { |
|
stringArguments[i] = arguments[i]; |
|
} |
|
|
|
return { stringArguments }; |
|
} |
|
|
|
// Shell equivalent ; |
|
System System::operator+(System rhs) |
|
{ |
|
auto lhs = *this; |
|
|
|
lhs.exec(); |
|
rhs.m_output.append(lhs.m_output); |
|
rhs.m_error.append(lhs.m_error); |
|
rhs.exec(); |
|
|
|
return rhs; |
|
} |
|
|
|
System System::operator|(System rhs) |
|
{ |
|
auto lhs = *this; |
|
|
|
lhs.exec(); |
|
rhs.exec(lhs.m_output); |
|
|
|
return rhs; |
|
} |
|
|
|
System System::operator&&(System rhs) |
|
{ |
|
auto lhs = *this; |
|
|
|
lhs.exec(); |
|
if (lhs.m_status > 0) { |
|
return lhs; |
|
} |
|
|
|
rhs.m_output.append(lhs.m_output); |
|
rhs.m_error.append(lhs.m_error); |
|
rhs.exec(); |
|
|
|
return rhs; |
|
} |
|
|
|
System System::operator||(System rhs) |
|
{ |
|
auto lhs = *this; |
|
|
|
lhs.exec(); |
|
if (lhs.m_status == 0) { |
|
return lhs; |
|
} |
|
|
|
rhs.m_output.append(lhs.m_output); |
|
rhs.m_error.append(lhs.m_error); |
|
rhs.exec(); |
|
|
|
return rhs; |
|
} |
|
|
|
// cut -f -d |
|
System& System::cut(uint32_t field, char delimiter) |
|
{ |
|
exec(); |
|
|
|
return apply([&field, &delimiter](std::vector<std::string>& lines) { |
|
for (auto& line : lines) { |
|
size_t count = 1; |
|
size_t index = 0; |
|
while (index != std::string::npos) { |
|
if (count == field) { |
|
line = line.substr(0, line.find_first_of(delimiter)); |
|
break; |
|
} |
|
|
|
index = line.find_first_of(delimiter); |
|
line = line.substr(index + 1); |
|
count++; |
|
} |
|
} |
|
}); |
|
} |
|
|
|
System& System::sort(bool unique) |
|
{ |
|
exec(); |
|
|
|
return apply([&unique](std::vector<std::string>& lines) { |
|
std::sort(lines.begin(), lines.end()); |
|
|
|
if (unique) { |
|
auto last = std::unique(lines.begin(), lines.end()); |
|
lines.erase(last, lines.end()); |
|
} |
|
}); |
|
} |
|
|
|
// tail -n |
|
System& System::tail(int32_t number, bool starting) |
|
{ |
|
exec(); |
|
|
|
return apply([&number, &starting](std::vector<std::string>& lines) { |
|
number = abs(number); |
|
if (!starting) { |
|
lines.erase(lines.begin(), lines.end() - number); |
|
} |
|
else { |
|
lines.erase(lines.begin(), lines.begin() + number - 1); |
|
} |
|
}); |
|
} |
|
|
|
System& System::apply(LineCallback callback) |
|
{ |
|
exec(); |
|
|
|
std::vector<std::string> lines; |
|
|
|
auto stream = std::istringstream(m_output); |
|
std::string line; |
|
while (std::getline(stream, line)) { |
|
lines.push_back(line); |
|
} |
|
|
|
callback(lines); |
|
|
|
m_output.clear(); |
|
for (size_t i = 0; i < lines.size(); ++i) { |
|
m_output.append(lines.at(i) + '\n'); |
|
} |
|
|
|
return *this; |
|
} |
|
|
|
void System::print(const std::vector<std::string>& arguments) |
|
{ |
|
if (!arguments.size()) { |
|
return; |
|
} |
|
|
|
printf("----------\n"); |
|
printf("size: %zu\n", arguments.size()); |
|
printf("command: "); |
|
for (size_t i = 0; i < arguments.size(); ++i) { |
|
printf("%s ", arguments.at(i).c_str()); |
|
} |
|
printf("\n"); |
|
printf("----------\n"); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
System System::exec(std::string input) |
|
{ |
|
if (m_arguments.empty()) { |
|
return *this; |
|
} |
|
|
|
int stdinFd[2]; |
|
int stdoutFd[2]; |
|
int stderrFd[2]; |
|
if (pipe(stdinFd) < 0) { |
|
perror("\033[31;1mError:\033[0m pipe"); |
|
} |
|
if (pipe(stdoutFd) < 0) { |
|
perror("\033[31;1mError:\033[0m pipe"); |
|
} |
|
if (pipe(stderrFd) < 0) { |
|
perror("\033[31;1mError:\033[0m pipe"); |
|
} |
|
|
|
pid_t pid = fork(); |
|
switch (pid) { |
|
// Failed |
|
case -1: |
|
perror("\033[31;1mError:\033[0m fork"); |
|
break; |
|
// Child |
|
case 0: { |
|
close(stdinFd[WriteFileDescriptor]); |
|
dup2(stdinFd[ReadFileDescriptor], fileno(stdin)); |
|
close(stdinFd[ReadFileDescriptor]); |
|
|
|
close(stdoutFd[ReadFileDescriptor]); |
|
dup2(stdoutFd[WriteFileDescriptor], fileno(stdout)); |
|
close(stdoutFd[WriteFileDescriptor]); |
|
|
|
close(stderrFd[ReadFileDescriptor]); |
|
dup2(stderrFd[WriteFileDescriptor], fileno(stderr)); |
|
close(stderrFd[WriteFileDescriptor]); |
|
|
|
std::vector<char*> charArguments(m_arguments.size() + 1, 0); |
|
for (size_t i = 0; i < m_arguments.size(); ++i) { |
|
charArguments[i] = const_cast<char*>(m_arguments[i].c_str()); |
|
} |
|
|
|
execvp(charArguments[0], &charArguments[0]); |
|
exit(0); |
|
} |
|
// Parent |
|
default: |
|
m_arguments.clear(); |
|
break; |
|
} |
|
|
|
close(stdinFd[ReadFileDescriptor]); |
|
if (!input.empty()) { |
|
write(stdinFd[WriteFileDescriptor], input.c_str(), input.size()); |
|
} |
|
close(stdinFd[WriteFileDescriptor]); |
|
|
|
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); |
|
|
|
return *this; |
|
} |
|
|
|
void System::readFromFileDescriptor(int fileDescriptor[2], std::string& output) |
|
{ |
|
close(fileDescriptor[WriteFileDescriptor]); |
|
|
|
constexpr int bufferSize = 4096; |
|
char buffer[bufferSize]; |
|
|
|
for (;;) { |
|
const ssize_t result = read(fileDescriptor[ReadFileDescriptor], buffer, bufferSize); |
|
if (result > 0) { |
|
output.append(buffer, result); |
|
} |
|
// EOF |
|
if (result == 0) { |
|
break; |
|
} |
|
// Error |
|
else if (result == -1) { |
|
if (errno != EAGAIN && errno != EINTR) { |
|
perror("\033[31;1mError:\033[0m read"); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
close(fileDescriptor[ReadFileDescriptor]); |
|
} |
|
|
|
} // namespace Util
|
|
|