Merge pull request #1 from zawwz/dev

v1.2.0
This commit is contained in:
zawwz 2021-06-11 14:32:47 +02:00 committed by GitHub
commit 3429a398cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1919 additions and 1564 deletions

View file

@ -14,65 +14,76 @@ LDFLAGS = -lpthread
# compiler
CC=g++
# compiler flags
CXXFLAGS= -I$(IDIR) -Wall -pedantic -std=c++20
CXXFLAGS= -I$(IDIR) -Wall -std=c++20
ifeq ($(DEBUG),true)
# debugging flags
CC=clang++
CXXFLAGS += -g -pg -D NO_PARSE_CATCH
# debugging flags
CXXFLAGS += -g -D DEBUG_MODE
RODIR = $(ODIR)/debug
else
# release flags
CXXFLAGS += -Ofast
# release flags
CXXFLAGS += -Ofast
RODIR = $(ODIR)/release
endif
ifeq ($(STATIC),true)
# static links
LDFLAGS += -l:libztd.a
else
# dynamic links
LDFLAGS += -lztd
endif
ifeq ($(PROFILE),true)
CXXFLAGS += -pg
endif
ifneq ($(RELEASE), true)
VSUFFIX=-dev-$(SHA_SHORT)
endif
ifeq ($(STATIC),true)
# static links
LDFLAGS += -l:libztd.a
else
# dynamic links
LDFLAGS += -lztd
endif
## END CONFIG ##
$(shell ./generate_version.sh)
$(shell ./generate_shellcode.sh)
$(shell mkdir -p $(ODIR))
$(shell mkdir -p $(RODIR))
$(shell mkdir -p $(BINDIR))
# automatically find .h and .hpp
DEPS = $(shell find $(IDIR) -type f -regex '.*\.hp?p?' ! -name 'g_version.h' ! -name 'g_shellcode.h')
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')
OBJ = $(shell find $(SRCDIR) -type f -regex '.*\.cp?p?' | sed 's|\.cpp|.o|g;s|\.c|.o|g;s|^$(SRCDIR)/|$(RODIR)/|g')
build: lxsh $(OBJ) $(DEPS)
build: $(BINDIR)/$(NAME)
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
# specific files for autogenerated headers
$(OBJDIR)/options.o: $(SRCDIR)/options.cpp $(DEPS) $(IDIR)/g_version.h
$(CC) $(CXXFLAGS) -c -o $@ $<
$(ODIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(OBJDIR)/shellcode.o: $(SRCDIR)/shellcode.cpp $(DEPS) $(IDIR)/g_shellcode.h
$(CC) $(CXXFLAGS) -c -o $@ $<
$(ODIR)/options.o: $(SRCDIR)/options.cpp $(DEPS) $(IDIR)/g_version.h
$(OBJDIR)/debashify.o: $(SRCDIR)/debashify.cpp $(DEPS) $(IDIR)/g_shellcode.h
$(CC) $(CXXFLAGS) -c -o $@ $<
$(ODIR)/shellcode.o: $(SRCDIR)/shellcode.cpp $(DEPS) $(IDIR)/g_shellcode.h
# generic files
$(RODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
$(CC) $(CXXFLAGS) -c -o $@ $<
$(ODIR)/debashify.o: $(SRCDIR)/debashify.cpp $(DEPS) $(IDIR)/g_shellcode.h
$(RODIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CC) $(CXXFLAGS) -c -o $@ $<
lxsh: $(OBJ)
$(BINDIR)/$(NAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
test: $(BINDIR)/$(NAME)
$(BINDIR)/$(NAME)
clean:
rm $(ODIR)/*.o gmon.out
rm $(ODIR)/*/*.o
clear:
rm $(BINDIR)/$(NAME)

View file

@ -8,7 +8,7 @@ Extended shell linker for linking, processing and minifying shell code
### zpkg
Available from the `zpkg` repository:
Available from the [zpkg](https://github.com/zawwz/zpkg) repository:
```shell
wget -qO- https://zpkg.zawz.net/install.sh | sh
zpkg install lxsh
@ -20,7 +20,7 @@ Download the `lxsh.tar.gz` archive, extract it,
and move the `lxsh` binary in a PATH folder (`/usr/local/bin` is the recommended).
```shell
wget https://github.com/zawwz/lxsh/releases/download/v1.1.0/lxsh.tar.gz
wget https://github.com/zawwz/lxsh/releases/download/v1.2.0/lxsh-linux-amd64.tar.gz
tar -xvf lxsh.tar.gz
sudo mv lxsh /usr/local/bin
```
@ -122,6 +122,23 @@ these features will continue working with undesired behavior.
Array argument with `[@]` does not expand into the desired multiple arguments.
## Extension commands
If you use the `#!/usr/bin/lxsh` shebang, you can use special lxsh-defined commands.
To list such commands, see `lxsh --help-extend-fcts`
## String processors
You can use prefixes in singlequote strings to apply processing to the string contents. <br>
To use string processors, prefix the string content with a line in the form of `#<PROCESSOR>`.
Example:
```shell
sh -c '#LXSH_PARSE_MINIFY
printf "%s\n" "Hello world!"'
```
As of now only the processor `LXSH_PARSE_MINIFY` is implemented, but more may come later
## Other features
### Output generated code
@ -154,7 +171,7 @@ Depends on [ztd](https://github.com/zawwz/ztd)
## Building
Use `make -j13` to build.<br>
Use `make -j` to build.<br>
You can use environment variables to alter some aspects:
- DEBUG: when set to `true` will generate a debug binary with profiling
- RELEASE: when set to `true`, the version string will be generated for release format

View file

@ -6,13 +6,13 @@
#include <map>
#include <set>
typedef struct debashify_params {
struct debashify_params {
std::set<std::string> required_fcts;
void require_fct(std::string const& in) { required_fcts.insert(in); }
// map of detected arrays
// bool value: is associative
std::map<std::string,bool> arrays;
} debashify_params;
};
bool r_debashify(_obj* o, debashify_params* params);

View file

@ -1,9 +1,9 @@
#ifndef ERRCODES_H
#define ERRCODES_H
#define ERR_HELP 1001
#define ERR_OPT 1002
#define ERR_PARSE 1003
#define ERR_RUNTIME 1004
#define ERR_HELP 101
#define ERR_OPT 102
#define ERR_PARSE 103
#define ERR_RUNTIME 104
#endif //ERRCODES_H

View file

@ -2,11 +2,11 @@
#define EXEC_HPP
#include "options.hpp"
#include "parse.hpp"
void parse_exec(FILE* fd, const char* in, uint32_t size, std::string const& filename="");
inline void parse_exec(FILE* fd, std::string const& in, std::string const& filename="") { parse_exec(fd, in.c_str(), in.size(), filename); }
void parse_exec(FILE* fd, parse_context ct);
int exec_process(std::string const& runtime, std::vector<std::string> const& args, std::string const& filecontents, std::string const& file);
int exec_process(std::string const& runtime, std::vector<std::string> const& args, parse_context ct);
#endif //EXEC_HPP

View file

@ -6,14 +6,14 @@
#include <string>
#include <utility>
#include <vector>
#include <ztd/filedat.hpp>
#include <tuple>
#define SPACES " \t"
#define SEPARATORS " \t\n"
#define ARG_END " \t\n;#()&|<>"
#define VARNAME_END " \t\n;#()&|=\"'\\{}/-+"
#define BLOCK_TOKEN_END " \t\n;#()&|=\"'\\"
#define BASH_BLOCK_END " \t\n;#()&|=\"'\\{}"
#define COMMAND_SEPARATOR "\n;"
#define CONTROL_END "#)"
#define PIPELINE_END "\n;#()&"
@ -27,59 +27,94 @@
#define ARRAY_ARG_END " \t\n;#()&|<>]"
// macros
#define PARSE_ERROR(str, i) ztd::format_error(str, "", in, i)
// #define PARSE_ERROR_I(str, ctx, i) format_error(str, ctx.filename, ctx.data, i)
// #define PARSE_ERROR(str, ctx) format_error(str, ctx.filename, ctx.data, ctx.i)
// #define PARSE_ERROR_I(str, ctx, i) { printFormatError(format_error(str, ctx.filename, ctx.data, i)); ctx.has_errored=true; }
// #define PARSE_ERROR(str, ctx) { printFormatError(format_error(str, ctx.filename, ctx.data, ctx.i)); ctx.has_errored=true; }
// structs
struct list_parse_options {
char end_char=0;
bool word_mode=false;
std::vector<std::string> end_words={};
const char* expecting=NULL;
};
// globals
extern bool g_bash;
extern const std::vector<std::string> posix_cmdvar;
extern const std::vector<std::string> bash_cmdvar;
std::string import_file(std::string const& path);
shmain* parse_text(const char* in, uint32_t size, std::string const& filename="");
inline shmain* parse_text(std::string const& in, std::string const& filename="") { return parse_text(in.c_str(), in.size(), filename); }
inline shmain* parse(std::string const& file) { return parse_text(import_file(file), file); }
std::pair<shmain*, parse_context> parse_text(parse_context context);
std::pair<shmain*, parse_context> parse_text(std::string const& in, std::string const& filename="");
inline std::pair<shmain*, parse_context> parse(std::string const& file) { return parse_text(import_file(file), file); }
// tools
parse_context make_context(std::string const& in, std::string const& filename="", bool bash=false);
parse_context make_context(parse_context ctx, std::string const& in="", std::string const& filename="", bool bash=false);
parse_context make_context(parse_context ctx, uint64_t i);
parse_context operator+(parse_context ctx, int64_t a);
parse_context operator-(parse_context ctx, int64_t a);
// error handlers
void parse_error(std::string const& message, parse_context& ctx);
void parse_error(std::string const& message, parse_context& ctx, uint64_t i);
// ** unit parsers ** //
/* util parsers */
bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set=NULL);
std::pair<std::string,uint32_t> get_word(const char* in, uint32_t size, uint32_t start, const char* end_set);
uint32_t word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set=NULL);
inline bool word_eq(const char* word, parse_context const& ct, const char* end_set=NULL) {
return word_eq(word, ct.data, ct.size, ct.i, end_set);
}
std::pair<std::string,uint32_t> get_word(parse_context ct, const char* end_set);
uint32_t skip_chars(const char* in, uint32_t size, uint32_t start, const char* set);
inline uint32_t skip_chars(parse_context const& ct, const char* set) {
return skip_chars(ct.data, ct.size, ct.i, set);
}
uint32_t skip_until(const char* in, uint32_t size, uint32_t start, const char* set);
inline uint32_t skip_until(parse_context const& ct, const char* set) {
return skip_until(ct.data, ct.size, ct.i, set);
}
uint32_t skip_unread(const char* in, uint32_t size, uint32_t start);
inline uint32_t skip_unread(parse_context const& ct) {
return skip_unread(ct.data, ct.size, ct.i);
}
// list
std::pair<list*, uint32_t> parse_list_until(const char* in, uint32_t size, uint32_t start, char end_c, const char* expecting=NULL);
std::pair<list*, uint32_t> parse_list_until(const char* in, uint32_t size, uint32_t start, std::string const& end_word);
std::tuple<list*, uint32_t, std::string> parse_list_until(const char* in, uint32_t size, uint32_t start, std::vector<std::string> const& end_words, const char* expecting=NULL);
// std::pair<list*, parse_context> parse_list_until(parse_context ct, char end_c, const char* expecting=NULL);
// std::pair<list*, parse_context> parse_list_until(parse_context ct, std::string const& end_word);
// std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ct, std::vector<std::string> const& end_words, const char* expecting=NULL);
std::tuple<list*, parse_context, std::string> parse_list_until(parse_context ct, list_parse_options opts={});
// name
std::pair<variable*, uint32_t> parse_var(const char* in, uint32_t size, uint32_t start, bool specialvars=true, bool array=false);
std::pair<variable*, parse_context> parse_var(parse_context ct, bool specialvars=true, bool array=false);
// subarg parsers
std::pair<arithmetic*, uint32_t> parse_arithmetic(const char* in, uint32_t size, uint32_t start);
std::pair<variable*, uint32_t> parse_manipulation(const char* in, uint32_t size, uint32_t start);
std::pair<arithmetic*, parse_context> parse_arithmetic(parse_context ct);
std::pair<variable*, parse_context> parse_manipulation(parse_context ct);
// arg parser
std::pair<arg*, uint32_t> parse_arg(const char* in, uint32_t size, uint32_t start, const char* end=ARG_END, const char* unexpected=SPECIAL_TOKENS, bool doquote=true);
std::pair<arg*, parse_context> parse_arg(parse_context ct, const char* end=ARG_END, const char* unexpected=SPECIAL_TOKENS, bool doquote=true);
// redirect parser
std::pair<redirect*, uint32_t> parse_redirect(const char* in, uint32_t size, uint32_t start);
std::pair<redirect*, parse_context> parse_redirect(parse_context ct);
// arglist parser
std::pair<arglist*, uint32_t> parse_arglist(const char* in, uint32_t size, uint32_t start, bool hard_error=false, std::vector<redirect*>* redirs=nullptr);
std::pair<arglist*, parse_context> parse_arglist(parse_context ct, bool hard_error=false, std::vector<redirect*>* redirs=nullptr);
// block parsers
std::pair<block*, uint32_t> parse_block(const char* in, uint32_t size, uint32_t start);
std::pair<cmd*, uint32_t> parse_cmd(const char* in, uint32_t size, uint32_t start);
std::pair<function*, uint32_t> parse_function(const char* in, uint32_t size, uint32_t start, const char* after="()");
std::pair<subshell*, uint32_t> parse_subshell(const char* in, uint32_t size, uint32_t start);
std::pair<brace*, uint32_t> parse_brace(const char* in, uint32_t size, uint32_t start);
std::pair<case_block*, uint32_t> parse_case(const char* in, uint32_t size, uint32_t start);
std::pair<if_block*, uint32_t> parse_if(const char* in, uint32_t size, uint32_t start);
std::pair<for_block*, uint32_t> parse_for(const char* in, uint32_t size, uint32_t start);
std::pair<while_block*, uint32_t> parse_while(const char* in, uint32_t size, uint32_t start);
std::pair<block*, parse_context> parse_block(parse_context ct);
std::pair<cmd*, parse_context> parse_cmd(parse_context ct);
std::pair<function*, parse_context> parse_function(parse_context ct, const char* after="()");
std::pair<subshell*, parse_context> parse_subshell(parse_context ct);
std::pair<brace*, parse_context> parse_brace(parse_context ct);
std::pair<case_block*, parse_context> parse_case(parse_context ct);
std::pair<if_block*, parse_context> parse_if(parse_context ct);
std::pair<for_block*, parse_context> parse_for(parse_context ct);
std::pair<while_block*, parse_context> parse_while(parse_context ct);
// pipeline parser
std::pair<pipeline*, uint32_t> parse_pipeline(const char* in, uint32_t size, uint32_t start);
std::pair<pipeline*, parse_context> parse_pipeline(parse_context ct);
// condlist parser
std::pair<condlist*, uint32_t> parse_condlist(const char* in, uint32_t size, uint32_t start);
std::pair<condlist*, parse_context> parse_condlist(parse_context ct);
#endif //PARSE_HPP

View file

@ -76,4 +76,6 @@ bool r_delete_var(_obj* in, set_t* vars);
std::set<std::string> find_lxsh_commands(shmain* sh);
void add_unset_variables(shmain* sh, std::regex const& exclude);
void string_processors(_obj* in);
#endif //PROCESSING_HPP

View file

@ -2,16 +2,16 @@
#define RESOLVE_HPP
#include "struc.hpp"
#include "parse.hpp"
extern std::vector<std::string> included;
std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, std::string const& filename, std::string* ex_dir=nullptr);
std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, std::string const& filename, std::string* ex_dir=nullptr);
std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, parse_context ctx, std::string* ex_dir=nullptr);
std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, parse_context ctx, std::string* ex_dir=nullptr);
bool add_include(std::string const& file);
void resolve(_obj* sh, std::string* filename);
void resolve(shmain* sh);
void resolve(_obj* sh, parse_context ctx);
std::string _pre_cd(std::string const& filename);
void _cd(std::string const& dir);

