improve echo debashifying

This commit is contained in:
zawz 2021-05-28 16:31:17 +02:00
parent 9918bb61ba
commit 8b6a576713
3 changed files with 140 additions and 43 deletions

View file

@ -187,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);
@ -231,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);

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,6 +165,7 @@ 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, {.ignore_numbers=true, .stop_on_argument=true} );
@ -174,52 +174,115 @@ bool debashify_echo(pipeline* pl)
{
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);
}
}
}

View file

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