|
Packit |
8f70b4 |
/*
|
|
Packit |
8f70b4 |
* lftp - file transfer program
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8f70b4 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8f70b4 |
* the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
8f70b4 |
* (at your option) any later version.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8f70b4 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8f70b4 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8f70b4 |
* GNU General Public License for more details.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
8f70b4 |
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
8f70b4 |
*/
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include <config.h>
|
|
Packit |
8f70b4 |
#include <fcntl.h>
|
|
Packit |
8f70b4 |
#include "CmdExec.h"
|
|
Packit |
8f70b4 |
#include "alias.h"
|
|
Packit |
8f70b4 |
#include "xstring.h"
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool CmdExec::quotable(char ch,char in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!ch)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
if(ch=='\\' || ch=='!' || ch==in_quotes)
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
if(in_quotes)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
if(strchr("\"' \t>|;&",ch))
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
CmdExec::parse_result CmdExec::parse_one_cmd()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char in_quotes;
|
|
Packit |
8f70b4 |
const char *line=cmd_buf.Get();
|
|
Packit |
8f70b4 |
const char *line_begin=line;
|
|
Packit |
8f70b4 |
static xstring nextarg;
|
|
Packit |
8f70b4 |
const char *alias=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(args)
|
|
Packit |
8f70b4 |
args->Empty();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
args=new ArgV;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
output=0;
|
|
Packit |
8f70b4 |
char redir_type=0;
|
|
Packit |
8f70b4 |
background=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!*line)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// empty command
|
|
Packit |
8f70b4 |
return PARSE_OK;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(line[0]=='&' && line[1]=='&')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
condition=COND_AND;
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(line[0]=='|' && line[1]=='|')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
condition=COND_OR;
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
condition=COND_ANY;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// loop for all arguments
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// skip leading whitespace
|
|
Packit |
8f70b4 |
while(is_space(*line))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(line[0]=='\\' && line[1]=='\n')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
continue; // and continue skipping space
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(line[0]=='\r' && line[1]=='\n')
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line=='\n'
|
|
Packit |
8f70b4 |
|| *line=='|' || *line=='>' || *line==';' || *line=='&')
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
nextarg.truncate(0);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(args->count()==0 && *line=='#')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// comment -- skip and return
|
|
Packit |
8f70b4 |
while(*line!='\n' && *line)
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
if(*line=='\n')
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
return PARSE_OK;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(args->count()==0 && *line=='!')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// shell command -- it ends only with '\n'
|
|
Packit |
8f70b4 |
args->Append("!");
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
while(is_space(*line))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
while(*line!='\n' && *line)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line=='\\' && line[1]=='\n')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
nextarg.append(*line++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
if(*line=='\n')
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
if(nextarg.length()>0)
|
|
Packit |
8f70b4 |
args->Append(nextarg);
|
|
Packit |
8f70b4 |
return PARSE_OK;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(args->count()==0 && *line=='(')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
args->Append("(");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int level=1;
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
if(*line=='\\' && line[1] && (strchr("\"\\",line[1])
|
|
Packit |
8f70b4 |
|| (level==1 && line[1]==')')))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
nextarg.append(*line++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line==')')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(--level==0)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(*line=='(')
|
|
Packit |
8f70b4 |
level++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(in_quotes && *line==in_quotes)
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
else if(!in_quotes && is_quote(*line))
|
|
Packit |
8f70b4 |
in_quotes=*line;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
nextarg.append(*line++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
args->Append(nextarg);
|
|
Packit |
8f70b4 |
line++; // skip )
|
|
Packit |
8f70b4 |
while(is_space(*line))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
goto cmd_end;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(args->count()==0 && *line=='?')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
args->Append("?");
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// loop for one argument
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line=='\\' && line[1]=='\n')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(line[0]=='\r' && line[1]=='\n')
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
if(*line=='\\' && quotable(line[1],in_quotes))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
if(*line=='\n'
|
|
Packit |
8f70b4 |
|| (!in_quotes && (is_space(*line)
|
|
Packit |
8f70b4 |
|| *line=='>' || *line=='|' || *line==';' || *line=='&')))
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(!in_quotes && is_quote(*line))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=*line;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(in_quotes && *line==in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
nextarg.append(*line++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN; // normal commands finish with \n or ;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// check if the first arg is an alias, expand it accordingly.
|
|
Packit |
8f70b4 |
if(args->count()==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
alias=Alias::Find(nextarg);
|
|
Packit |
8f70b4 |
if(alias)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int alias_len=strlen(alias);
|
|
Packit |
8f70b4 |
/* Check if the previous alias ends before the end of new one.
|
|
Packit |
8f70b4 |
* So the new alias does not expand entirely from previous
|
|
Packit |
8f70b4 |
* aliases and we can repeat the expansion from the very beginning. */
|
|
Packit |
8f70b4 |
if(alias_field<(int)(line-line_begin))
|
|
Packit |
8f70b4 |
free_used_aliases();
|
|
Packit |
8f70b4 |
if(!TouchedAlias::IsTouched(alias,used_aliases))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
used_aliases=new TouchedAlias(alias,used_aliases);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
cmd_buf.Prepend(alias);
|
|
Packit |
8f70b4 |
alias_field+=alias_len;
|
|
Packit |
8f70b4 |
line=line_begin=cmd_buf.Get();
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
args->Append(nextarg);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if((line[0]=='&' && line[1]=='&')
|
|
Packit |
8f70b4 |
|| (line[0]=='|' && line[1]=='|'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
return PARSE_OK;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line=='>' || *line=='|')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
redir_type=*line;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
if(*line=='>')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// '>>' means append
|
|
Packit |
8f70b4 |
redir_type='+';
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// skip leading whitespace
|
|
Packit |
8f70b4 |
while(is_space(*line))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line=='\n' || *line==';' || *line=='&')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(redir_type=='|')
|
|
Packit |
8f70b4 |
eprintf(_("parse: missing filter command\n"));
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
eprintf(_("parse: missing redirection filename\n"));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(*line==';' || *line=='&' || *line=='\n')
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
return PARSE_ERR;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
nextarg.truncate(0);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line=='\\' && line[1]=='\n')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
line+=2;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(*line=='\\' && quotable(line[1],in_quotes))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line==0)
|
|
Packit |
8f70b4 |
return PARSE_AGAIN;
|
|
Packit |
8f70b4 |
// filename can end with a space, filter command can't.
|
|
Packit |
8f70b4 |
if(*line=='\n' || (!in_quotes
|
|
Packit |
8f70b4 |
&& ((redir_type!='|' && is_space(*line))
|
|
Packit |
8f70b4 |
|| *line==';' || *line=='&')))
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(!in_quotes && is_quote(*line))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=*line;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(in_quotes && *line==in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=0;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
nextarg.append(*line++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
// skip spaces
|
|
Packit |
8f70b4 |
while(is_space(*line))
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
cmd_end:
|
|
Packit |
8f70b4 |
if((line[0]=='&' && line[1]=='&')
|
|
Packit |
8f70b4 |
|| (line[0]=='|' && line[1]=='|'))
|
|
Packit |
8f70b4 |
;
|
|
Packit |
8f70b4 |
else if(*line==';' || *line=='&' || *line=='\n')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*line=='&')
|
|
Packit |
8f70b4 |
background=1;
|
|
Packit |
8f70b4 |
line++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
skip_cmd(line-line_begin);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
switch(redir_type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case('|'):
|
|
Packit |
8f70b4 |
output=new OutputFilter(nextarg);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case('>'):
|
|
Packit |
8f70b4 |
output=new FileStream(nextarg,O_WRONLY|O_TRUNC|O_CREAT);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case('+'):
|
|
Packit |
8f70b4 |
output=new FileStream(nextarg,O_WRONLY|O_APPEND|O_CREAT);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return PARSE_OK;
|
|
Packit |
8f70b4 |
}
|