View file

@ -15,9 +15,9 @@ struct lxsh_fct {
std::vector<std::string> depends_on=std::vector<std::string>();
};
extern const std::map<const std::string, const struct lxsh_fct> lxsh_extend_fcts;
extern const std::map<const std::string, const struct lxsh_fct> lxsh_array_fcts;
extern const std::map<const std::string, const struct lxsh_fct> lxsh_allfcts;
extern const std::map<const std::string, const lxsh_fct> lxsh_extend_fcts;
extern const std::map<const std::string, const lxsh_fct> lxsh_array_fcts;
extern const std::map<const std::string, const lxsh_fct> lxsh_allfcts;
void add_lxsh_fcts(shmain* sh, std::set<std::string> fcts);

View file

@ -62,6 +62,8 @@ subarg: can be one of
*/
// pre-definitions
#define AND_OP false
#define OR_OP true
@ -71,6 +73,56 @@ class pipeline;
class arg;
class subarg;
class cmd;
class redirect;
// structs
struct parse_context {
const char* data=NULL;
uint64_t size=0;
uint64_t i=0;
const char* filename="";
bool bash=false;
const char* expecting="";
const char* here_delimiter="";
const char* here_doc="";
const char operator[](uint64_t a) { return data[a]; }
bool has_errored=false;
redirect* here_document=nullptr;
char* here_delimitor=NULL;
};
struct generate_context {
arg* here_document=nullptr;
};
// exceptions
class format_error : public std::exception
{
public:
//! @brief Conctructor
inline format_error(const std::string& what, const std::string& origin, const std::string& data, int where, std::string level="error") { desc=what; index=where; filename=origin; sdat=data; severity=level; }
inline format_error(const std::string& what, parse_context const& ctx, std::string level="error") { desc=what; index=ctx.i; filename=ctx.filename; sdat=ctx.data; severity=level; }
//! @brief Error message
inline const char * what () const throw () {return desc.c_str();}
//! @brief Origin of the data, name of imported file, otherwise empty if generated
inline const char * origin() const throw () {return filename.c_str();}
//! @brief Data causing the exception
inline const char * data() const throw () {return sdat.c_str();}
//! @brief Severity of the exception
inline const std::string level() const throw () {return severity.c_str();}
//! @brief Where the error is located in the data
inline const int where () const throw () {return index;}
private:
std::string desc;
int index;
std::string filename;
std::string sdat;
std::string severity;
};
// objects
// type pack of condlist
typedef std::vector<arg*> arglist_t;
@ -135,11 +187,15 @@ public:
std::vector<subarg*> sa;
bool is_string();
// return if is a string and only one subarg
std::string string();
// return if the first subarg is a string
std::string first_sa_string();
// can expand into multiple arguments
bool can_expand();
inline bool equals(std::string const& in) { return this->string() == in; }
std::string generate(int ind);
@ -179,6 +235,9 @@ public:
std::vector<std::string> strargs(uint32_t start);
// potentially expands into more arguments than its size
bool can_expand();
void insert(uint32_t i, arg* val);
void insert(uint32_t i, arglist const& lst);
@ -190,15 +249,20 @@ public:
class redirect : public _obj
{
public:
redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; }
redirect(arg* in) { type=_obj::_redirect; target=in; }
redirect(std::string strop, arg* in) { type=_obj::_redirect; op=strop; target=in; }
~redirect() { if(target != nullptr) delete target; }
redirect(std::string strop="") { type=_obj::_redirect; op=strop; target=nullptr; here_document=nullptr; }
redirect(arg* in) { type=_obj::_redirect; target=in; here_document=nullptr; }
redirect(std::string strop, arg* in) { type=_obj::_redirect; op=strop; target=in; here_document=nullptr; }
redirect(std::string strop, arg* in, arg* doc) { type=_obj::_redirect; op=strop; target=in; here_document=doc; }
~redirect() {
if(target != nullptr) delete target;
if(here_document != nullptr) delete here_document;
}
std::string generate(int ind);
std::string op;
arg* target;
arg* here_document;
};
// Meta block
@ -213,9 +277,9 @@ public:
// subshell: return the containing cmd, if it is a single command
cmd* single_cmd();
std::string generate_redirs(int ind, std::string const& _str);
std::string generate_redirs(int ind, std::string const& _str, generate_context* ctx);
virtual std::string generate(int ind)=0;
virtual std::string generate(int ind, generate_context* ctx)=0;
};
// PL
@ -230,7 +294,8 @@ public:
bool negated; // negated return value (! at start)
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); };
};
// CL
@ -331,7 +396,8 @@ public:
arglist* args;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class shmain : public block
@ -349,7 +415,8 @@ public:
list* lst;
std::string generate(bool print_shebang=true, int ind=0);
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class subshell : public block
@ -365,7 +432,8 @@ public:
list* lst;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class brace : public block
@ -380,7 +448,8 @@ public:
list* lst;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class function : public block
@ -394,7 +463,8 @@ public:
std::string name;
list* lst;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class case_block : public block
@ -414,7 +484,8 @@ public:
arg* carg;
std::vector< std::pair<std::vector<arg*>, list*> > cases;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class if_block : public block
@ -434,7 +505,8 @@ public:
list* else_lst;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class for_block : public block
@ -452,7 +524,8 @@ public:
arglist* iter;
list* ops;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
class while_block : public block
@ -469,7 +542,8 @@ public:
list* cond;
list* ops;
std::string generate(int ind);
std::string generate(int ind, generate_context* ctx);
std::string generate(int ind) { return this->generate(ind, nullptr); }
};
// Subarg subtypes //

