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