From c12ec601f15645932db7511c28fd6cbb3caa84d2 Mon Sep 17 00:00:00 2001 From: zawwz Date: Wed, 20 Jan 2021 16:31:31 +0100 Subject: [PATCH] implement [[ ]] debashify --- include/struc_helper.hpp | 15 ++++++ include/util.hpp | 5 ++ src/debashify.cpp | 99 ++++++++++++++++++++++++++++++++-- src/main.cpp | 20 ++++--- src/parse.cpp | 111 +++++++++++++++++++++++++-------------- src/struc_helper.cpp | 63 ++++++++++++++++++++-- 6 files changed, 259 insertions(+), 54 deletions(-) diff --git a/include/struc_helper.hpp b/include/struc_helper.hpp index 85a6bf0..591d551 100644 --- a/include/struc_helper.hpp +++ b/include/struc_helper.hpp @@ -3,13 +3,28 @@ #include "struc.hpp" +// makers arg* make_arg(std::string const& in); + cmd* make_cmd(std::vector const& args); +cmd* make_cmd(std::vector const& args); cmd* make_cmd(std::string const& in); + +pipeline* make_pipeline(std::vector const& bls); +pipeline* make_pipeline(std::string const& in); + condlist* make_condlist(std::string const& in); +list* make_list(std::string const& in); +// testers + +bool arg_has_char(char c, arg* in); + +// modifiers void force_quotes(arg* in); +void add_quotes(arg* in); +// operators inline bool operator==(arg a, std::string const& b) { return a.equals(b); } #endif //STRUC_HELPER_HPP diff --git a/include/util.hpp b/include/util.hpp index 28e53c2..1a4f9e8 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -45,6 +45,11 @@ std::string strf( const std::string& format, Args ... args ) return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside } +template +std::vector make_vector(Args ... args) +{ + return std::vector( { args... } ); +} template std::vector> sort_by_value(std::map const& in) diff --git a/src/debashify.cpp b/src/debashify.cpp index 941e925..468a786 100644 --- a/src/debashify.cpp +++ b/src/debashify.cpp @@ -5,10 +5,103 @@ #include "parse.hpp" #include "struc_helper.hpp" -bool debashify_bashtest(cmd* in) +/* +[[ ]] debashifying: +[[ EXPRESSION && EXPRESSION ]] separated into two parts +EXPRESSION : gen_bashtest_cmd +&& , || : debashify_bashtest +*/ + +// [[ $a = b ]] : quote vars +// [[ a == b ]] : replace == with = +// [[ a = b* ]] : case a in b*) true;; *) false;; esac +// [[ a =~ b ]] : echo a | grep -q b +pipeline* gen_bashtest_cmd(std::vector args) { + pipeline* ret = nullptr; + + if(args.size() == 3 && args[1]->string() == "==") + { + delete args[1]->sa[0]; + args[1]->sa[0] = new string_subarg("="); + } + + if(args.size() == 3 && args[1]->string() == "=" && arg_has_char('*', args[2])) + { + delete args[1]; + args[1]=nullptr; + case_block* tc = new case_block(args[0]); + tc->cases.push_back( std::make_pair(std::vector({args[2]}), make_list("true")) ); + tc->cases.push_back( std::make_pair(std::vector({new arg("*")}), make_list("false")) ); + ret = new pipeline(tc); + } + else if(args.size() == 3 && args[1]->string() == "=~") + { + delete args[1]; + args[1]=nullptr; + cmd* echo_arg1 = make_cmd( std::vector({ new arg("echo"), args[0] }) ); + cmd* grep_arg2 = make_cmd( std::vector({ new arg("grep"), new arg("-q"), new arg("--"), args[2] }) ); + add_quotes(args[2]); + ret = make_pipeline( std::vector({echo_arg1, grep_arg2}) ); + } + else // regular [ ] + { + cmd* t = make_cmd(args); + t->args->insert(0, new arg("[")); + t->add(new arg("]")); + ret = new pipeline(t); + } + // arg oblivious replacements: + // quote variables + for(auto it: args) + { + if(it!=nullptr) + force_quotes(it); + } + return ret; +} + +// [[ a && b ]] : [ a ] && [ b ] +bool debashify_bashtest(pipeline* pl) +{ + if(pl->cmds.size()<=0) + return false; + + if(pl->cmds[0]->type != _obj::block_cmd) + return false; + cmd* in = dynamic_cast(pl->cmds[0]); + if(in->firstarg_string() == "[[") - throw std::runtime_error("Cannot debashify '[[ ]]'"); + { + // throw std::runtime_error("Cannot debashify '[[ ]]'"); + brace* br = new brace(new list); + condlist* cl = new condlist; + br->lst->add(cl); + + arg *a=nullptr; + uint32_t j=1; + bool or_op=false; + for(uint32_t i=1 ; iargs->size() ; i++) + { + a = in->args->args[i]; + + if(i >= in->args->size()-1 || a->string() == "&&" || a->string() == "||") + { + pipeline* tpl = gen_bashtest_cmd(std::vector(in->args->args.begin()+j, in->args->args.begin()+i)); + cl->add(tpl, or_op); + or_op = a->string() == "||"; + j=i+1; + } + } + + delete in->args->args[0]; + delete in->args->args[in->args->args.size()-1]; + in->args->args.resize(0); + delete in; + pl->cmds[0] = br; + + return true; + } return false; } @@ -204,11 +297,11 @@ bool r_debashify(_obj* o, bool* need_random_func) case _obj::_pipeline: { pipeline* t = dynamic_cast(o); debashify_herestring(t); + debashify_bashtest(t); } break; case _obj::block_cmd: { cmd* t = dynamic_cast(o); debashify_combined_redirects(t); - debashify_bashtest(t); debashify_declare(t); debashify_array_def(t); } break; diff --git a/src/main.cpp b/src/main.cpp index 1ed0089..21ca4a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,12 +20,17 @@ #include "version.h" #include "g_version.h" +#define ERR_HELP 1001 +#define ERR_OPT 1002 +#define ERR_PARSE 1003 +#define ERR_RUNTIME 1004 + void oneshot_opt_process(const char* arg0) { if(options['h']) { print_help(arg0); - exit(0); + exit(ERR_HELP); } else if(options["version"]) { @@ -38,7 +43,7 @@ void oneshot_opt_process(const char* arg0) print_include_help(); printf("\n\n"); print_resolve_help(); - exit(0); + exit(ERR_HELP); } } @@ -55,7 +60,7 @@ int main(int argc, char* argv[]) catch(std::exception& e) { std::cerr << e.what() << std::endl; - return 1; + return ERR_OPT; } oneshot_opt_process(argv[0]); @@ -78,7 +83,7 @@ int main(int argc, char* argv[]) if(isatty(fileno(stdin))) // stdin is interactive { print_help(argv[0]); - return 1; + return ERR_HELP; } else // is piped { @@ -132,7 +137,10 @@ int main(int argc, char* argv[]) continue; tsh = parse_text(filecontents, file); if(shebang_is_bin) // resolve lxsh shebang to sh + { + options["debashify"].activated=true; tsh->shebang="#!/bin/sh"; + } /* mid processing */ // resolve/include @@ -211,7 +219,7 @@ int main(int argc, char* argv[]) delete tsh; delete sh; printFormatError(e); - return 100; + return ERR_PARSE; } #endif catch(std::runtime_error& e) @@ -220,7 +228,7 @@ int main(int argc, char* argv[]) delete tsh; delete sh; std::cerr << e.what() << std::endl; - return 2; + return ERR_RUNTIME; } delete sh; diff --git a/src/parse.cpp b/src/parse.cpp index 9394c98..e398113 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -510,7 +510,7 @@ std::pair parse_redirect(const char* in, uint32_t size, uin else if(in[i] == '&') // >& bash operator { if(!g_bash) - throw PARSE_ERROR("bash specific: '>&'. Use --debashify to remove bashisms", i); + throw PARSE_ERROR("bash specific: '>&'", i); i++; needs_arg=true; } @@ -534,7 +534,7 @@ std::pair parse_redirect(const char* in, uint32_t size, uin if(i parse_redirect(const char* in, uint32_t size, uin else if( word_eq("&>", in, size, i) ) // &> bash operator { if(!g_bash) - throw PARSE_ERROR("bash specific: '&>'. Use --debashify to remove bashisms", i); + throw PARSE_ERROR("bash specific: '&>'", i); i+=2; if(i') i++; @@ -640,55 +640,86 @@ std::pair parse_arglist(const char* in, uint32_t size, uint3 try { #endif - if(is_in(in[i], SPECIAL_TOKENS) && !word_eq("&>", in, size, i)) + ; + if(word_eq("[[", in, size, i, ARG_END) ) // [[ bash specific parsing + { + if(!g_bash) + throw PARSE_ERROR("bash specific: '[['", i); + while(true) + { + if(ret == nullptr) + ret = new arglist; + auto pp=parse_arg(in, size, i, SEPARATORS, NULL); + ret->add(pp.first); + i = pp.second; + i = skip_chars(in, size, i, SEPARATORS); + if(word_eq("]]", in, size, i, ARG_END)) + { + ret->add(new arg("]]")); + i = skip_chars(in, size, i+2, SEPARATORS); + if( !is_in(in[i], ARGLIST_END) ) + throw PARSE_ERROR("Unexpected argument after ']]'", i); + break; + } + if(i>=size) + throw PARSE_ERROR( "Expecting ']]'", i); + } + } + else if(is_in(in[i], SPECIAL_TOKENS) && !word_eq("&>", in, size, i)) { if(hard_error) throw PARSE_ERROR( strf("Unexpected token '%c'", in[i]) , i); else return std::make_pair(ret, i); } - while(i') && in[i+1] == '(' ) // bash specific <() + while(iadd(new arg(new procsub_subarg(is_output, ps.first))); - i=ps.second; - } - else if(redirs!=nullptr) - { - auto pr = parse_redirect(in, size, i); - if(pr.first != nullptr) + if(i+1 < size && (in[i] == '<' || in[i] == '>') && in[i+1] == '(' ) // bash specific <() { - redirs->push_back(pr.first); - i=pr.second; + bool is_output = in[i] == '>'; + i+=2; + if(ret == nullptr) + ret = new arglist; + auto ps = parse_subshell(in, size, i); + ret->add(new arg(new procsub_subarg(is_output, ps.first))); + i=ps.second; + } + else if(redirs!=nullptr) + { + auto pr = parse_redirect(in, size, i); + if(pr.first != nullptr) + { + redirs->push_back(pr.first); + i=pr.second; + } + else + goto argparse; } else - goto argparse; + { + argparse: + if(ret == nullptr) + ret = new arglist; + auto pp=parse_arg(in, size, i); + ret->add(pp.first); + i = pp.second; + } + i = skip_chars(in, size, i, SPACES); + if(word_eq("&>", in, size, i)) + continue; // &> has to be managed in redirects + if(word_eq("|&", in, size, i)) + throw PARSE_ERROR("Unsupported '|&', use '2>&1 |' instead", i); + if(i>=size) + return std::make_pair(ret, i); + if( is_in(in[i], SPECIAL_TOKENS) ) + return std::make_pair(ret, i); } - else - { - argparse: - if(ret == nullptr) - ret = new arglist; - auto pp=parse_arg(in, size, i); - ret->add(pp.first); - i = pp.second; - } - i = skip_chars(in, size, i, SPACES); - if(word_eq("&>", in, size, i)) - continue; // &> has to be managed in redirects - if(word_eq("|&", in, size, i)) - throw PARSE_ERROR("Unsupported '|&', use '2>&1 |' instead", i); - if(i>=size) - return std::make_pair(ret, i); - if( is_in(in[i], SPECIAL_TOKENS) ) - return std::make_pair(ret, i); + } + + #ifndef NO_PARSE_CATCH } catch(ztd::format_error& e) @@ -1437,7 +1468,7 @@ std::pair parse_block(const char* in, uint32_t size, uint32_t else if( word == "function" ) // bash style function { if(!g_bash) - throw PARSE_ERROR("bash specific: 'function'. Use --debashify to remove bashisms", i); + throw PARSE_ERROR("bash specific: 'function'", i); auto wp2=get_word(in, size, skip_unread(in, size, wp.second), VARNAME_END); if(!valid_name(wp2.first)) throw PARSE_ERROR( strf("Bad function name: '%s'", word.c_str()), start ); diff --git a/src/struc_helper.cpp b/src/struc_helper.cpp index 60d7e8f..83a0025 100644 --- a/src/struc_helper.cpp +++ b/src/struc_helper.cpp @@ -14,12 +14,21 @@ arg* make_arg(std::string const& in) cmd* make_cmd(std::vector const& args) { - cmd* ret = new cmd(); - ret->args = new arglist(); + cmd* ret = new cmd; + ret->args = new arglist; for(auto it: args) - { ret->args->add(new arg(it)); - } + + return ret; +} + +cmd* make_cmd(std::vector const& args) +{ + cmd* ret = new cmd; + ret->args = new arglist; + for(auto it: args) + ret->args->add(it); + return ret; } @@ -28,11 +37,30 @@ cmd* make_cmd(std::string const& in) return parse_cmd(in.c_str(), in.size(), 0).first; } +pipeline* make_pipeline(std::vector const& bls) +{ + pipeline* ret = new pipeline; + for(auto it: bls) + ret->add(it); + + return ret; +} + +pipeline* make_pipeline(std::string const& in) +{ + return parse_pipeline(in.c_str(), in.size(), 0).first; +} + condlist* make_condlist(std::string const& in) { return parse_condlist(in.c_str(), in.size(), 0).first; } +list* make_list(std::string const& in) +{ + return parse_list_until(in.c_str(), in.size(), 0, 0).first; +} + // modifiers void force_quotes(arg* in) @@ -42,13 +70,38 @@ void force_quotes(arg* in) if(!in->sa[i]->quoted && (in->sa[i]->type == _obj::subarg_variable || in->sa[i]->type == _obj::subarg_manipulation || in->sa[i]->type == _obj::subarg_subshell) ) { in->sa[i]->quoted=true; + in->insert(i+1, new string_subarg("\"")); in->insert(i, new string_subarg("\"")); i+=2; - in->insert(i, new string_subarg("\"")); } } } +void add_quotes(arg* in) +{ + for(uint32_t i=0; i < in->sa.size() ; i++) + in->sa[i]->quoted=true; + + in->insert(0, new string_subarg("\"")); + in->add(new string_subarg("\"")); +} + +// ** TESTERS ** // + +bool arg_has_char(char c, arg* in) +{ + for(auto it: in->sa) + { + if(it->type == _obj::subarg_string) + { + string_subarg* t = dynamic_cast(it); + if(t->val.find(c) != std::string::npos) + return true; + } + } + return false; +} + // ** CLASS EXTENSIONS ** // /// GETTERS ///