Init sources

This commit is contained in:
zawz 2020-08-28 10:57:28 +02:00
parent 597ccca191
commit ca41c27246
13 changed files with 1392 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/obj
/test
/run-tests.sh
/Zmakefile
/TODO

62
Makefile Normal file
View file

@ -0,0 +1,62 @@
## CONFIG ##
IDIR=include
SRCDIR=src
ODIR=obj
BINDIR=.
# binary name, default name of dir
NAME = $(shell readlink -f . | xargs basename)
# global links
LDFLAGS = -lpthread
# compiler
CC=g++
# compiler flags
CXXFLAGS= -I$(IDIR) -Wall -pedantic -std=c++17
ifeq ($(DEBUG),true)
# debugging flags
CC=clang++
CXXFLAGS += -g
else
# release flags
CXXFLAGS += -O2
endif
ifeq ($(STATIC),true)
# static links
LDFLAGS += -l:libztd.a
else
# dynamic links
LDFLAGS += -lztd
endif
## END CONFIG ##
$(shell mkdir -p $(ODIR))
$(shell mkdir -p $(BINDIR))
# automatically find .h and .hpp
DEPS = $(shell find $(IDIR) -type f -regex '.*\.hp?p?')
# automatically find .c and .cpp and make the corresponding .o rule
OBJ = $(shell find $(SRCDIR) -type f -regex '.*\.cp?p?' | sed 's|\.cpp|.o|g;s|\.c|.o|g;s|^$(SRCDIR)/|$(ODIR)/|g')
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
$(CC) $(CXXFLAGS) -c -o $@ $<
$(ODIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CC) $(CXXFLAGS) -c -o $@ $<
$(BINDIR)/$(NAME): $(OBJ)
$(CC) $(CXXFLAGS) $(LDFLAGS) -o $@ $^
test: $(BINDIR)/$(NAME)
$(BINDIR)/$(NAME)
clean:
rm $(ODIR)/*.o
clear:
rm $(BINDIR)/$(NAME)

36
include/options.hpp Normal file
View file

@ -0,0 +1,36 @@
#ifndef OPTIONS_HPP
#define OPTIONS_HPP
#include <ztd/options.hpp>
extern ztd::option_set options;
extern bool opt_minimize;
ztd::option_set gen_options();
void print_help(const char* arg0);
void print_include_help();
void print_resolve_help();
/**
%include [options]
options:
-s single quote contents
-d double quote contents
-e escape chars. For double quotes
-r include raw text, don't parse. Don't count as included
-f include even if already included. Don't count as included
*/
ztd::option_set create_include_opts();
/**
%resolve [options]
options:
-e escape chars. For double quotes
-p parse as shell code
-f ignore non zero return values
*/
ztd::option_set create_resolve_opts();
#endif //OPTIONS_HPP

17
include/parse.hpp Normal file
View file

@ -0,0 +1,17 @@
#ifndef PARSE_HPP
#define PARSE_HPP
#include "struc.hpp"
#include <string>
#include <ztd/filedat.hpp>
extern std::string g_origin;
std::string import_file(std::string const& path);
block parse(const char* in, uint32_t size);
inline block parse(std::string const& in) { return parse(in.c_str(), in.size()); }
#endif //PARSE_HPP

175
include/struc.hpp Normal file
View file

