init
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
typedef int8_t I8;
|
||||||
|
typedef uint8_t U8;
|
||||||
|
typedef int16_t I16;
|
||||||
|
typedef uint16_t U16;
|
||||||
|
typedef int32_t I32;
|
||||||
|
typedef uint32_t U32;
|
||||||
|
typedef int64_t I64;
|
||||||
|
typedef uint64_t U64;
|
||||||
|
|
||||||
|
typedef int fd_t; // file descriptor
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include "childProcess.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
RetProc::RetProc(Proc origin, int status)
|
||||||
|
: Proc(origin)
|
||||||
|
, normal_exit(WIFEXITED(status))
|
||||||
|
, returned(WEXITSTATUS(status))
|
||||||
|
{}
|
||||||
|
|
||||||
|
RetProc::operator bool()
|
||||||
|
{
|
||||||
|
return normal_exit && returned == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create a process taking input from pipe childIn */
|
||||||
|
static Proc createProc(const char* const argv[], const fd_t childIn[2])
|
||||||
|
{
|
||||||
|
Proc childproc;
|
||||||
|
childproc.in = childIn[1];
|
||||||
|
childproc.out = STDOUT_FILENO;
|
||||||
|
|
||||||
|
childproc.pid = fork();
|
||||||
|
if(childproc.pid == 0) /* child */
|
||||||
|
{
|
||||||
|
close(childIn[1]);
|
||||||
|
dup2(childIn[0], STDIN_FILENO);
|
||||||
|
|
||||||
|
exec_or_die(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return childproc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create a process and redirect it's output to a pipe */
|
||||||
|
static Proc createRedirProc(const char* const argv[])
|
||||||
|
{
|
||||||
|
fd_t childOut[2];
|
||||||
|
pipe(childOut);
|
||||||
|
|
||||||
|
Proc childproc;
|
||||||
|
childproc.in = STDIN_FILENO;
|
||||||
|
childproc.out = childOut[0];
|
||||||
|
|
||||||
|
childproc.pid = fork();
|
||||||
|
/* close(STDIN_FILENO); flush?*/
|
||||||
|
if(childproc.pid != 0) /* parent */
|
||||||
|
{
|
||||||
|
close(childOut[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dup2(childOut[1], STDOUT_FILENO);
|
||||||
|
|
||||||
|
exec_or_die(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return childproc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create a process redirecting both in and out */
|
||||||
|
static Proc createRedirProc(const char* const argv[], const fd_t childIn[2])
|
||||||
|
{
|
||||||
|
fd_t childOut[2]; /* todo: add error #include "processTypes.hpp"*/
|
||||||
|
pipe(childOut);
|
||||||
|
|
||||||
|
Proc childproc;
|
||||||
|
childproc.in = childIn[1];
|
||||||
|
childproc.out = childOut[0];
|
||||||
|
|
||||||
|
childproc.pid = fork();
|
||||||
|
/* close(STDIN_FILENO); flush?*/
|
||||||
|
if(childproc.pid != 0) /* parent */
|
||||||
|
{
|
||||||
|
close(childOut[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(childIn[1]);
|
||||||
|
dup2(childIn[0], STDIN_FILENO);
|
||||||
|
|
||||||
|
dup2(childOut[1], STDOUT_FILENO);
|
||||||
|
|
||||||
|
exec_or_die(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return childproc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Proc createRedirProcess(const char* const argv[], U32 flags) /* todo rename */
|
||||||
|
{
|
||||||
|
fd_t childIn[2];
|
||||||
|
Proc proc;
|
||||||
|
|
||||||
|
if(flags & INPUT)
|
||||||
|
{
|
||||||
|
pipe(childIn);
|
||||||
|
if(flags & OUTPUT)
|
||||||
|
proc = createRedirProc(argv, childIn);
|
||||||
|
else
|
||||||
|
proc = createProc(argv, childIn);
|
||||||
|
}
|
||||||
|
else if(flags & OUTPUT)
|
||||||
|
proc = createRedirProc(argv);
|
||||||
|
else
|
||||||
|
proc = createProcess(argv);
|
||||||
|
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proc createCapProcess(const char* const argv[], fd_t in, fd_t err)
|
||||||
|
{
|
||||||
|
fd_t childOut[2];
|
||||||
|
pipe(childOut);
|
||||||
|
|
||||||
|
Proc proc;
|
||||||
|
proc.in = in;
|
||||||
|
proc.out = childOut[0];
|
||||||
|
proc.err = err;
|
||||||
|
|
||||||
|
proc.pid = fork();
|
||||||
|
if(proc.pid == 0) /* child */
|
||||||
|
{
|
||||||
|
if(in != STDIN_FILENO)
|
||||||
|
dup2(in, STDIN_FILENO);
|
||||||
|
|
||||||
|
dup2(childOut[1], STDOUT_FILENO);
|
||||||
|
|
||||||
|
if(err != STDERR_FILENO)
|
||||||
|
dup2(err, STDERR_FILENO);
|
||||||
|
|
||||||
|
exec_or_die(argv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(childOut[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proc createProcess(const char* const argv[], fd_t in, fd_t out, fd_t err)
|
||||||
|
{
|
||||||
|
Proc proc;
|
||||||
|
proc.in = in;
|
||||||
|
proc.out = out;
|
||||||
|
proc.err = err;
|
||||||
|
|
||||||
|
proc.pid = fork();
|
||||||
|
if(proc.pid == 0) /* child */
|
||||||
|
{
|
||||||
|
if(in != STDIN_FILENO)
|
||||||
|
dup2(in, STDIN_FILENO);
|
||||||
|
|
||||||
|
if(out != STDOUT_FILENO)
|
||||||
|
dup2(out, STDOUT_FILENO);
|
||||||
|
|
||||||
|
if(err != STDERR_FILENO)
|
||||||
|
dup2(err, STDERR_FILENO);
|
||||||
|
|
||||||
|
exec_or_die(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc runProcess(const char* const argv[], fd_t in, fd_t out, fd_t err)
|
||||||
|
{
|
||||||
|
Proc p = createProcess(argv, in, out, err);
|
||||||
|
|
||||||
|
// Wait to end
|
||||||
|
int status;
|
||||||
|
waitpid(p.pid, &status, 0);
|
||||||
|
|
||||||
|
return RetProc(p, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec_or_die(const char* const argv[])
|
||||||
|
{
|
||||||
|
execvp(argv[0], (char* const *)argv);
|
||||||
|
|
||||||
|
std::cerr << "Can't execute: " << argv[0] << ' ' << strerror(errno) << std::endl;
|
||||||
|
_exit(1); // _exit since we are a child
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "basicTypes.h"
|
||||||
|
|
||||||
|
struct Proc
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
fd_t in;
|
||||||
|
fd_t out;
|
||||||
|
fd_t err;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RetProc: public Proc /* returned process */
|
||||||
|
{
|
||||||
|
RetProc(Proc, int status); /* status as returned by waitpid */
|
||||||
|
bool normal_exit;
|
||||||
|
U8 returned; /* return code */
|
||||||
|
|
||||||
|
/* A returned process evaluates to true if it exited normaly and
|
||||||
|
* returned 0 */
|
||||||
|
explicit operator bool();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Redirect: U32
|
||||||
|
{
|
||||||
|
NOTHING =0,
|
||||||
|
INPUT = 1 << 0,
|
||||||
|
OUTPUT = 1 << 1,
|
||||||
|
ERR = 1 << 2 /* todo */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* For all funcions argv is an array of command parameters
|
||||||
|
* argv[0] is the command itself, argv needs to end with nullptr */
|
||||||
|
|
||||||
|
/* Create a proccess and redirect anyting flaged to a new pipe
|
||||||
|
flags - INPUT, OUTPUT, ERR
|
||||||
|
return pipe file descriptors*/
|
||||||
|
Proc createRedirProcess(const char* const argv[], U32 flags);
|
||||||
|
|
||||||
|
/* Capture ouput to a pipe, can take alternative input and err FDs */
|
||||||
|
Proc createCapProcess(const char* const argv[], fd_t in=0, fd_t err=2);
|
||||||
|
|
||||||
|
/* Can take FDs as arguments which will be used instead of std */
|
||||||
|
Proc createProcess(const char* const argv[], fd_t in=0, fd_t out=1, fd_t err=2);
|
||||||
|
|
||||||
|
/* Same but waits for the process to finish */
|
||||||
|
RetProc runProcess(const char* const argv[], fd_t in=0, fd_t out=1, fd_t err=2);
|
||||||
|
|
||||||
|
/* execvp the command or exit */
|
||||||
|
void exec_or_die(const char* const argv[]);
|
||||||
+264
@@ -0,0 +1,264 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "commands.hpp"
|
||||||
|
#include "childProcess.hpp"
|
||||||
|
|
||||||
|
enum constants: I32
|
||||||
|
{
|
||||||
|
FILE_PERMISIONS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
||||||
|
};
|
||||||
|
|
||||||
|
static fd_t open_or_die(const char* file, I32 flags)
|
||||||
|
{
|
||||||
|
fd_t fd = open(file, flags, FILE_PERMISIONS);
|
||||||
|
if(fd == -1)
|
||||||
|
{
|
||||||
|
std::cerr << "Can't open: " << file << ' ' << strerror(errno) << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc Cmd::operator()(fd_t in, fd_t out, fd_t err)
|
||||||
|
{
|
||||||
|
RetProc p = runProcess(argv.data(), in, out, err);
|
||||||
|
|
||||||
|
// Close files if such were used
|
||||||
|
// if(in > 2)
|
||||||
|
// close(in);
|
||||||
|
// if(out > 2)
|
||||||
|
// close(out);
|
||||||
|
// if(err > 2)
|
||||||
|
// close(err);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proc Cmd::detach(fd_t in, fd_t out, fd_t err)
|
||||||
|
{
|
||||||
|
return createProcess(argv.data(), in, out, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cmd::append_args(std::initializer_list<const char*> args)
|
||||||
|
{
|
||||||
|
argv.reserve(argv.size() + args.size());
|
||||||
|
argv.back() = *args.begin();
|
||||||
|
for(auto it = args.begin() + 1; it < args.end(); ++it)
|
||||||
|
argv.push_back(*it);
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd Cmd::operator+(const char* arg)
|
||||||
|
{
|
||||||
|
Cmd result(*this);
|
||||||
|
result.argv.back() = arg;
|
||||||
|
result.argv.push_back(nullptr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd& Cmd::operator+=(const char* arg)
|
||||||
|
{
|
||||||
|
argv.back() = arg;
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PendingCmd::PendingCmd(const Cmd& origin, fd_t in, fd_t out, fd_t err)
|
||||||
|
: cmd(origin)
|
||||||
|
, in(in)
|
||||||
|
, out(out)
|
||||||
|
, err(err)
|
||||||
|
, execed_(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PendingCmd::~PendingCmd()
|
||||||
|
{
|
||||||
|
if(!execed_)
|
||||||
|
operator()();
|
||||||
|
/* if(in != 0) */
|
||||||
|
/* close(in); */
|
||||||
|
/* if(out != 1) */
|
||||||
|
/* close(out); */
|
||||||
|
/* if(err != 2) */
|
||||||
|
/* close(err); */
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc PendingCmd::operator()()
|
||||||
|
{
|
||||||
|
assert(!execed_ && "Executed command twice");
|
||||||
|
execed_ = true;
|
||||||
|
return cmd(in, out, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc PendingCmd::runRedir()
|
||||||
|
{
|
||||||
|
assert(!execed_ && "executed command twice"); assert(out==1 && "already redirected");
|
||||||
|
execed_ = true;
|
||||||
|
|
||||||
|
RetProc p = runProcess(cmd.argv.data(), in, err);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Proc PendingCmd::detach()
|
||||||
|
{
|
||||||
|
assert(!execed_ && "Executed command twice");
|
||||||
|
execed_ = true;
|
||||||
|
return cmd.detach(in, out, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Proc PendingCmd::detachRedirOut()
|
||||||
|
{
|
||||||
|
assert(!execed_ && "Executed command twice"); assert(out==1 && "already redirected");
|
||||||
|
execed_ = true;
|
||||||
|
return createCapProcess(cmd.argv.data(), in, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingCmd::cancel()
|
||||||
|
{
|
||||||
|
execed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
str $(const PendingCmd& cmd)
|
||||||
|
{
|
||||||
|
RetProc p = const_cast<PendingCmd&>(cmd).runRedir();
|
||||||
|
|
||||||
|
// check output size (not portable?)
|
||||||
|
int pipe_size;
|
||||||
|
int rc = ioctl(p.out, FIONREAD, &pipe_size); assert(rc==0);
|
||||||
|
|
||||||
|
// write to the string directly, todo: find a better way
|
||||||
|
str output;
|
||||||
|
output.resize(pipe_size);
|
||||||
|
read(p.out, (char*)output.c_str(), output.size());
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec(const Cmd& cmd)
|
||||||
|
{
|
||||||
|
exec_or_die(cmd.argv.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd operator,(const PendingCmd& cfirst, const Cmd& second)
|
||||||
|
{
|
||||||
|
auto& first = const_cast<PendingCmd&>(cfirst);
|
||||||
|
first();
|
||||||
|
return PendingCmd(second);
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd operator,(RetProc, const Cmd& second)
|
||||||
|
{
|
||||||
|
return PendingCmd(second);
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd operator|(const PendingCmd& cfirst, const Cmd& second)
|
||||||
|
{
|
||||||
|
auto& first = const_cast<PendingCmd&>(cfirst);
|
||||||
|
fd_t firstOut = first.detachRedirOut().out;
|
||||||
|
return PendingCmd(second, firstOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RetProc operator&&(const PendingCmd& cfirst, const Cmd& csecond)
|
||||||
|
{
|
||||||
|
auto& first = const_cast<PendingCmd&>(cfirst);
|
||||||
|
auto& second = const_cast<Cmd&>(csecond);
|
||||||
|
RetProc firstProc = first();
|
||||||
|
|
||||||
|
if(firstProc)
|
||||||
|
return second();
|
||||||
|
|
||||||
|
return firstProc;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc operator&&(RetProc p, const Cmd& ccmd)
|
||||||
|
{
|
||||||
|
auto& cmd = const_cast<Cmd&>(ccmd);
|
||||||
|
if(p)
|
||||||
|
return cmd();
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc operator||(const PendingCmd& cfirst, const Cmd& csecond)
|
||||||
|
{
|
||||||
|
auto& first = const_cast<PendingCmd&>(cfirst);
|
||||||
|
auto& second = const_cast<Cmd&>(csecond);
|
||||||
|
RetProc firstProc = first();
|
||||||
|
|
||||||
|
if(!firstProc)
|
||||||
|
return second();
|
||||||
|
|
||||||
|
return firstProc;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetProc operator||(RetProc p, const Cmd& ccmd)
|
||||||
|
{
|
||||||
|
auto& cmd = const_cast<Cmd&>(ccmd);
|
||||||
|
if(!p)
|
||||||
|
return cmd();
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd& operator>(const PendingCmd& cmd, const char* file)
|
||||||
|
{
|
||||||
|
fd_t fd = open_or_die(file, O_WRONLY | O_CREAT);
|
||||||
|
return cmd > fd;
|
||||||
|
}
|
||||||
|
PendingCmd& operator>(const PendingCmd& ccmd, fd_t fd)
|
||||||
|
{
|
||||||
|
auto& cmd = const_cast<PendingCmd&>(ccmd);
|
||||||
|
assert(cmd.out == 1 || !"ERROR: Output is already redirected!");
|
||||||
|
|
||||||
|
cmd.out = fd;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd& operator>>(const PendingCmd& cmd, const char* file)
|
||||||
|
{
|
||||||
|
fd_t fd = open_or_die(file, O_WRONLY | O_CREAT | O_APPEND);
|
||||||
|
return cmd > fd;
|
||||||
|
}
|
||||||
|
PendingCmd& operator>>(const PendingCmd& cmd, fd_t fd)
|
||||||
|
{
|
||||||
|
return cmd > fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd& operator>=(const PendingCmd& cmd, const char* file)
|
||||||
|
{
|
||||||
|
fd_t fd = open_or_die(file, O_WRONLY | O_CREAT);
|
||||||
|
return cmd >= fd;
|
||||||
|
}
|
||||||
|
PendingCmd& operator>=(const PendingCmd& ccmd, fd_t fd)
|
||||||
|
{
|
||||||
|
auto& cmd = const_cast<PendingCmd&>(ccmd);
|
||||||
|
assert(cmd.err == 2 || !"ERROR: Error output is already redirected!");
|
||||||
|
|
||||||
|
cmd.err = fd;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd& operator<(const PendingCmd& cmd, const char* file)
|
||||||
|
{
|
||||||
|
fd_t fd = open_or_die(file, O_RDONLY);
|
||||||
|
return cmd < fd;
|
||||||
|
}
|
||||||
|
PendingCmd& operator<(const PendingCmd& ccmd, fd_t fd)
|
||||||
|
{
|
||||||
|
auto& cmd = const_cast<PendingCmd&>(ccmd);
|
||||||
|
assert(cmd.in == 0 || !"ERROR: Input is already redirected!");
|
||||||
|
cmd.in = fd;
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingCmd operator&(const PendingCmd& cfirst, const Cmd& second)
|
||||||
|
{
|
||||||
|
const_cast<PendingCmd&>(cfirst).detach();
|
||||||
|
return PendingCmd(second);
|
||||||
|
}
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "childProcess.hpp"
|
||||||
|
|
||||||
|
/* All CONST references are used and cast away to allow for taking
|
||||||
|
both l and r values without using templates or making copies
|
||||||
|
|
||||||
|
implicitly convert to it but not to const char* and we want the
|
||||||
|
implicit conversion for quick scripting
|
||||||
|
arg pointers are kept and used not copied */
|
||||||
|
|
||||||
|
/* A shell command */
|
||||||
|
class Cmd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* all args must be const char* */
|
||||||
|
template<typename... Args>
|
||||||
|
explicit Cmd(Args... args)
|
||||||
|
: argv({args...})
|
||||||
|
{
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute the command, if arguments are not given use stdin,out,err
|
||||||
|
else use the given file desciptors. this can also be used
|
||||||
|
to redirect err to out by giving err=1 like in shell */
|
||||||
|
RetProc operator()(fd_t in=0, fd_t out=1, fd_t err=2);
|
||||||
|
|
||||||
|
/* Run the command, don't wait to return like shell's & */
|
||||||
|
Proc detach(fd_t in=0, fd_t out=1, fd_t err=2);
|
||||||
|
|
||||||
|
/* Append arguments */
|
||||||
|
void append_args(std::initializer_list<const char*>);
|
||||||
|
/* Append an argument and return the new command */
|
||||||
|
Cmd operator+(const char* arg);
|
||||||
|
/* Append an argument */
|
||||||
|
Cmd& operator+=(const char* arg);
|
||||||
|
|
||||||
|
std::vector<const char*> argv; /* null terminateded arg list */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* An instance of a shell comand that is pending execution
|
||||||
|
if the command is not executed during the life of the obj
|
||||||
|
its executed on destruction */
|
||||||
|
class PendingCmd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* Can be implicitly created from a command */
|
||||||
|
PendingCmd(const Cmd&, fd_t in=0, fd_t out=1, fd_t err=2);
|
||||||
|
/* Execute the command on destruction */
|
||||||
|
~PendingCmd();
|
||||||
|
|
||||||
|
/* No copy we use unnamed return value optimization to return PendingCmd without destruction */
|
||||||
|
PendingCmd(const PendingCmd&) = delete;
|
||||||
|
PendingCmd& operator=(const PendingCmd&) = delete;
|
||||||
|
|
||||||
|
/* Run the command */
|
||||||
|
RetProc operator()();
|
||||||
|
/* Run the command, capturing the output in a new pipe */
|
||||||
|
RetProc runRedir();
|
||||||
|
|
||||||
|
/* Run the command async
|
||||||
|
shell: cmd &
|
||||||
|
becomes: cmd.detach() */
|
||||||
|
Proc detach();
|
||||||
|
/* Detach but redirect output to a pipe */
|
||||||
|
Proc detachRedirOut();
|
||||||
|
|
||||||
|
/* Prevent a pending command from being executed on destruction */
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
Cmd cmd;
|
||||||
|
fd_t in, out, err;
|
||||||
|
private:
|
||||||
|
bool execed_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Anything that takes PendingCmd can take Cmd aswell */
|
||||||
|
|
||||||
|
/* Like shell's exec */
|
||||||
|
void exec(const Cmd&); /* todo: take Pending? */
|
||||||
|
|
||||||
|
/* Run commands in sequence
|
||||||
|
shell:
|
||||||
|
ls
|
||||||
|
cd
|
||||||
|
becomes:
|
||||||
|
ls,
|
||||||
|
cd;
|
||||||
|
*/
|
||||||
|
PendingCmd operator,(const PendingCmd&, const Cmd&);
|
||||||
|
PendingCmd operator,(RetProc, const Cmd&);
|
||||||
|
/* Forbiden funcion to prevent wrong sequencing such as:
|
||||||
|
echo, echo && echo
|
||||||
|
here the 2nd and 3rd echos would be ran before 1st
|
||||||
|
due to the C++ operator precedence*/
|
||||||
|
PendingCmd operator,(const PendingCmd&, RetProc) = delete;
|
||||||
|
|
||||||
|
|
||||||
|
/* Shell pipe operator | - execute two commands, second takes input
|
||||||
|
from first can be chained many times */
|
||||||
|
PendingCmd operator|(const PendingCmd&, const Cmd&);
|
||||||
|
|
||||||
|
/* Shell operator && - run the second command only if first returns 0 (no errors)*/
|
||||||
|
RetProc operator&&(const PendingCmd&, const Cmd&);
|
||||||
|
RetProc operator&&(RetProc, const Cmd&);
|
||||||
|
|
||||||
|
/* Shell operator || - run second only if first returns != 0
|
||||||
|
Operator precedence is different from shell, C precendence is && > ||
|
||||||
|
so mixing || and && may not compile, but shouldn't cause other issues,
|
||||||
|
use () to resolve these cases */
|
||||||
|
RetProc operator||(const PendingCmd&, const Cmd&);
|
||||||
|
RetProc operator||(RetProc, const Cmd&);
|
||||||
|
|
||||||
|
/* Shell operator > - redirect output to file
|
||||||
|
shell: cmd > file 2>&1
|
||||||
|
becomes: cmd > "file" >=1 */
|
||||||
|
PendingCmd& operator>(const PendingCmd&, const char* file);
|
||||||
|
/* A file descriptor can be given istead of a file path in which case
|
||||||
|
* no truncation occurs */
|
||||||
|
PendingCmd& operator>(const PendingCmd&, fd_t);
|
||||||
|
/* Same but append rather then truncate the file */
|
||||||
|
PendingCmd& operator>>(const PendingCmd&, const char* file);
|
||||||
|
PendingCmd& operator>>(const PendingCmd&, fd_t);
|
||||||
|
/* Redirect errors to file */
|
||||||
|
PendingCmd& operator>=(const PendingCmd&, const char* file);
|
||||||
|
PendingCmd& operator>=(const PendingCmd&, fd_t);
|
||||||
|
/* todo >>= */
|
||||||
|
|
||||||
|
/* Shell oprator < use file as input */
|
||||||
|
PendingCmd& operator<(const PendingCmd&, const char* file);
|
||||||
|
PendingCmd& operator<(const PendingCmd&, fd_t);
|
||||||
|
|
||||||
|
/* Shell operator & but only for single processes
|
||||||
|
e.g.: echo a && echo b & echo c
|
||||||
|
in shell would detach "echo a && echo b" but here
|
||||||
|
this is not possible */
|
||||||
|
PendingCmd operator&(const PendingCmd&, const Cmd&);
|
||||||
|
|
||||||
|
/* string that is implicitly cast to const char* and doesn't deallocate
|
||||||
|
to allow for simpler synthax, speed and safety
|
||||||
|
e.g: echo + some_string
|
||||||
|
rather then: echo + some_string.c_str() */
|
||||||
|
class str: public std::basic_string<char> /* todo: allocator */
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::basic_string<char>::basic_string;
|
||||||
|
operator const char*() { return c_str();}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Execute a command and capture the output
|
||||||
|
shell:
|
||||||
|
var=$(ls)
|
||||||
|
becomes:
|
||||||
|
string var = $(ls);
|
||||||
|
*/
|
||||||
|
str $(const PendingCmd&);
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "commands.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::filesystem;
|
||||||
|
|
||||||
|
static path get_cache_path(const char* file);
|
||||||
|
static bool compile_file(const char* file, path cache, bool debug); // return true on success
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if(argc < 2)
|
||||||
|
{
|
||||||
|
cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(!strcmp(argv[1], "--help"))
|
||||||
|
{
|
||||||
|
cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n"
|
||||||
|
<< "Compile and run C++ source FILE that uses the cppipe library.\n"
|
||||||
|
"Pass the ARGUMENTS to he compiled binary.\n"
|
||||||
|
"The binaries are cached and recompiled only if the source is newer.\n"
|
||||||
|
"-g debug the binary, asserts are also enabled\n";
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool debug = false;
|
||||||
|
int file_arg = 1;
|
||||||
|
if(!strcmp(argv[1], "-g"))
|
||||||
|
{
|
||||||
|
if(argc < 3)
|
||||||
|
{
|
||||||
|
cout << "Usage: cppipe [-g] FILE [ARGUMENTS]...\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug = true;
|
||||||
|
file_arg = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* file = argv[file_arg];
|
||||||
|
if( !exists(file) )
|
||||||
|
{
|
||||||
|
cerr << "File: " << file << " doesn't exist\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
path cache( get_cache_path(file) ); // cache bins to avoid recompiles
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
cache += "_dbg";
|
||||||
|
|
||||||
|
// Compile the passed file
|
||||||
|
if( !compile_file(file, cache, debug) )
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Run the file without forking
|
||||||
|
Cmd run;
|
||||||
|
if(debug) // debug it with gdb
|
||||||
|
{
|
||||||
|
run += "gdb";
|
||||||
|
run += "--args";
|
||||||
|
}
|
||||||
|
|
||||||
|
run += cache.c_str();
|
||||||
|
for(int i = file_arg; i < argc; ++i)
|
||||||
|
run += argv[i];
|
||||||
|
exec(run);
|
||||||
|
}
|
||||||
|
|
||||||
|
static path get_cache_path(const char* file)
|
||||||
|
{
|
||||||
|
path cache;
|
||||||
|
if(char* xdg_cache = getenv("XDG_CACHE_HOME"))
|
||||||
|
{
|
||||||
|
cache = xdg_cache;
|
||||||
|
cache /= "cppipe";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char* home( getenv("HOME") );
|
||||||
|
cache = home;
|
||||||
|
cache /= ".cache/cppipe";
|
||||||
|
}
|
||||||
|
cache += absolute(file);
|
||||||
|
create_directories(cache.parent_path());
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compile_file(const char* file, path cache, bool debug)
|
||||||
|
{
|
||||||
|
// Only compile if the source is newer then the bin or we are in debug
|
||||||
|
error_code ec;
|
||||||
|
if(last_write_time(file) > last_write_time(cache, ec))
|
||||||
|
{
|
||||||
|
Cmd compile(
|
||||||
|
"g++",
|
||||||
|
"-o", cache.c_str(),
|
||||||
|
"-pipe", "-std=c++17", "-march=native",
|
||||||
|
// "-Wall", "-Wextra", "-Wno-parentheses",
|
||||||
|
file,
|
||||||
|
"-lcppipe"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
{
|
||||||
|
compile.append_args({
|
||||||
|
"-g",
|
||||||
|
"-Wall", "-Wextra", "-Wno-parentheses"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compile.append_args({
|
||||||
|
"-Ofast", "-flto", "-DNDEBUG", "-s"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !compile() ) // if failed to compile
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Compile lib
|
||||||
|
options="-std=c++17 -Ofast -flto -march=native -DNDEBUG -Wall -Wextra -Wno-parentheses -s -pipe"
|
||||||
|
g++ -c $options commands.cpp childProcess.cpp
|
||||||
|
|
||||||
|
# Create lib
|
||||||
|
ar r libcppipe.a commands.o childProcess.o
|
||||||
|
mv libcppipe.a /usr/local/lib
|
||||||
|
rm commands.o childProcess.o
|
||||||
|
|
||||||
|
# Compile cppipe
|
||||||
|
g++ -o cppipe $options cppipe.cpp -lcppipe
|
||||||
|
chmod 755 cppipe
|
||||||
|
mv cppipe /usr/local/bin
|
||||||
|
|
||||||
|
# Add headers
|
||||||
|
mkdir -p /usr/local/include/cppipe
|
||||||
|
cp basicTypes.h commands.hpp childProcess.hpp /usr/local/include/cppipe
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include <cppipe/commands.hpp>
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// tab vs space
|
||||||
|
// shaded obj?
|
||||||
|
// fix warning
|
||||||
|
/* close files on destruction ?*/
|
||||||
|
/* -> for error ? */
|
||||||
|
// check if processes normal_exit
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// f(var("ab"));
|
||||||
|
Cmd ls("ls");
|
||||||
|
Cmd ll("ls", "-l");
|
||||||
|
Cmd grep("grep");
|
||||||
|
Cmd echo("echo");
|
||||||
|
Cmd rm("rm");
|
||||||
|
|
||||||
|
str out1 = $(ll | grep + "cpp" | grep + "child");
|
||||||
|
if(out1.find("childProcess.cpp") != str::npos)
|
||||||
|
cout << "OK 0/11" << endl;
|
||||||
|
|
||||||
|
Cmd success("echo", "OK 1/11");
|
||||||
|
Cmd fail("mkdir", ".");
|
||||||
|
Cmd unexpected("echo", "FAILURE");
|
||||||
|
success &&
|
||||||
|
fail &&
|
||||||
|
unexpected;
|
||||||
|
|
||||||
|
// shouldnt compile
|
||||||
|
// success ||
|
||||||
|
// unexpected &&
|
||||||
|
// unexpected;
|
||||||
|
|
||||||
|
Cmd write_file("echo", "Existing ", " ", "file. OK 2/11");
|
||||||
|
|
||||||
|
write_file > "file.txt";
|
||||||
|
grep + "Existing" < "file.txt";
|
||||||
|
|
||||||
|
echo + "Appended to file OK 3/11" >> "file.txt";
|
||||||
|
grep + "Appended" < "file.txt" &&
|
||||||
|
rm + "file.txt" &&
|
||||||
|
fail ||
|
||||||
|
Cmd("echo", "OK 4/11");
|
||||||
|
|
||||||
|
echo + "OK 5/11" &&
|
||||||
|
echo + "OK 6/11",
|
||||||
|
Cmd("echo", "OK 7/11");
|
||||||
|
|
||||||
|
echo + "OK 8/11" &
|
||||||
|
echo + "OK 9/11" &&
|
||||||
|
echo + "OK 10/11";
|
||||||
|
|
||||||
|
// wait for all detached
|
||||||
|
while(wait(nullptr) != -1);
|
||||||
|
|
||||||
|
exec( echo + $(echo + "OK 11/11") );
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user