View file

@ -12,8 +12,6 @@
#include <functional>
#include <regex>
#include <ztd/filedat.hpp>
#include "struc.hpp"
extern std::string indenting_string;
@ -149,9 +147,6 @@ int _exec(std::string const& bin, std::vector<std::string> const& args);
std::string stringReplace(std::string subject, const std::string& search, const std::string& replace);
void printFormatError(ztd::format_error const& e, bool print_line=true);
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin, bool print_line=true);
int execute(shmain* sh, std::vector<std::string>& args);
void printFormatError(format_error const& e, bool print_line=true);
#endif //UTIL_HPP

View file

@ -1,6 +1,6 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION_STRING "v1.1.0"
#define VERSION_STRING "v1.2.0"
#endif //VERSION_H

View file

@ -1,6 +1,6 @@
#include "debashify.hpp"
#include "ztd/options.hpp"
#include <ztd/options.hpp>
#include "processing.hpp"
#include "recursive.hpp"
@ -144,12 +144,11 @@ std::string get_declare_opt(cmd* in)
ztd::option_set gen_echo_opts()
{
ztd::option_set ret;
ret.add(
ztd::option('e'),
ztd::option('E'),
ztd::option('n')
);
ztd::option_set ret( std::vector<ztd::option>({
ztd::option('e'),
ztd::option('E'),
ztd::option('n')
}) );
return ret;
}
@ -166,60 +165,124 @@ bool debashify_echo(pipeline* pl)
ztd::option_set opts=gen_echo_opts();
std::vector<std::string> args=in->args->strargs(1);
std::vector<std::string> postargs;
try
{
postargs=opts.process(args, true, true);
postargs=opts.process(args, {.ignore_numbers=true, .stop_on_argument=true} );
}
catch(ztd::option_error& e)
{
skip=true;
}
if(skip || postargs.size() == args.size()) // no options processed: skip
return false;
// delete the number of args that were processed
for(uint32_t i=0; i<args.size()-postargs.size(); i++)
{
delete in->args->args[1];
in->args->args.erase(in->args->args.begin()+1);
}
bool doprintf=false;
bool enable_interpretation=false;
bool newline=true;
if(opts['E'])
bool has_escape_sequence=false;
bool has_processed_options=false;
if(!skip && postargs.size() != args.size())
{
doprintf=true;
}
else if(opts['n'])
{
doprintf=true;
newline=false;
has_processed_options=true;
// delete the number of args that were processed
for(uint32_t i=0; i<args.size()-postargs.size(); i++)
{
delete in->args->args[1];
in->args->args.erase(in->args->args.begin()+1);
}
if(opts['e'])
enable_interpretation=true;
else if(opts['n'])
newline=false;
}
if(doprintf)
for(auto it=in->args->args.begin()+1; it!=in->args->args.end(); it++)
{
delete in->args->args[0];
in->args->args[0] = new arg("printf");
if(possibly_expands(in->args->args[2]) )
if(!(*it)->is_string() || (*it)->string().find('\\') != std::string::npos)
{
in->args->insert(1, new arg("%s\\ "));
if(newline) // newline: add a newline command at the end
has_escape_sequence=true;
break;
}
}
if(newline && !has_escape_sequence)
{
// newline and no potential escape: don't replace, keep echo
return has_processed_options;
}
else
{
// replace by printf
if(!in->args->can_expand())
{
// no potential expansion: static number of args
std::string format_str = "'";
for(uint32_t i=1; i<in->args->args.size(); i++)
{
brace* br = new brace(new list);
br->lst->add(new condlist(in));
br->lst->add(make_condlist("echo"));
pl->cmds[0] = br;
if(enable_interpretation)
format_str += "%b ";
else
format_str += "%s ";
}
format_str.pop_back();
if(newline)
format_str += "\\n";
format_str += '\'';
in->args->insert(1, new arg(format_str));
delete in->args->args[0];
in->args->args[0] = new arg("printf");
}
else
{
std::string printfarg="'%s";
for(uint32_t i=2; i<in->args->size(); i++)
printfarg+=" %s";
std::string format_str;
if(enable_interpretation)
format_str = "%b";
else
format_str = "%s";
list* lst=nullptr;
// more than 1 arg and first arg can't expand: can split into two printf
// printf '%s' arg1
// printf ' %s' args...
if(in->args->args.size()>2 && !in->args->args[1]->can_expand())
{
// extract arg 1
arg* arg1 = in->args->args[1];
in->args->args.erase(in->args->args.begin()+1);
delete in->args->args[0];
in->args->args[0] = new arg("printf");
lst = new list;
lst->add(new condlist(make_cmd({new arg("printf"), new arg(format_str+"\\ "), arg1 })));
lst->add(new condlist(in));
}
else
{
// can't reliable replace: keep echo if newline
if(newline)
return has_processed_options;
in->args->insert(1, new arg(format_str+"\\ "));
delete in->args->args[0];
in->args->args[0] = new arg("printf");
}
if(newline)
printfarg+="\\n";
printfarg+="'";
in->args->insert(1, new arg(printfarg));
{
if(lst == nullptr)
{
lst = new list;
lst->add(new condlist(in));
}
lst->add(make_condlist("echo"));
}
if(lst != nullptr)
{
pl->cmds[0] = new brace(lst);
}
}
}
@ -473,7 +536,7 @@ bool debashify_array_set(cmd* in, debashify_params* params)
gen=gen.substr(2);
gen.pop_back();
// create cmd out of arguments
arglist* args = parse_arglist( gen.c_str(), gen.size(), 0 ).first;
arglist* args = parse_arglist( make_context(gen) ).first;
cmd* c = new cmd(args);
// cmd first argument is _lxsh_X_create
if(params->arrays[varname])
@ -569,7 +632,7 @@ bool debashify_array_set(cmd* in, debashify_params* params)
gen=gen.substr(3);
gen.pop_back();
// create cmd out of arguments
arglist* args = parse_arglist( gen.c_str(), gen.size(), 0 ).first;
arglist* args = parse_arglist( make_context(gen) ).first;
cmd* c = new cmd(args);
// cmd first argument is _lxsh_array_create
if(params->arrays[varname])