@ -0,0 +1,175 @@
#ifndef STRUC_HPP
#define STRUC_HPP
#include <string>
#include <vector>
#include <set>
#include <exception>
#include <stdexcept>
/*
structure:
block:
- group
- brace: condlist[]
- subsh: condlist[]
- cmd: arglist[]
- if
- pair<condlist[]>[]
condlist:
pipeline[]
pipeline:
block[]
arglist:
arg[]
arg:
- raw
- subarg[]
subarg:
- raw
- variable
- block: subshell
*/
#define AND_OP false
#define OR_OP true
class condlist;
class block;
class pipeline;
class arg;
class subarg;
typedef std::vector<condlist> list_t;
block make_cmd(std::vector<std::string> args);
bool add_include(std::string const& file);
class arg
{
public:
arg() { ; }
arg(std::string const& str) {this->setstring(str);}
void setstring(std::string const& str);
std::string raw;
std::vector<subarg> sa;
std::string string();
std::string generate(int ind);
};
class arglist
{
public:
inline void add(arg const& in) { args.push_back(in); }
inline void push_back(arg const& in) { args.push_back(in); }
std::vector<arg> args;
std::vector<std::string> strargs(uint32_t start);
inline uint64_t size() { return args.size(); }
inline arg& operator[](uint32_t i) { return args[i]; }
std::string generate(int ind);
};
class redir
{
public:
enum redirtype { none, write, append, read, raw } ;
redir(redirtype in=none) { type=in; }
redirtype type;
arg val;
};
class block
{
public:
enum blocktype { none, subshell, brace, main, cmd, function, case_block, for_block, if_block, while_block};
block() { type=none; }
block(blocktype in) { type=in; }
blocktype type;
// subshell/brace/main
list_t cls;
// cmd
arglist args;
// case
arg carg;
std::vector< std::pair<arg, list_t> > cases;
// main: shebang
// function: name
std::string shebang;
// subshell: return the containing cmd, if it is a single command
block* single_cmd();
std::string generate(int ind=0, bool print_shebang=true);
private:
std::string generate_cmd(int ind);
std::string generate_case(int ind);
};
class pipeline
{
public:
pipeline() {;}
pipeline(block const& bl) { cmds.push_back(bl); }
inline void add(block bl) { this->cmds.push_back(bl); }
std::vector<block> cmds;
std::string generate(int ind);
};
class condlist
{
public:
condlist() { parallel=false; }
condlist(block const& pl) { parallel=false; this->add(pl);}
condlist(pipeline const& pl) { parallel=false; this->add(pl);}
void add(pipeline const& pl, bool or_op=false);
bool parallel;
// don't push_back here
std::vector<bool> or_ops;
std::vector<pipeline> pls;
std::string generate(int ind);
};
class subarg
{
public:
enum argtype { string, subshell };
subarg(argtype in) { this->type=in; }
subarg(std::string const& in="") { type=string; val=in; }
subarg(block const& in) { type=subshell; sbsh=in; }
argtype type;
// raw string
std::string val;
// subshell
block sbsh;
std::string generate(int ind);
};
#endif //STRUC_HPP

32
include/util.hpp Normal file
View file

