implement [[ ]] debashify
This commit is contained in:
parent
9c2f3c91f9
commit
c12ec601f1
6 changed files with 259 additions and 54 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
20
src/main.cpp
20
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;
|
||||
|
|
|
|||
111
src/parse.cpp
111
src/parse.cpp
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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 ///
|
||||
|
|
|
|||
Loading…
Reference in a new issue