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