@ -0,0 +1,32 @@
#ifndef UTIL_HPP
#define UTIL_HPP
#include <string>
#include <vector>
#include <memory>
#include <exception>
#include <stdexcept>
#define INDENT indent(ind)
extern std::string indenting_string;
std::string indent(int n);
std::vector<std::string> split(std::string const& in, char c);
std::string escape_str(std::string const& in);
template<typename ... Args>
std::string strf( const std::string& format, Args ... args )
{
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
std::unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
std::string delete_brackets(std::string const& in);
#endif //UTIL_HPP

BIN
lxsh Executable file

Binary file not shown.

332
src/generate.cpp Normal file
View file

@ -0,0 +1,332 @@
#include "struc.hpp"
#include <ztd/shell.hpp>
#include "util.hpp"
#include "options.hpp"
#include "parse.hpp"
std::vector<std::string> included;
bool is_sub_special_cmd(std::string in)
{
return in == "%include_sub" || in == "%resolve_sub";
}
std::string arg::generate(int ind)
{
std::string ret;
for(auto it: sa)
{
ret += it.generate(ind);
}
return ret;
}
std::string arglist::generate(int ind)
{
std::string ret;
for(auto it: args)
{
ret += it.generate(ind);
ret += ' ';
}
ret.pop_back();
return ret;
}
std::string pipeline::generate(int ind)
{
std::string ret;
ret += cmds[0].generate(ind);
for(uint32_t i=1 ; i<cmds.size() ; i++)
{
ret += opt_minimize ? "|" : " | " ;
ret += cmds[i].generate(ind);
}
return ret;
}
std::string condlist::generate(int ind)
{
std::string ret;
if(!opt_minimize) ret += INDENT;
ret += pls[0].generate(ind);
for(uint32_t i=0 ; i<pls.size()-1 ; i++)
{
if(or_ops[i])
ret += opt_minimize ? "||" : " || ";
else
ret += opt_minimize ? "&&" : " && ";
ret += pls[i+1].generate(ind);
}
if(ret=="")
return "";
if(parallel)
{
ret += opt_minimize ? "&" : "&\n";
}
else
ret += '\n';
return ret;
}
bool add_include(std::string const& file)
{
std::string truepath=ztd::exec("readlink", "-f", file).first;
for(auto it: included)
{
if(it == truepath)
return false;
}
included.push_back(truepath);
return true;
}
std::string concatargs(std::vector<std::string> args)
{
std::string ret;
for(auto it: args)
ret += it + ' ';
ret.pop_back();
return ret;
}
std::string generate_resolve(std::vector<std::string> args, int ind)
{
auto opts=create_resolve_opts();
auto rargs = opts.process(args, false, true, false);
std::string cmd=concatargs(rargs);
auto p=ztd::shp("exec "+cmd);
if(!opts['f'] && p.second!=0)
{
throw std::runtime_error( strf("command `%s` returned %u", cmd.c_str(), p.second) );
}
while(p.first[p.first.size()-1] == '\n')
p.first.pop_back();
if(opts['p'])
{
block bl = parse(p.first);
std::string ret = bl.generate(ind, false);
std::string tmpind=INDENT;
ret = ret.substr(tmpind.size());
ret.pop_back(); // remove \n
return ret;
}
else
{
return p.first;
}
}
std::string generate_include(std::vector<std::string> args, int ind)
{
std::string ret;
auto opts=create_include_opts();
auto rargs = opts.process(args, false, true, false);
std::string quote;
if(opts['s'])
quote = '\'';
else if(opts['d'])
quote = '"';
// do shell resolution
std::string command="for I in ";
for(auto it: rargs)
command += it + ' ';
command += "; do echo $I ; done";
std::string inc=ztd::sh(command);
auto v = split(inc, '\n');
std::string file;
block bl;
bool indent_remove=true;
for(auto it : v)
{
if( opts['f'] || // force include
add_include(it) ) // not already included
{
file=import_file(it);
if(opts['r'])
ret += file;
else
{
try
{
bl = parse(quote + file + quote);
}
catch(ztd::format_error& e)
{
throw ztd::format_error(e.what(), it, e.data(), e.where());
}
file = bl.generate(ind, false);
if(indent_remove)
{
indent_remove=false;
std::string tmpind=INDENT;
file = file.substr(tmpind.size());
}
ret += file;
}
}
}
if(!opts['r'])
ret.pop_back();
return ret;
}
std::string block::generate_cmd(int ind)
{
std::string ret;
if(args.size()<=0)
return "";
std::string cmd=args[0].raw;
if(cmd == "%include" || cmd == "%include_s")
{
ret += generate_include(args.strargs(1), ind);
}
else if(cmd == "%resolve" || cmd == "%resolve_s")
{
ret += generate_resolve(args.strargs(1), ind);
}
else
ret = args.generate(ind);
return ret;
}
std::string block::generate_case(int ind)
{
std::string ret;
ret += "case " + carg.generate(ind) + " in\n";
ind++;
for(auto cs: this->cases)
{
if(!opt_minimize) ret += INDENT;
ret += cs.first.generate(ind) + ')';
if(!opt_minimize) ret += '\n';
for(auto it: cs.second)
ret += it.generate(ind+1);
if(opt_minimize)
{
if(ret[ret.size()-1] == '\n')
ret.pop_back();
}
else
{
ind++;
ret += INDENT;
ind--;
}
ret += ";;\n";
}
ind--;
if(!opt_minimize) ret += INDENT;
ret += "esac";
return ret;
}
std::string block::generate(int ind, bool print_shebang)
{
std::string ret;
if(type==cmd)
{
ret += generate_cmd(ind);
}
else
{
if(type==function)
{
ret += shebang + "()";
if(!opt_minimize) ret += '\n' + INDENT;
ret += "{\n";
for(auto it: cls)
ret += it.generate(ind+1);
if(!opt_minimize)
ret += INDENT;
ret += '}';
}
else if(type==subshell)
{
ret += '(';
if(!opt_minimize) ret += '\n';
for(auto it: cls)
ret += it.generate(ind+1);
if(opt_minimize)
ret.pop_back();
else
ret += INDENT;
ret += ')';
}
else if(type==brace)
{
ret += "{\n" ;
for(auto it: cls)
ret += it.generate(ind+1);
if(!opt_minimize)
ret += INDENT;
ret += '}';
}
else if(type==main)
{
if(print_shebang && shebang!="")
ret += shebang + '\n';
for(auto it: cls)
ret += it.generate(ind);
}
else if(type==case_block)
{
ret += generate_case(ind);
}
std::string t = generate_cmd(ind); // leftover redirections
if(t!="")
{
if(!opt_minimize) ret += ' ';
ret += t;
}
}
return ret;
}
std::string subarg::generate(int ind)
{
std::string ret;
if(type == subarg::string)
{
ret += val;
}
else if(type == subarg::subshell)
{
// includes and resolves inside command substitutions
// resolve here and not inside subshell
block* cmd = sbsh.single_cmd();
if( cmd != nullptr && (cmd->args[0].raw == "%include" || cmd->args[0].raw == "%resolve") )
{
ret += cmd->generate(ind);
}
// regular substitution
else
{
ret += '$';
ret += sbsh.generate(ind);
}
}
return ret;
}

91
src/main.cpp Normal file
View file

@ -0,0 +1,91 @@
#include <iostream>
#include <string.h>
#include <ztd/options.hpp>
#include <ztd/shell.hpp>
#include <unistd.h>
#include "struc.hpp"
#include "parse.hpp"
#include "options.hpp"
int main(int argc, char* argv[])
{
auto args=options.process(argc, argv);
if(options['m'])
opt_minimize=true;
bool piped=false;
if(options['h'])
{
print_help(argv[0]);
return 1;
}
if(options["help-commands"])
{
print_include_help();
printf("\n\n");
print_resolve_help();
return 1;
}
std::string file;
if(args.size() > 0)
{
if(args[0] == "-")
{
piped=true;
file = "/dev/stdin";
}
else
file=args[0];
}
else
{
if(isatty(fileno(stdin)))
{
print_help(argv[0]);
return 1;
}
else
{
piped=true;
file = "/dev/stdin";
}
}
if(!piped && !options['C'])
{
std::string dir=ztd::exec("dirname", file).first;
dir.pop_back();
file=ztd::exec("basename", file).first;
file.pop_back();
chdir(dir.c_str());
}
g_origin=file;
add_include(file);
if(args.size()>0)
{
try
{
block sh(parse(import_file(file)));
std::cout << sh.generate();
}
catch(ztd::format_error& e)
{
printFormatException(e);
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
return 0;
}

76
src/options.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "options.hpp"
ztd::option_set options = gen_options();
bool opt_minimize;
ztd::option_set gen_options()
{
ztd::option_set ret;
ret.add(ztd::option('h', "help", false, "Display this help message"));
ret.add(ztd::option('m', "minimize", false, "Minimize code"));
ret.add(ztd::option('C', "no-cd", false, "Don't change directories"));
ret.add(ztd::option("help-commands", false, "Print help for linker commands"));
return ret;
}
ztd::option_set create_include_opts()
{
ztd::option_set opts;
opts.add(
ztd::option('s', false, "Single quote contents"),
ztd::option('d', false, "Double quote contents"),
// ztd::option('e', false, "Escape for double quotes"),
ztd::option('r', false, "Include raw contents, don't parse"),
ztd::option('f', false, "Force include even if already included. Don't count as included")
);
return opts;
}
ztd::option_set create_resolve_opts()
{
ztd::option_set opts;
opts.add(
// ztd::option('e', false, "Escape for double quotes"),
ztd::option('p', false, "Parse contents as shell code"),
ztd::option('f', false, "Ignore non-zero return values")
);
return opts;
}
void print_help(const char* arg0)
{
printf("%s [options] [file]\n", arg0);
printf("Link extended shell, allows file including and command resolving\n");
printf("See --help-commands for help on linker commands\n");
printf("\n");
printf("Options:\n");
options.print_help(3,20);
}
void print_include_help()
{
printf("%%include [options] <file...>\n");
printf("Include the targeted files. Paths are relative to folder of current file\n");
printf(" - Regular shell processing applies to the file arguments\n");
printf(" - Only includes not already included files\n");
printf(" - `%%include` in command substitutions replaces the substitution\n");
printf(" =>`%%include_s` can be used inside a substitution to prevent this\n");
printf("\n");
ztd::option_set opts=create_include_opts();
printf("Options:\n");
opts.print_help(3,7);
}
void print_resolve_help()
{
printf("%%resolve [options] <command...>\n");
printf("Execute shell command and substitute output. Paths is from folder of current file\n");
printf(" - Fails if return value is not 0. Can be ignored with -f\n");
printf(" - `%%resolve` in command substitutions replaces the substitution\n");
printf(" =>`%%resolve_s` can be used inside a substitution to prevent this\n");
printf("\n");
ztd::option_set opts=create_resolve_opts();
printf("Options:\n");
opts.print_help(3,7);
}

458
src/parse.cpp Normal file
View file

@ -0,0 +1,458 @@
#include "parse.hpp"
#include <fstream>
#include <strings.h>
#include <string.h>
#include "util.hpp"
std::string g_origin;
inline bool is_in(char c, const char* set)
{
return index(set, c) != NULL;
}
inline bool is_alphanum(char c)
{
return (c >= 'a' && c<='z') || (c >= 'A' && c<='Z') || (c >= '0' && c<='9');
}
inline bool is_alpha(char c)
{
return (c >= 'a' && c<='z') || (c >= 'A' && c<='Z');
}
bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set=NULL)
{
uint32_t wordsize=strlen(word);
if(wordsize > size-start)
return false;
if(strncmp(word, in+start, wordsize) == 0)
{
if(end_set==NULL)
return true;
// end set
if(wordsize < size-start)
return is_in(in[start+wordsize], end_set);
}
return false;
}
uint32_t skip_chars(const char* in, uint32_t size, uint32_t start, const char* set)
{
for(uint32_t i=start; i<size ; i++)
{
if(!is_in(in[i],set))
return i;
}
return size;
}
uint32_t skip_until(const char* in, uint32_t size, uint32_t start, const char* set)
{
for(uint32_t i=start; i<size ; i++)
{
if(is_in(in[i],set))
return i;
}
return size;
}
uint32_t skip_unread(const char* in, uint32_t size, uint32_t start)
{
uint32_t i=start;
while(true)
{
i = skip_chars(in, size, i, " \t\n");
if(in[i] != '#') // not a comment
return i;
i = skip_until(in, size, i, "\n"); //skip to endline
}
}
std::pair<block, uint32_t> parse_subshell(const char* in, uint32_t size, uint32_t start);
// parse one argument
// must start at a read char
// ends at either " \t|&;\n()"
std::pair<arg, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start)
{
arg ret;
// j : start of subarg
uint32_t i=start,j=start;
while(i<size && !is_in(in[i], " \t|&;\n()"))
{
if(i+1<size && is_in(in[i], "<>") && in[i+1]=='&') // special case for <& and >&
{
i+=2;
}
else if(in[i]=='\\') // backslash: don't check next char
{
i++;
if(i>=size)
break;
i++;
}
else if(in[i] == '"') // start double quote
{
i++;
while(i<size && in[i] != '"') // while inside quoted string
{
if(in[i] == '\\') // backslash: don't check next char
{
i++;
if(i>=size)
break;
i++;
}
else if( word_eq("$(", in, size, i) ) // substitution
{
// add string subarg
ret.sa.push_back(subarg(std::string(in+j, i-j)));
i+=2;
// add subshell subarg
auto r=parse_subshell(in, size, i);
ret.sa.push_back(subarg(r.first));
j = i = r.second;
}
else
i++;
}
if(i>=size)
break;
i++;
}
else if(in[i] == '\'') // start single quote
{
i++;
while(i<size && in[i]!='\'')
i++;
if(i>=size)
break;
i++;
}
else if( word_eq("$(", in, size, i) ) // substitution
{
ret.sa.push_back(subarg(std::string(in+j, i-j)));
i+=2;
auto r=parse_subshell(in, size, i);
ret.sa.push_back(subarg(r.first));
j = i = r.second;
}
else
i++;
}
// add string subarg
std::string val=std::string(in+j, i-j);
ret.sa.push_back(subarg(val));
// raw string for other uses
ret.raw = std::string(in+start, i-start);
return std::make_pair(ret, i);
}
// parse one list of arguments (a command for instance)
// must start at a read char
// first char has to be read
// ends at either &|;\n#()
std::pair<arglist, uint32_t> parse_arglist(const char* in, uint32_t size, uint32_t start)
{
uint32_t i=start;
arglist ret;
while(i<size)
{
auto pp=parse_arg(in, size, i);
ret.args.push_back(pp.first);
i = skip_chars(in, size, pp.second, " \t");
if(i>=size || is_in(in[i], "&|;\n#()") )
return std::make_pair(ret, i);
}
return std::make_pair(ret, i);
}
std::pair<block, uint32_t> parse_block(const char* in, uint32_t size, uint32_t start);
// parse a pipeline
// must start at a read char
// separated by |
// ends at either &;\n#)
std::pair<pipeline, uint32_t> parse_pipeline(const char* in, uint32_t size, uint32_t start)
{
uint32_t i = skip_unread(in, size, start);
pipeline ret;
while(i<size)
{
auto pp=parse_block(in, size, i);
ret.add(pp.first);
i = skip_chars(in, size, pp.second, " \t");
if( (i>=size || is_in(in[i], "&;\n#)") ) || word_eq("||", in, size, i) )
return std::make_pair(ret, i);
else if( in[i] != '|')
throw ztd::format_error( strf("Unexpected token: '%c'", in[i] ), g_origin, in, i);
i++;
}
return std::make_pair(ret, i);
}
// parse condition lists
// must start at a read char
// separated by && or ||
// ends at either ;\n)#
std::pair<condlist, uint32_t> parse_condlist(const char* in, uint32_t size, uint32_t start)
{
uint32_t i = skip_unread(in, size, start);
condlist ret;
bool optype=AND_OP;
while(i<size)
{
auto pp=parse_pipeline(in, size, i);
ret.add(pp.first, optype);
i = pp.second;
if(i>=size || is_in(in[i], ")#")) // end here exactly: used for control later
{
return std::make_pair(ret, i);
}
else if(is_in(in[i], ";\n")) // end one char after: skip them for next parse
{
i++;
return std::make_pair(ret, i);
}
else if( word_eq("&", in, size, i) && !word_eq("&&", in, size, i) ) // parallel: end one char after
{
ret.parallel=true;
i++;
return std::make_pair(ret, i);
}
else if( word_eq("&&", in, size, i) ) // and op
{
i += 2;
optype=AND_OP;
}
else if( word_eq("||", in, size, i) ) // or op
{
i += 2;
optype=OR_OP;
}
else if(i<size-1) // bad combination
throw ztd::format_error( strf("Unexpected token: '%c%c'", in[i], in[i+1]), g_origin, in, i);
else // unknown
throw ztd::format_error("Unknown error", g_origin, in, i);
}
return std::make_pair(ret, i);
}
// parse condlists until )
std::pair<block, uint32_t> parse_subshell(const char* in, uint32_t size, uint32_t start)
{
uint32_t i = skip_unread(in, size, start);
block ret(block::subshell);
while(i<size && in[i] != ')')
{
auto pp=parse_condlist(in, size, i);
ret.cls.push_back(pp.first);
i = skip_unread(in, size, pp.second);
}
if(i>=size)
throw ztd::format_error("Expecting )", g_origin, in, start-1);
i++;
return std::make_pair(ret,i);
}
// parse condlists until }
std::pair<block, uint32_t> parse_brace(const char* in, uint32_t size, uint32_t start)
{
uint32_t i = skip_unread(in, size, start);
block ret(block::brace);
while(i<size && in[i] != '}')
{
auto pp=parse_condlist(in, size, i);
ret.cls.push_back(pp.first);
i = skip_unread(in, size, pp.second);
if(is_in(in[i], ")"))
throw ztd::format_error( strf("Unexpected token: '%c'", in[i]) , g_origin, in, i );
}
if(i>=size)
throw ztd::format_error("Expecting }", g_origin, in, start-1);
i++;
return std::make_pair(ret,i);
}
std::pair<block, uint32_t> parse_function(const char* in, uint32_t size, uint32_t start)
{
block ret(block::function);
uint32_t i=start;
i=skip_unread(in, size, i);
if(in[i] != '{')
throw ztd::format_error("Expecting { after ()", g_origin, in, i);
i++;
auto pp = parse_brace(in, size, i);
ret.cls = pp.first.cls;
i=pp.second;
return std::make_pair(ret, i);
}
std::pair<block, uint32_t> parse_cmd(const char* in, uint32_t size, uint32_t start)
{
block ret(block::cmd);
uint32_t i=start;
auto tp=parse_arg(in, size, i);
i=skip_unread(in, size, tp.second);
if(word_eq("()", in, size, i))
{
i += 2;
auto pp = parse_function(in, size, i);
pp.first.shebang = tp.first.raw;
return pp;
}
else
{
auto pp=parse_arglist(in, size, start);
ret.args = pp.first;
i = pp.second;
}
return std::make_pair(ret, i);
}
std::pair<block, uint32_t> parse_case(const char* in, uint32_t size, uint32_t start)
{
block ret(block::case_block);
uint32_t i=start;
auto pa = parse_arg(in, size, i); // case arg
ret.carg = pa.first;
i=skip_unread(in, size, pa.second);
if(!word_eq("in", in, size, i))
{
auto pp=parse_arg(in, size, i);
throw ztd::format_error("Unexpected word: '"+pp.first.raw+"', expecting 'in' after case", g_origin, in, i);
}
i=skip_unread(in, size, i+2);
while(i<size && !word_eq("esac", in, size, i, " \t\n;()&") )
{
// toto)
std::pair<arg, list_t> cc;
pa = parse_arg(in, size, i);
cc.first = pa.first;
i=skip_unread(in, size, pa.second);
if(in[i] != ')')
throw ztd::format_error( strf("Unexpected token '%c', expecting ')'", in[i]), g_origin, in, i );
i++;
while(true) // blocks
{
auto pc = parse_condlist(in, size, i);
cc.second.push_back(pc.first);
i=pc.second;
if(i+1>=size)
throw ztd::format_error("Expecting ';;'", g_origin, in, i);
if(in[i] == ')')
throw ztd::format_error( strf("Unexpected token '%c', expecting ';;'", in[i]), g_origin, in, i );
if(in[i-1] == ';' && in[i] == ';')
{
i++;
break;
}
i=skip_unread(in, size, i);
if(word_eq(";;", in, size, i))
{
i+=2;
break;
}
if(word_eq("esac", in, size, i))
break;
}
i=skip_unread(in, size, i);
ret.cases.push_back(cc);
}
if(i>=size)
throw ztd::format_error("Expecting 'esac'", g_origin, in, i);
i+=4;
return std::make_pair(ret, i);
}
// detect if brace, subshell, case or other
std::pair<block, uint32_t> parse_block(const char* in, uint32_t size, uint32_t start)
{
uint32_t i = skip_chars(in, size, start, " \n\t");
std::pair<block, uint32_t> ret;
if(in[i] == '{') // brace block
{
i++;
ret = parse_brace(in, size, i);
}
else if(in[i] == '(') //subshell
{
i++;
ret = parse_subshell(in, size, i);
}
else if(word_eq("case", in, size, i))
{
i = skip_unread(in, size, i+4);
ret = parse_case(in, size, i);
}
else // command
{
ret = parse_cmd(in, size, i);
}
if(ret.first.args.args.size()<=0)
{
auto pp=parse_arglist(in, size, ret.second); // in case of redirects
ret.second=pp.second;
ret.first.args=pp.first;
}
return ret;
}
// parse main
block parse(const char* in, uint32_t size)
{
block ret(block::main);
uint32_t i=0;
if(word_eq("#!", in, size, 0))
{
i=skip_until(in, size, 0, "\n");
ret.shebang=std::string(in, i);
}
i = skip_unread(in, size, i);
while(i<size)
{
auto pp=parse_condlist(in, size, i);
ret.cls.push_back(pp.first);
i = skip_unread(in, size, pp.second);
}
return ret;
}
std::string import_file(std::string const& path)
{
std::ifstream st(path);
if(!st)
throw std::runtime_error("Cannot open stream to '"+path+'\'');
std::string ret, ln;
while(getline(st, ln))
{
ret += ln + '\n';
}
st.close();
return ret;
}

54
src/struc.cpp Normal file
View file

@ -0,0 +1,54 @@
#include "struc.hpp"
#include "util.hpp"
block make_cmd(std::vector<std::string> args)
{
block cmd(block::cmd);
for(auto it: args)
{
cmd.args.add(arg(it));
}
return cmd;
}
std::vector<std::string> arglist::strargs(uint32_t start)
{
std::vector<std::string> ret;
for(uint32_t i=start; i<args.size(); i++)
ret.push_back(args[i].raw);
return ret;
}
void arg::setstring(std::string const& str)
{
sa.resize(0);
sa.push_back(subarg(str));
}
void condlist::add(pipeline const& pl, bool or_op)
{
if(this->pls.size() > 0)
this->or_ops.push_back(or_op);
this->pls.push_back(pl);
}
block* block::single_cmd()
{
if(this->type == block::subshell)
{
if( cls.size() == 1 && // only one condlist
cls[0].pls.size() == 1 && // only one pipeline
cls[0].pls[0].cmds.size() == 1 && // only one block
cls[0].pls[0].cmds[0].type == block::cmd) // block is a command
return &(cls[0].pls[0].cmds[0]); // return command
}
return nullptr;
}
std::string arg::string()
{
if(sa.size() > 1 || sa[0].type != subarg::string)
return "";
return sa[0].val;
}

54
src/util.cpp Normal file
View file

@ -0,0 +1,54 @@
#include "util.hpp"
std::string indenting_string="\t";
std::string indent(int n)
{
std::string ret;
for(int i=0; i<n; i++)
ret += indenting_string;
return ret;
}
std::vector<std::string> split(std::string const& in, char c)
{
uint32_t i=0,j=0;
std::vector<std::string> ret;
while(j<in.size())
{
i=in.find(c, j);
ret.push_back(in.substr(j,i-j));
j=i+1;
}
return ret;
}
std::string escape_str(std::string const& in)
{
std::string ret;
for(uint64_t i=0; i<in.size(); i++)
{
if(in[i] == '\n')
ret += "\\n";
else if(in[i] == '\"')
ret += "\\\"";
else if(in[i] == '\t')
ret += "\\t";
else if(in[i] == '\\')
ret += "\\\\";
else
ret += in[i];
}
return ret;
}
std::string delete_brackets(std::string const& in)
{
std::string ret;
for(auto it: in)
{
if(it!='[' && it!=']')
ret += it;
}
return ret;
}