View file

@ -18,16 +18,16 @@
#define PIPE_READ 0
#define PIPE_WRITE 1
std::vector<condlist*> do_include_exec(condlist* cmd, std::string const& filename, FILE* fd)
std::vector<condlist*> do_include_exec(condlist* cmd, parse_context ctx, FILE* fd)
{
std::vector<condlist*> ret;
std::string dir;
auto incs=do_include_raw(cmd, filename, &dir);
auto incs=do_include_raw(cmd, ctx, &dir);
for(auto it: incs)
{
parse_exec(fd, it.second, it.first);
parse_exec(fd, make_context(ctx, it.second, it.first));
}
// cd back
_cd(dir);
@ -36,7 +36,7 @@ std::vector<condlist*> do_include_exec(condlist* cmd, std::string const& filenam
}
// if first is nullptr: is a string
std::vector<condlist*> do_resolve_exec(condlist* cmd, std::string const& filename, FILE* fd)
std::vector<condlist*> do_resolve_exec(condlist* cmd, parse_context ctx, FILE* fd)
{
std::vector<condlist*> ret;
@ -45,15 +45,15 @@ std::vector<condlist*> do_resolve_exec(condlist* cmd, std::string const& filenam
{
// get
std::string dir;
p=do_resolve_raw(cmd, filename, &dir);
p=do_resolve_raw(cmd, ctx, &dir);
// do parse
parse_exec(fd, p.second, filename);
parse_exec(fd, make_context(ctx, p.second, p.first));
// cd back
_cd(dir);
}
catch(ztd::format_error& e)
catch(format_error& e)
{
throw ztd::format_error(e.what(), '`'+p.first+'`', e.data(), e.where());
throw format_error(e.what(), '`'+p.first+'`', e.data(), e.where());
}
return ret;
@ -61,7 +61,7 @@ std::vector<condlist*> do_resolve_exec(condlist* cmd, std::string const& filenam
// -- OBJECT CALLS --
bool resolve_condlist_exec(condlist* in, std::string const& filename, FILE* fd)
bool resolve_condlist_exec(condlist* in, parse_context ctx, FILE* fd)
{
cmd* tc = in->first_cmd();
if(tc == nullptr)
@ -71,23 +71,23 @@ bool resolve_condlist_exec(condlist* in, std::string const& filename, FILE* fd)
if(g_include && strcmd == "%include")
{
do_include_exec(in, filename, fd);
do_include_exec(in, ctx, fd);
return true;
}
else if(g_resolve && strcmd == "%resolve")
{
do_resolve_exec(in, filename, fd);
do_resolve_exec(in, ctx, fd);
return true;
}
return false;
}
bool resolve_exec(condlist* in, std::string const& filename, FILE* fd)
bool resolve_exec(condlist* in, parse_context ctx, FILE* fd)
{
if(!resolve_condlist_exec(in, filename, fd))
if(!resolve_condlist_exec(in, ctx, fd))
{
resolve(in, (std::string*) &filename);
resolve(in, ctx);
return false;
}
return true;
@ -138,60 +138,57 @@ std::string random_string()
return ret;
}
void parse_exec(FILE* fd, const char* in, uint32_t size, std::string const& filename)
void parse_exec(FILE* fd, parse_context ctx)
{
uint32_t i=skip_unread(in, size, 0);
#ifndef NO_PARSE_CATCH
try
ctx.i=skip_unread(ctx);
debashify_params debash_params;
list* t_lst=new list;
if(t_lst == nullptr)
throw std::runtime_error("Alloc error");
while(ctx.i<ctx.size)
{
#endif
;
debashify_params debash_params;
list* t_lst=new list;
if(t_lst == nullptr)
throw std::runtime_error("Alloc error");
while(i<size)
auto pp=parse_condlist(ctx);
ctx=pp.second;
if(ctx.has_errored)
{
auto pp=parse_condlist(in, size, i);
i=pp.second;
t_lst->add(pp.first);
if(g_resolve || g_include)
parse_list_until(ctx);
throw std::runtime_error("Aborting due to previous errors");
}
t_lst->add(pp.first);
if(g_resolve || g_include)
{
if(resolve_exec(t_lst->cls[0], ctx, fd))
{
if(resolve_exec(t_lst->cls[0], filename, fd))
{
t_lst->clear();
continue;
}
}
if(options["debashify"])
debashify(t_lst, &debash_params);
std::string gen=t_lst->generate(0);
t_lst->clear();
fprintf(fd, "%s", gen.c_str());
if(i < size)
{
if(in[i] == '#')
; // skip here
else if(is_in(in[i], COMMAND_SEPARATOR))
i++; // skip on next char
else if(is_in(in[i], CONTROL_END))
throw PARSE_ERROR(strf("Unexpected token: '%c'", in[i]), i);
i = skip_unread(in, size, i);
t_lst->clear();
continue;
}
}
delete t_lst;
#ifndef NO_PARSE_CATCH
}
catch(ztd::format_error& e)
{
throw ztd::format_error(e.what(), filename, in, e.where());
if(options["debashify"])
debashify(t_lst, &debash_params);
std::string gen=t_lst->generate(0);
t_lst->clear();
fprintf(fd, "%s", gen.c_str());
if(ctx.i < ctx.size)
{
if(ctx[ctx.i] == '#')
; // skip here
else if(is_in(ctx[ctx.i], COMMAND_SEPARATOR))
ctx.i++; // skip on next char
else if(is_in(ctx[ctx.i], CONTROL_END))
{
format_error(strf("Unexpected token: '%c'", ctx[ctx.i]), ctx);
return;
}
ctx.i = skip_unread(ctx);
}
}
#endif
delete t_lst;
}
pid_t forkexec(const char* bin, char *const args[])
@ -205,10 +202,6 @@ pid_t forkexec(const char* bin, char *const args[])
}
if (child_pid == 0) // child process
{
// char buf[1000] = {0};
// read(STDIN_FILENO, buf, 1000);
// std::cout << std::string(buf) << std::endl;
// std::cout << dup2(tfd, STDIN_FILENO) << std::endl;
setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
execv(bin, args);
throw std::runtime_error("execv() failed");
@ -234,7 +227,7 @@ int wait_pid(pid_t pid)
return WEXITSTATUS(stat);
}
int exec_process(std::string const& runtime, std::vector<std::string> const& args, std::string const& filecontents, std::string const& file)
int exec_process(std::string const& runtime, std::vector<std::string> const& args, parse_context ctx)
{
std::vector<std::string> strargs = split(runtime, " \t");
std::vector<char*> runargs;
@ -254,8 +247,6 @@ int exec_process(std::string const& runtime, std::vector<std::string> const& arg
runargs.push_back(NULL);
pid_t pid=0;
// std::string test="echo Hello world\nexit 10\n";
// fprintf(ffd, "%s\n",, test.c_str(), test.size());
FILE* ffd=0;
try
{
@ -268,7 +259,7 @@ int exec_process(std::string const& runtime, std::vector<std::string> const& arg
}
for(auto it: lxsh_extend_fcts)
fprintf(ffd, "%s\n", it.second.code);
parse_exec(ffd, filecontents, file);
parse_exec(ffd, ctx);
}
catch(std::runtime_error& e)
{

View file

@ -44,7 +44,7 @@ std::string arglist::generate(int ind)
return ret;
}
std::string pipeline::generate(int ind)
std::string pipeline::generate(int ind, generate_context* ctx)
{
std::string ret;
@ -53,11 +53,11 @@ std::string pipeline::generate(int ind)
if(negated)
ret += "! ";
ret += cmds[0]->generate(ind);
ret += cmds[0]->generate(ind, ctx);
for(uint32_t i=1 ; i<cmds.size() ; i++)
{
ret += opt_minify ? "|" : " | " ;
ret += cmds[i]->generate(ind);
ret += cmds[i]->generate(ind, ctx);
}
return ret;
@ -68,18 +68,27 @@ std::string condlist::generate(int ind)
std::string ret;
if(pls.size() <= 0)
return "";
ret += pls[0]->generate(ind);
generate_context ctx;
ret += pls[0]->generate(ind, &ctx);
for(uint32_t i=0 ; i<pls.size()-1 ; i++)
{
if(or_ops[i])
ret += opt_minify ? "||" : " || ";
else
ret += opt_minify ? "&&" : " && ";
ret += pls[i+1]->generate(ind);
ret += pls[i+1]->generate(ind, &ctx);
}
if(ret=="")
return "";
if(parallel)
if(ctx.here_document != nullptr)
{
if(parallel)
ret += '&';
ret += '\n';
ret += ctx.here_document->generate(0);
ret += '\n';
}
else if(parallel)
{
ret += opt_minify ? "&" : " &\n";
}
@ -123,12 +132,18 @@ std::string redirect::generate(int ind)
// BLOCK
std::string block::generate_redirs(int ind, std::string const& _str)
std::string block::generate_redirs(int ind, std::string const& _str, generate_context* ctx=nullptr)
{
std::string ret=" ";
bool previous_isnt_num = _str.size()>0 && !is_num(_str[_str.size()-1]);
for(auto it: redirs)
{
if(ctx != nullptr && it->here_document != nullptr)
{
if(ctx->here_document != nullptr)
throw std::runtime_error("Unsupported generation of concurrent here documents");
ctx->here_document = it->here_document;
}
std::string _r = it->generate(0);
if(opt_minify && _r.size() > 0 && !is_num(_r[0]) && previous_isnt_num)
ret.pop_back(); // remove one space if possible
@ -139,7 +154,7 @@ std::string block::generate_redirs(int ind, std::string const& _str)
return ret;
}
std::string if_block::generate(int ind)
std::string if_block::generate(int ind, generate_context* ctx)
{
std::string ret;
@ -169,11 +184,11 @@ std::string if_block::generate(int ind)
ret += indented("fi", ind);
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string for_block::generate(int ind)
std::string for_block::generate(int ind, generate_context* ctx)
{
std::string ret;
@ -187,11 +202,11 @@ std::string for_block::generate(int ind)
if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2]))
ret.pop_back();
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string while_block::generate(int ind)
std::string while_block::generate(int ind, generate_context* ctx)
{
std::string ret;
@ -207,11 +222,11 @@ std::string while_block::generate(int ind)
if(opt_minify && ret.size()>1 && !is_alpha(ret[ret.size()-2]))
ret.pop_back();
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string subshell::generate(int ind)
std::string subshell::generate(int ind, generate_context* ctx)
{
std::string ret;
// open subshell
@ -224,11 +239,11 @@ std::string subshell::generate(int ind)
// close subshell
ret += indented(")", ind);
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string shmain::generate(int ind)
std::string shmain::generate(int ind, generate_context* ctx)
{
return this->generate(false, ind);
}
@ -241,11 +256,10 @@ std::string shmain::generate(bool print_shebang, int ind)
if( opt_minify && ret[ret.size()-1] == '\n')
ret.pop_back();
ret += generate_redirs(ind, ret);
return ret;
}
std::string brace::generate(int ind)
std::string brace::generate(int ind, generate_context* ctx)
{
std::string ret;
@ -253,11 +267,11 @@ std::string brace::generate(int ind)
ret += lst->generate(ind+1);
ret += indented("}", ind);
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string function::generate(int ind)
std::string function::generate(int ind, generate_context* ctx)
{
std::string ret;
// function definition
@ -268,11 +282,11 @@ std::string function::generate(int ind)
ret += lst->generate(ind+1);
ret += indented("}", ind);
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string case_block::generate(int ind)
std::string case_block::generate(int ind, generate_context* ctx)
{
std::string ret;
ret += "case " + carg->generate(ind) + " in\n";
@ -292,27 +306,30 @@ std::string case_block::generate(int ind)
// end of case: ;;
if(opt_minify && ret[ret.size()-1] == '\n') // ;; can be right after command
ret.pop_back();
ret += indented(";;\n", ind+1);
ret += indented(";;", ind+1);
if(!opt_minify)
ret+="\n";
}
// remove ;; from last case
// replace ;; from last case with ;
if(this->cases.size()>0 && opt_minify)
{
ret.erase(ret.size()-3, 2);
ret.pop_back();
}
// close case
ind--;
ret += indented("esac", ind);
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}
std::string cmd::generate(int ind)
std::string cmd::generate(int ind, generate_context* ctx)
{
std::string ret;
// var assigns
// is a varassign cmd
if(is_cmdvar)
{
ret += args->generate(ind) + ' ';
@ -328,6 +345,7 @@ std::string cmd::generate(int ind)
return ret;
}
// pre-cmd var assigns
for(auto it: var_assigns)
{
if(it.first != nullptr)
@ -337,6 +355,7 @@ std::string cmd::generate(int ind)
ret += ' ';
}
// cmd itself
if(args!=nullptr && args->size()>0)
{
// command
@ -351,7 +370,7 @@ std::string cmd::generate(int ind)
ret.pop_back();
}
ret += generate_redirs(ind, ret);
ret += generate_redirs(ind, ret, ctx);
return ret;
}

View file

@ -1,12 +1,13 @@
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <ztd/options.hpp>
#include <ztd/shell.hpp>
#include <unistd.h>
#include "util.hpp"
#include "struc.hpp"
#include "parse.hpp"
@ -29,6 +30,7 @@ int main(int argc, char* argv[])
bool optstop=false;
shmain *sh=nullptr, *tsh=nullptr;
try
{
args=options.process(argc, argv, {.stop_on_argument=true, .output_doubledash=true} );
@ -37,53 +39,49 @@ int main(int argc, char* argv[])
optstop=true;
args.erase(args.begin());
}
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
return ERR_OPT;
}
oneshot_opt_process(argv[0]);
// resolve input
std::string file;
if(args.size() > 0) // argument provided
{
if(args[0] == "-" || args[0] == "/dev/stdin") //stdin
oneshot_opt_process(argv[0]);
// resolve input
std::string file;
if(args.size() > 0) // argument provided
{
file = "/dev/stdin";
if(args[0] == "-" || args[0] == "/dev/stdin") //stdin
{
file = "/dev/stdin";
}
else
{
file=args[0];
}
}
else
{
file=args[0];
if(isatty(fileno(stdin))) // stdin is interactive
{
print_help(argv[0]);
return ERR_HELP;
}
else // is piped
{
file = "/dev/stdin";
args.push_back("/dev/stdin");
}
}
}
else
{
if(isatty(fileno(stdin))) // stdin is interactive
{
print_help(argv[0]);
return ERR_HELP;
}
else // is piped
{
file = "/dev/stdin";
args.push_back("/dev/stdin");
}
}
// parsing
// parsing
sh = new shmain(new list);
shmain* sh = new shmain(new list);
shmain* tsh = nullptr;
try
{
bool is_exec = false;
bool first_run = true;
// do parsing
bool shebang_is_bin=false;
bool parse_bash=false;
parse_context ctx;
std::string binshebang;
for(uint32_t i=0 ; i<args.size() ; i++)
{
std::string file = args[i];
@ -96,7 +94,9 @@ int main(int argc, char* argv[])
{
first_run=false;
// resolve shebang
shebang_is_bin = ( basename(argv[0]) == basename(shebang) );
binshebang = basename(shebang);
shebang_is_bin = ( basename(argv[0]) == binshebang );
parse_bash = (options["debashify"] || binshebang == "bash" || binshebang == "lxsh");
// detect if need execution
if(options['e'])
@ -113,7 +113,10 @@ int main(int argc, char* argv[])
throw std::runtime_error("Option -e must be before file");
if(shebang_is_bin) // enable debashify option
{
shebang="#!/bin/sh";
options["debashify"].activated=true;
}
oneshot_opt_process(argv[0]);
get_opts();
@ -124,25 +127,27 @@ int main(int argc, char* argv[])
if(!add_include(file))
continue;
ctx.data=filecontents.data();
ctx = make_context(filecontents, file, parse_bash);
if(is_exec)
{
if(options["debashify"])
shebang = "#!/bin/sh";
if(options["debashify"] || basename(shebang) == "bash")
g_bash = true;
args.erase(args.begin());
return exec_process(shebang.substr(2), args, filecontents, file);
return exec_process(shebang.substr(2), args, ctx);
}
else
{
tsh = parse_text(filecontents, file);
auto pp = parse_text(ctx);
tsh = pp.first;
ctx = pp.second;
if(shebang_is_bin) // resolve lxsh shebang to sh
tsh->shebang="#!/bin/sh";
/* mid processing */
// resolve/include
if(g_include || g_resolve)
resolve(tsh);
resolve(tsh, ctx);
// concatenate to main
sh->concat(tsh);
@ -166,11 +171,13 @@ int main(int argc, char* argv[])
list_fcts(sh, re_fct_exclude);
else if(options["list-cmd"])
list_cmds(sh, regex_null);
// output
#ifdef DEBUG_MODE
else if(options['J'])
{
std::cout << gen_json_struc(sh) << std::endl;
}
#endif
// output
else
{
// post-listing modifiers
@ -186,7 +193,10 @@ int main(int argc, char* argv[])
// processing before output
// minify
if(options['m'])
{
opt_minify=true;
string_processors(sh);
}
if(options["minify-quotes"])
minify_quotes(sh);
if(options["minify-var"])
@ -215,8 +225,7 @@ int main(int argc, char* argv[])
}
}
}
#ifndef NO_PARSE_CATCH
catch(ztd::format_error& e)
catch(format_error& e)
{
if(tsh != nullptr)
delete tsh;
@ -224,12 +233,17 @@ int main(int argc, char* argv[])
printFormatError(e);
return ERR_PARSE;
}
#endif
catch(ztd::option_error& e)
{
std::cerr << e.what() << std::endl;
return ERR_OPT;
}
catch(std::runtime_error& e)
{
if(tsh != nullptr)
delete tsh;
delete sh;
if(sh != nullptr)
delete sh;
std::cerr << e.what() << std::endl;
return ERR_RUNTIME;
}

View file

@ -69,8 +69,8 @@ bool r_replace_var(_obj* in, strmap_t* varmap)
return true;
}
const char* escaped_char=" \\\t!\"()|&*?~";
const char* doublequote_escape_char=" \t'|&\\*?~";
const char* escaped_char=" \\\t!\"()|&*?~><";
const char* doublequote_escape_char=" \t'|&\\*?~><";
uint32_t count_escape_chars(std::string const& in, bool doublequote)
{
uint32_t r=0;

View file

@ -7,55 +7,53 @@
#include "version.h"
#include "g_version.h"
ztd::option_set options = gen_options();
bool opt_minify=false;
ztd::option_set options( {
ztd::option("\r [Help]"),
ztd::option('h', "help", false, "Display this help message"),
ztd::option("version", false, "Display version"),
ztd::option("help-link-commands", false, "Print help for linker commands"),
ztd::option("help-extend-fcts", false, "Print help for lxsh extension functions"),
ztd::option("\r [Output]"),
ztd::option('o', "output", true , "Output result script to file", "file"),
ztd::option('c', "stdout", false, "Output result script to stdout"),
ztd::option('e', "exec", false, "Directly execute script"),
ztd::option("no-shebang", false, "Don't output shebang"),
#ifdef DEBUG_MODE
ztd::option("\r [Debugging]"),
ztd::option('J', "json", false, "Output the json structure"),
#endif
ztd::option("\r [Processing]"),
ztd::option('m', "minify", false, "Minify code without changing functionality"),
ztd::option('M', "minify-full", false, "Enable all minifying features: -m --minify-quotes --minify-var --minify-fct --remove-unused"),
ztd::option("minify-quotes", false, "Remove unnecessary quotes"),
ztd::option('C', "no-cd", false, "Don't cd when doing %include and %resolve"),
ztd::option('I', "no-include", false, "Don't resolve %include commands"),
ztd::option('R', "no-resolve", false, "Don't resolve %resolve commands"),
ztd::option("no-extend", false, "Don't add lxsh extension functions"),
ztd::option("debashify", false, "Attempt to turn a bash-specific script into a POSIX shell script"),
ztd::option("remove-unused", false, "Remove unused functions and variables"),
ztd::option("list-cmd", false, "List all commands invoked in the script"),
ztd::option("\r [Variable processing]"),
ztd::option("exclude-var", true, "List of matching regex to ignore for variable processing", "list"),
ztd::option("no-exclude-reserved",false, "Don't exclude reserved variables"),
ztd::option("minify-var", false, "Minify variable names"),
ztd::option("list-var", false, "List all variables set and invoked in the script"),
ztd::option("list-var-def", false, "List all variables set in the script"),
ztd::option("list-var-call", false, "List all variables invoked in the script"),
ztd::option("unset-var", false, "Add 'unset' to all variables at the start of the script to avoid environment interference"),
ztd::option("\r [Function processing]"),
ztd::option("exclude-fct", true, "List of matching regex to ignore for function processing", "list"),
ztd::option("minify-fct", false, "Minify function names"),
ztd::option("list-fct", false, "List all functions defined in the script")
} );
bool g_cd=false;
bool g_include=true;
bool g_resolve=true;
bool g_shebang=true;
ztd::option_set gen_options()
{
ztd::option_set ret;
ret.add(
ztd::option("\r [Help]"),
ztd::option('h', "help", false, "Display this help message"),
ztd::option("version", false, "Display version"),
ztd::option("help-link-commands", false, "Print help for linker commands"),
ztd::option("help-extend-fcts", false, "Print help for lxsh extension functions"),
ztd::option("\r [Output]"),
ztd::option('o', "output", true , "Output result script to file", "file"),
ztd::option('c', "stdout", false, "Output result script to stdout"),
ztd::option('e', "exec", false, "Directly execute script"),
ztd::option("no-shebang", false, "Don't output shebang"),
ztd::option('J', "json", false, "Output the json structure"),
ztd::option("\r [Processing]"),
ztd::option('m', "minify", false, "Minify code without changing functionality"),
ztd::option('M', "minify-full", false, "Enable all minifying features: -m --minify-quotes --minify-var --minify-fct --remove-unused"),
ztd::option("minify-quotes", false, "Remove unnecessary quotes"),
ztd::option('C', "no-cd", false, "Don't cd when doing %include and %resolve"),
ztd::option('I', "no-include", false, "Don't resolve %include commands"),
ztd::option('R', "no-resolve", false, "Don't resolve %resolve commands"),
ztd::option("no-extend", false, "Don't add lxsh extension functions"),
ztd::option("debashify", false, "Attempt to turn a bash-specific script into a POSIX shell script"),
ztd::option("\r [var/fct processing]"),
ztd::option("minify-var", false, "Minify variable names"),
ztd::option("minify-fct", false, "Minify function names"),
ztd::option("exclude-var", true, "List of matching regex to ignore for variable processing", "list"),
ztd::option("exclude-fct", true, "List of matching regex to ignore for function processing", "list"),
ztd::option("no-exclude-reserved",false, "Don't exclude reserved variables"),
ztd::option("list-var", false, "List all variables set and invoked in the script"),
ztd::option("list-var-def", false, "List all variables set in the script"),
ztd::option("list-var-call", false, "List all variables invoked in the script"),
ztd::option("list-fct", false, "List all functions defined in the script"),
ztd::option("list-cmd", false, "List all commands invoked in the script"),
ztd::option("remove-unused", false, "Remove unused functions and variables"),
ztd::option("unset-var", false, "Add 'unset' to all vars at the start of the script to avoid environment interference")
);
return ret;
}
void get_opts()
{
g_cd=!options['C'].activated;
@ -102,9 +100,8 @@ ztd::option_set create_resolve_opts()
void print_help(const char* arg0)
{
printf("%s [options] <file> [arg...]\n", arg0);
printf("Link extended shell\n");
printf("Include files and resolve commands on build time\n");
printf("See --help-commands for help on linker commands\n");
printf("Extended shell linker\n");
printf("Include files, resolve commands on build time, process and minify shell code\n");
printf("\n");
printf("Options:\n");
options.print_help(4,25);

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,10 @@
#include "util.hpp"
#include "shellcode.hpp"
#include "struc_helper.hpp"
#include "options.hpp"
#include "minify.hpp"
#include "errcodes.h"
// Global regex
@ -415,6 +419,67 @@ std::set<std::string> find_lxsh_commands(shmain* sh)
return ret;
}
std::set<std::string> get_processors(std::string const& in)
{
std::set<std::string> ret;
if(in.size()>2 && in[0] == '\'' && in[in.size()-1] == '\'')
{
uint32_t i=1;
while(true)
{
std::string ln = in.substr(i, in.find('\n', i)-i);
if(ln.size()>1 && ln[0] == '#' && is_alphanum(ln[1]))
{
i+=ln.size();
ret.insert(get_word(make_context(ln.substr(1)), SEPARATORS).first);
}
else
break;
}
}
return ret;
}
bool r_do_string_processor(_obj* in)
{
if(in->type == _obj::subarg_string)
{
string_subarg* t = dynamic_cast<string_subarg*>(in);
auto v = get_processors(t->val);
if(v.find("LXSH_PARSE_MINIFY") != v.end())
{
try
{
std::string stringcode = t->val.substr(1, t->val.size()-2);
shmain* tsh = parse_text( stringcode ).first;
require_rescan_all();
if(options["remove-unused"])
delete_unused( tsh, re_var_exclude, re_fct_exclude );
if(options["minify-quotes"])
minify_quotes(tsh);
if(options["minify-var"])
minify_var( tsh, re_var_exclude );
if(options["minify-fct"])
minify_fct( tsh, re_fct_exclude );
require_rescan_all();
t->val='\'' + tsh->generate(false, 0) + '\'';
}
catch(format_error& e) // if fail: skip processing
{
std::cerr << "Exception caused in string processing LXSH_PARSE_MINIFY\n";
printFormatError(e);
exit(ERR_RUNTIME);
}
}
}
return true;
}
void string_processors(_obj* in)
{
recurse(r_do_string_processor, in);
}
/** JSON **/
std::string quote_string(std::string const& in)
@ -454,6 +519,7 @@ std::string boolstring(bool in)
return "false";
}
#ifdef DEBUG_MODE
std::string gen_json_struc(_obj* o)
{
if(o==nullptr)
@ -781,3 +847,4 @@ std::string gen_json_struc(_obj* o)
}
return gen_json(vec);
}
#endif

View file

@ -34,7 +34,6 @@ bool add_include(std::string const& file)
if(it == truepath)
return false;
}
// std::cout << truepath << std::endl;
included.push_back(truepath);
return true;
}
@ -60,7 +59,7 @@ void _cd(std::string const& dir)
// -- COMMANDS --
// return <name, contents>[]
std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, std::string const& filename, std::string* ex_dir)
std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, parse_context ctx, std::string* ex_dir)
{
std::vector<std::pair<std::string, std::string>> ret;
@ -68,7 +67,7 @@ std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, s
std::vector<std::string> rargs;
try
{
rargs = opts.process(cmd->first_cmd()->args->strargs(1), false, true, false);
rargs = opts.process(cmd->first_cmd()->args->strargs(1), {.stop_on_argument=true});
}
catch(ztd::option_error& e)
{
@ -78,7 +77,7 @@ std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, s
std::string dir;
if(g_cd && !opts['C'])
{
dir=_pre_cd(filename);
dir=_pre_cd(ctx.filename);
if(ex_dir!=nullptr)
*ex_dir=dir;
}
@ -106,7 +105,7 @@ std::vector<std::pair<std::string, std::string>> do_include_raw(condlist* cmd, s
}
//
std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, std::string const& filename, std::string* ex_dir)
std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, parse_context ctx, std::string* ex_dir)
{
std::pair<std::string, std::string> ret;
@ -114,7 +113,7 @@ std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, std::string co
std::vector<std::string> rargs;
try
{
rargs = opts.process(cmd->first_cmd()->args->strargs(1), false, true, false);
rargs = opts.process(cmd->first_cmd()->args->strargs(1), {.stop_on_argument=true} );
}
catch(ztd::option_error& e)
{
@ -124,7 +123,7 @@ std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, std::string co
std::string dir;
if(g_cd && !opts['C'])
{
dir=_pre_cd(filename);
dir=_pre_cd(ctx.filename);
if(ex_dir!=nullptr)
*ex_dir=dir;
}
@ -153,23 +152,33 @@ std::pair<std::string, std::string> do_resolve_raw(condlist* cmd, std::string co
return ret;
}
std::vector<condlist*> do_include_parse(condlist* cmd, std::string const& filename)
std::vector<condlist*> do_include_parse(condlist* cmd, parse_context ctx)
{
std::vector<condlist*> ret;
std::string dir;
auto incs=do_include_raw(cmd, filename, &dir);
auto incs=do_include_raw(cmd, ctx, &dir);
for(auto it: incs)
std::vector<shmain*> shs;
shs.resize(incs.size());
for(uint32_t i=0; i<incs.size(); i++)
{
parse_context newctx = make_context(ctx, incs[i].second, incs[i].first);
auto pp = parse_text(newctx);
shmain* sh = pp.first;
resolve(sh, pp.second);
shs[i] = sh;
}
for(auto sh: shs)
{
shmain* sh=parse_text(it.second, it.first);
resolve(sh);
// get the cls
ret.insert(ret.end(), sh->lst->cls.begin(), sh->lst->cls.end());
// safety and cleanup
sh->lst->cls.resize(0);
delete sh;
}
shs.resize(0);
// cd back
_cd(dir);
@ -177,7 +186,7 @@ std::vector<condlist*> do_include_parse(condlist* cmd, std::string const& filena
}
// if first is nullptr: is a string
std::vector<condlist*> do_resolve_parse(condlist* cmd, std::string const& filename)
std::vector<condlist*> do_resolve_parse(condlist* cmd, parse_context ctx)
{
std::vector<condlist*> ret;
@ -186,10 +195,13 @@ std::vector<condlist*> do_resolve_parse(condlist* cmd, std::string const& filena
{
// get
std::string dir;
p=do_resolve_raw(cmd, filename, &dir);
p=do_resolve_raw(cmd, ctx, &dir);
// do parse
shmain* sh = parse_text(p.second);
resolve(sh);
parse_context newctx = make_context(ctx, p.second, '`'+p.first+'`');
auto pp = parse_text(newctx);
shmain* sh = pp.first;
resolve(sh, pp.second);
// get the cls
ret = sh->lst->cls;
// safety and cleanup
@ -198,9 +210,9 @@ std::vector<condlist*> do_resolve_parse(condlist* cmd, std::string const& filena
// cd back
_cd(dir);
}
catch(ztd::format_error& e)
catch(format_error& e)
{
throw ztd::format_error(e.what(), '`'+p.first+'`', e.data(), e.where());
throw format_error(e.what(), '`'+p.first+'`', e.data(), e.where());
}
return ret;
@ -208,7 +220,7 @@ std::vector<condlist*> do_resolve_parse(condlist* cmd, std::string const& filena
// -- OBJECT CALLS --
std::pair< std::vector<condlist*> , bool > resolve_condlist(condlist* in, std::string const& filename)
std::pair< std::vector<condlist*> , bool > resolve_condlist(condlist* in, parse_context ctx)
{
cmd* tc = in->first_cmd();
if(tc == nullptr)
@ -217,14 +229,14 @@ std::pair< std::vector<condlist*> , bool > resolve_condlist(condlist* in, std::s
std::string const& strcmd=tc->arg_string(0);
if(g_include && strcmd == "%include")
return std::make_pair(do_include_parse(in, filename), true);
return std::make_pair(do_include_parse(in, ctx), true);
else if(g_resolve && strcmd == "%resolve")
return std::make_pair(do_resolve_parse(in, filename), true);
return std::make_pair(do_resolve_parse(in, ctx), true);
else
return std::make_pair(std::vector<condlist*>(), false);
}
std::pair< std::vector<arg*> , bool > resolve_arg(arg* in, std::string const& filename, bool forcequote=false)
std::pair< std::vector<arg*> , bool > resolve_arg(arg* in, parse_context ctx, bool forcequote=false)
{
std::vector<arg*> ret;
if(in == nullptr)
@ -250,12 +262,12 @@ std::pair< std::vector<arg*> , bool > resolve_arg(arg* in, std::string const& fi
std::string fulltext;
if(g_include && strcmd == "%include")
{
for(auto it: do_include_raw(tc, filename) )
for(auto it: do_include_raw(tc, ctx) )
fulltext += it.second;
}
else if(g_resolve && strcmd == "%resolve")
{
fulltext = do_resolve_raw(tc, filename).second;
fulltext = do_resolve_raw(tc, ctx).second;
}
else // skip
continue;
@ -321,10 +333,10 @@ std::pair< std::vector<arg*> , bool > resolve_arg(arg* in, std::string const& fi
return std::make_pair(ret, has_resolved);
}
void resolve(_obj* in, parse_context* ctx);
// -- RECURSIVE CALL --
bool r_resolve(_obj* o, std::string* filename)
bool r_resolve(_obj* o, parse_context* ct)
{
switch(o->type)
{
@ -337,7 +349,7 @@ bool r_resolve(_obj* o, std::string* filename)
auto t = dynamic_cast<list*>(o);
for(uint32_t i=0 ; i<t->cls.size() ; i++)
{
auto r=resolve_condlist(t->cls[i], *filename);
auto r=resolve_condlist(t->cls[i], *ct);
if(r.second)
{
// add new cls after current
@ -350,7 +362,7 @@ bool r_resolve(_obj* o, std::string* filename)
}
else
{
resolve(t->cls[i], filename);
resolve(t->cls[i], ct);
}
}
return false;
@ -360,7 +372,7 @@ bool r_resolve(_obj* o, std::string* filename)
auto t = dynamic_cast<arglist*>(o);
for(uint32_t i=0 ; i<t->size() ; i++)
{
auto r=resolve_arg(t->args[i], *filename);
auto r=resolve_arg(t->args[i], *ct);
if(r.first.size()>0)
{
// add new args
@ -372,7 +384,7 @@ bool r_resolve(_obj* o, std::string* filename)
}
else
{
resolve(t->args[i], filename);
resolve(t->args[i], ct);
}
}
return false;
@ -382,12 +394,12 @@ bool r_resolve(_obj* o, std::string* filename)
auto t = dynamic_cast<cmd*>(o);
for(auto it: t->var_assigns) // var assigns
{
resolve_arg(it.second, *filename, true); // force quoted
resolve(it.second, filename);
resolve_arg(it.second, *ct, true); // force quoted
resolve(it.second, ct);
}
for(auto it: t->redirs)
resolve(it, filename);
resolve(t->args, filename);
resolve(it, ct);
resolve(t->args, ct);
return false;
}; break;
case _obj::block_case :
@ -395,15 +407,15 @@ bool r_resolve(_obj* o, std::string* filename)
auto t = dynamic_cast<case_block*>(o);
for(auto sc: t->cases)
{
resolve_arg(t->carg, *filename, true); // force quoted
resolve(t->carg, filename);
resolve_arg(t->carg, *ct, true); // force quoted
resolve(t->carg, ct);
for(auto it: sc.first)
{
resolve_arg(it, *filename, true); // force quoted
resolve(it, filename);
resolve_arg(it, *ct, true); // force quoted
resolve(it, ct);
}
resolve(sc.second, filename);
resolve(sc.second, ct);
}
}; break;
default: break;
@ -412,12 +424,13 @@ bool r_resolve(_obj* o, std::string* filename)
}
// recursive call of resolve
void resolve(_obj* in, std::string* filename)
void resolve(_obj* in, parse_context* ctx)
{
recurse(r_resolve, in, filename);
recurse(r_resolve, in, ctx);
}
void resolve(shmain* sh)
// recursive call of resolve
void resolve(_obj* in, parse_context ctx)
{
recurse(r_resolve, sh, &sh->filename);
recurse(r_resolve, in, &ctx);
}

View file

@ -4,13 +4,14 @@
#include "processing.hpp"
#include "struc_helper.hpp"
const std::map<const std::string, const struct lxsh_fct> lxsh_extend_fcts = {
{ "_lxsh_random", { "[K]", "Generate a random number between 0 and 2^(k*8). Default 2", RANDOM_SH} },
{ "_lxsh_random_string", { "[N]", "Generate a random alphanumeric string of length N. Default 20", RANDOM_STRING_SH} },
{ "_lxsh_random_tmpfile", { "[N]", "Get a random TMP filepath, with N random chars. Default 20", RANDOM_TMPFILE_SH, {"_lxsh_random_string"} } }
const std::map<const std::string, const lxsh_fct> lxsh_extend_fcts = {
{ "_lxsh_random", { "[K]", "Generate a random number between 0 and 2^(K*8). Default 2", RANDOM_SH} },
{ "_lxsh_random_string", { "[N]", "Generate a random alphanumeric string of length N. Default 20", RANDOM_STRING_SH} },
{ "_lxsh_random_tmpfile", { "[N]", "Get a random TMP filepath, with N random chars. Default 20", RANDOM_TMPFILE_SH, {"_lxsh_random_string"} }
}
};
const std::map<const std::string, const struct lxsh_fct> lxsh_array_fcts = {
const std::map<const std::string, const lxsh_fct> lxsh_array_fcts = {
{ "_lxsh_array_create", { "<VAL...>", "Create an array out of input arguments", ARRAY_CREATE_SH} },
{ "_lxsh_array_get", { "<ARRAY> <I>", "Get value from array", ARRAY_GET_SH} },
{ "_lxsh_array_set", { "<ARRAY> <I> <VAL>", "Set value of array", ARRAY_SET_SH} },
@ -19,7 +20,7 @@ const std::map<const std::string, const struct lxsh_fct> lxsh_array_fcts = {
{ "_lxsh_map_set", { "<MAP> <KEY> <VAL>", "Set value of map", MAP_SET_SH} }
};
std::map<const std::string, const struct lxsh_fct> create_allfcts()
std::map<const std::string, const lxsh_fct> create_allfcts()
{
auto r = lxsh_array_fcts;
for(auto it: lxsh_extend_fcts)
@ -27,7 +28,7 @@ std::map<const std::string, const struct lxsh_fct> create_allfcts()
return r;
}
const std::map<const std::string, const struct lxsh_fct> lxsh_allfcts = create_allfcts();
const std::map<const std::string, const lxsh_fct> lxsh_allfcts = create_allfcts();
void add_lxsh_fcts(shmain* sh, std::set<std::string> fcts)
{

View file

@ -9,7 +9,7 @@
arg* make_arg(std::string const& in)
{
return parse_arg(in.c_str(), in.size(), 0).first;
return parse_arg(make_context(in)).first;
}
cmd* make_cmd(std::vector<std::string> const& args)
@ -34,7 +34,7 @@ cmd* make_cmd(std::vector<arg*> const& args)
cmd* make_cmd(std::string const& in)
{
return parse_cmd(in.c_str(), in.size(), 0).first;
return parse_cmd(make_context(in)).first;
}
pipeline* make_pipeline(std::vector<block*> const& bls)
@ -48,22 +48,23 @@ pipeline* make_pipeline(std::vector<block*> const& bls)
pipeline* make_pipeline(std::string const& in)
{
return parse_pipeline(in.c_str(), in.size(), 0).first;
return parse_pipeline(make_context(in)).first;
}
condlist* make_condlist(std::string const& in)
{
return parse_condlist(in.c_str(), in.size(), 0).first;
return parse_condlist(make_context(in)).first;
}
list* make_list(std::string const& in)
{
return parse_list_until(in.c_str(), in.size(), 0, 0).first;
auto t = parse_list_until(make_context(in));
return std::get<0>(t);
}
block* make_block(std::string const& in)
{
return parse_block(in.c_str(), in.size(), 0).first;
return parse_block(make_context(in)).first;
}
@ -71,7 +72,7 @@ block* make_block(std::string const& in)
arg* copy(arg* in) {
std::string str = in->generate(0);
return parse_arg(str.c_str(), str.size(), 0).first;
return parse_arg(make_context(str)).first;
}
// modifiers
@ -147,20 +148,47 @@ size_t cmd::arglist_size()
// string getters
bool arg::is_string()
{
return sa.size() == 1 && sa[0]->type == _obj::subarg_string;
}
std::string arg::string()
{
if(sa.size() != 1 || sa[0]->type != subarg::subarg_string)
if(!this->is_string())
return "";
return dynamic_cast<string_subarg*>(sa[0])->val;
}
std::string arg::first_sa_string()
{
if(sa.size() <=0 || sa[0]->type != subarg::subarg_string)
return "";
if(sa.size() <=0 || sa[0]->type != _obj::subarg_string)
return "";
return dynamic_cast<string_subarg*>(sa[0])->val;
}
bool arg::can_expand()
{
for(auto it: sa)
{
if(it->type != _obj::subarg_string && !it->quoted)
return true;
}
return false;
}
bool arglist::can_expand()
{
bool arg_expands=false;
for(auto it: args)
{
arg_expands = it->can_expand();
if(arg_expands)
return true;
}
return false;
}
std::vector<std::string> arglist::strargs(uint32_t start)
{
std::vector<std::string> ret;

View file

@ -7,8 +7,10 @@
#include <tuple>
#include <iostream>
#include <fstream>
#include <ztd/shell.hpp>
#include <ztd/color.hpp>
std::string indenting_string="\t";
@ -210,16 +212,15 @@ std::string repeatString(std::string const& str, uint32_t n)
return ret;
}
void printFormatError(ztd::format_error const& e, bool print_line)
void printFormatError(format_error const& e, bool print_line)
{
printErrorIndex(e.data(), e.where(), e.what(), e.origin(), print_line);
}
const char* in = e.data();
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin, bool print_line)
{
int i=0, j=0; // j: last newline
int line=1; //n: line #
int in_size=strlen(in);
uint64_t index = e.where();
uint64_t i=0, j=0; // j: last newline
uint64_t line=1; //n: line #
uint64_t in_size=strlen(in);
if(index >= 0)
{
while(i < in_size && i < index)
@ -232,59 +233,25 @@ void printErrorIndex(const char* in, const int index, const std::string& message
i++;
}
while(i < in_size && in[i]!='\n')
{
i++;
}
}
if(origin != "")
std::cerr << ztd::color::b_white;
fprintf(stderr, "%s:%lu:%lu: ", e.origin(), line, index-j+1);
ztd::color level_color;
const std::string& level = e.level();
if(level == "error")
level_color = ztd::color::b_red;
else if(level == "warning")
level_color = ztd::color::b_magenta;
else if(level == "info")
level_color = ztd::color::b_cyan;
std::cerr << level_color << e.level() << ztd::color::none;
fprintf(stderr, ": %s\n", e.what());
if(print_line)
{
fprintf(stderr, "%s:%u:%u: %s\n", origin.c_str(), line, index-j+1, message.c_str());
if(print_line)
{
std::cerr << std::string(in+j, i-j) << std::endl;
std::cerr << repeatString(" ", index-j) << '^' << std::endl;
}
std::cerr << std::string(in+j, i-j) << std::endl;
std::cerr << repeatString(" ", index-j) << '^' << std::endl;
}
}
int execute(shmain* sh, std::vector<std::string>& args)
{
std::string data=sh->generate();
std::string filename = basename(args[0]);
// generate path
std::string tmpdir = (getenv("TMPDIR") != NULL) ? getenv("TMPDIR") : "/tmp" ;
std::string dirpath = tmpdir + "/lxsh_" + ztd::sh("tr -dc '[:alnum:]' < /dev/urandom | head -c10");
std::string filepath = dirpath+'/'+filename;
// create dir
if(ztd::exec("mkdir", "-p", dirpath).second)
{
throw std::runtime_error("Failed to create directory '"+dirpath+'\'');
}
// create stream
std::ofstream stream(filepath);
if(!stream)
{
ztd::exec("rm", "-rf", dirpath);
throw std::runtime_error("Failed to write to '"+filepath+'\'');
}
// output
stream << data;
stream.close();
if(ztd::exec("chmod", "+x", filepath).second != 0)
{
ztd::exec("rm", "-rf", dirpath);
throw std::runtime_error("Failed to make '"+filepath+"' executable");
}
// exec
int retval=_exec(filepath, args);
ztd::exec("rm", "-rf", dirpath);
return retval;
}