implement [[ ]] debashify

This commit is contained in:
zawwz 2021-01-20 16:31:31 +01:00
parent 9c2f3c91f9
commit c12ec601f1
6 changed files with 259 additions and 54 deletions

View file

@ -3,13 +3,28 @@
#include "struc.hpp"
// makers
arg* make_arg(std::string const& in);
cmd* make_cmd(std::vector<std::string> const& args);
cmd* make_cmd(std::vector<arg*> const& args);
cmd* make_cmd(std::string const& in);
pipeline* make_pipeline(std::vector<block*> 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

View file

@ -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<class T, typename ... Args>
std::vector<T> make_vector(Args ... args)
{
return std::vector<T>( { args... } );
}
template <class KEY, class VAL>
std::vector<std::pair<KEY, VAL>> sort_by_value(std::map<KEY,VAL> const& in)

View file

@ -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<arg*> 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<arg*>({args[2]}), make_list("true")) );
tc->cases.push_back( std::make_pair(std::vector<arg*>({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<arg*>({ new arg("echo"), args[0] }) );
cmd* grep_arg2 = make_cmd( std::vector<arg*>({ new arg("grep"), new arg("-q"), new arg("--"), args[2] }) );
add_quotes(args[2]);
ret = make_pipeline( std::vector<block*>({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<cmd*>(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 ; i<in->args->size() ; i++)
{
a = in->args->args[i];
if(i >= in->args->size()-1 || a->string() == "&&" || a->string() == "||")
{
pipeline* tpl = gen_bashtest_cmd(std::vector<arg*>(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<pipeline*>(o);
debashify_herestring(t);
debashify_bashtest(t);
} break;
case _obj::block_cmd: {
cmd* t = dynamic_cast<cmd*>(o);
debashify_combined_redirects(t);
debashify_bashtest(t);
debashify_declare(t);
debashify_array_def(t);
} break;

View file

@ -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;

View file

@ -510,7 +510,7 @@ std::pair<redirect*, uint32_t> 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<redirect*, uint32_t> parse_redirect(const char* in, uint32_t size, uin
if(i<size && in[i] == '<')
{
if(!g_bash)
throw PARSE_ERROR("bash specific: '<<<'. Use --debashify to remove bashisms", i);
throw PARSE_ERROR("bash specific: '<<<'", i);
i++;
}
}
@ -544,7 +544,7 @@ std::pair<redirect*, uint32_t> 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<size && in[i] == '>')
i++;
@ -640,55 +640,86 @@ std::pair<arglist*, uint32_t> 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<size)
else
{
if(i+1 < size && (in[i] == '<' || in[i] == '>') && in[i+1] == '(' ) // bash specific <()
while(i<size)
{
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)
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<block*, uint32_t> 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 );

View file

@ -14,12 +14,21 @@ arg* make_arg(std::string const& in)
cmd* make_cmd(std::vector<std::string> 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<arg*> 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<block*> 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<string_subarg*>(it);
if(t->val.find(c) != std::string::npos)
return true;
}
}
return false;
}
// ** CLASS EXTENSIONS ** //
/// GETTERS ///