|
Packit |
8f70b4 |
/*
|
|
Packit |
8f70b4 |
* lftp - file transfer program
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* Copyright (c) 1996-2017 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 |
|
|
Packit |
8f70b4 |
#include "trio.h"
|
|
Packit |
8f70b4 |
#include <sys/types.h>
|
|
Packit |
8f70b4 |
#include <sys/stat.h>
|
|
Packit |
8f70b4 |
#include <time.h>
|
|
Packit |
8f70b4 |
#include <unistd.h>
|
|
Packit |
8f70b4 |
#include <sys/time.h>
|
|
Packit |
8f70b4 |
#include <assert.h>
|
|
Packit |
8f70b4 |
#include "xmalloc.h"
|
|
Packit |
8f70b4 |
#include "FileAccess.h"
|
|
Packit |
8f70b4 |
#include "CmdExec.h"
|
|
Packit |
8f70b4 |
#include "alias.h"
|
|
Packit |
8f70b4 |
#include "SignalHook.h"
|
|
Packit |
8f70b4 |
#include "CharReader.h"
|
|
Packit |
8f70b4 |
#include "LsCache.h"
|
|
Packit |
8f70b4 |
#include "complete.h"
|
|
Packit |
8f70b4 |
#include "lftp_rl.h"
|
|
Packit |
8f70b4 |
#include "url.h"
|
|
Packit |
8f70b4 |
#include "ResMgr.h"
|
|
Packit |
8f70b4 |
#include "ColumnOutput.h"
|
|
Packit |
8f70b4 |
#include "FileSetOutput.h"
|
|
Packit |
8f70b4 |
#include "OutputJob.h"
|
|
Packit |
8f70b4 |
#include "misc.h"
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
CDECL_BEGIN
|
|
Packit |
8f70b4 |
#include <glob.h>
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#define USE_VARARGS 1
|
|
Packit |
8f70b4 |
#define PREFER_STDARG 1
|
|
Packit |
8f70b4 |
#include <readline/readline.h>
|
|
Packit |
8f70b4 |
CDECL_END
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#ifndef GLOB_PERIOD
|
|
Packit |
8f70b4 |
# define GLOB_PERIOD 0
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static char *bash_dequote_filename (const char *text, int quote_char);
|
|
Packit |
8f70b4 |
static int lftp_char_is_quoted(const char *string,int eindex);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static int len; // lenght of the word to complete
|
|
Packit |
8f70b4 |
static int cindex; // index in completion array
|
|
Packit |
8f70b4 |
static const char *const *array;
|
|
Packit |
8f70b4 |
static char **vars=NULL;
|
|
Packit |
8f70b4 |
static FileSet *glob_res=NULL;
|
|
Packit |
8f70b4 |
static bool inhibit_tilde;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static bool shell_cmd;
|
|
Packit |
8f70b4 |
static bool quote_glob;
|
|
Packit |
8f70b4 |
static bool quote_glob_basename;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char *command_generator(const char *text,int state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *name;
|
|
Packit |
8f70b4 |
static const Alias *alias;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If this is a new word to complete, initialize now. */
|
|
Packit |
8f70b4 |
if(!state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
cindex=0;
|
|
Packit |
8f70b4 |
alias=Alias::base;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Return the next name which partially matches from the command list. */
|
|
Packit |
8f70b4 |
while ((name=CmdExec::CmdByIndex(cindex))!=0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
cindex++;
|
|
Packit |
8f70b4 |
if(name[0]=='.' && len==0)
|
|
Packit |
8f70b4 |
continue; // skip hidden commands
|
|
Packit |
8f70b4 |
if(strncasecmp(name,text,len)==0)
|
|
Packit |
8f70b4 |
return(xstrdup(name));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while(alias)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const Alias *tmp=alias;
|
|
Packit |
8f70b4 |
alias=alias->next;
|
|
Packit |
8f70b4 |
if(strncasecmp(tmp->alias,text,len)==0)
|
|
Packit |
8f70b4 |
return(xstrdup(tmp->alias));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If no names matched, then return NULL. */
|
|
Packit |
8f70b4 |
return(NULL);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static char *file_generator(const char *text,int state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
/* If this is a new word to complete, initialize now. */
|
|
Packit |
8f70b4 |
if(!state)
|
|
Packit |
8f70b4 |
cindex=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(glob_res==NULL)
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while((*glob_res)[cindex])
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *name=(*glob_res)[cindex++]->name;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!name[0])
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
return(xstrdup(name));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static bool bookmark_prepend_bm;
|
|
Packit |
8f70b4 |
static char *bookmark_generator(const char *text,int s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
static int state;
|
|
Packit |
8f70b4 |
const char *t;
|
|
Packit |
8f70b4 |
if(!s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
state=0;
|
|
Packit |
8f70b4 |
lftp_bookmarks.Rewind();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case 0:
|
|
Packit |
8f70b4 |
t=lftp_bookmarks.CurrentKey();
|
|
Packit |
8f70b4 |
if(!t)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
state=1;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!lftp_bookmarks.Next())
|
|
Packit |
8f70b4 |
state=1;
|
|
Packit |
8f70b4 |
if(bookmark_prepend_bm)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xstring& e=xstring::get_tmp("bm:");
|
|
Packit |
8f70b4 |
t=e.append_url_encoded(t,URL_HOST_UNSAFE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(strncmp(t,text,len)==0)
|
|
Packit |
8f70b4 |
return xstrdup(t);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case 1:
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static char *array_generator(const char *text,int state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *name;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If this is a new word to complete, initialize now. */
|
|
Packit |
8f70b4 |
if(!state)
|
|
Packit |
8f70b4 |
cindex=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(array==NULL)
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while((name=array[cindex++])!=NULL)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!name[0])
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(strncmp(name,text,len)==0)
|
|
Packit |
8f70b4 |
return(xstrdup(name));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
array=NULL;
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static char *vars_generator(const char *text,int state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *name;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If this is a new word to complete, initialize now. */
|
|
Packit |
8f70b4 |
if(!state)
|
|
Packit |
8f70b4 |
cindex=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(vars==NULL)
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while((name=vars[cindex++])!=NULL)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!name[0])
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
char *text0=string_alloca(len+2);
|
|
Packit |
8f70b4 |
strncpy(text0,text,len);
|
|
Packit |
8f70b4 |
text0[len]=0;
|
|
Packit |
8f70b4 |
if(ResMgr::VarNameCmp(name,text0)!=ResMgr::DIFFERENT)
|
|
Packit |
8f70b4 |
return(xstrdup(name));
|
|
Packit |
8f70b4 |
if(strchr(text0,':')==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
strcat(text0,":");
|
|
Packit |
8f70b4 |
if(ResMgr::VarNameCmp(name,text0)!=ResMgr::DIFFERENT)
|
|
Packit |
8f70b4 |
return(xstrdup(name));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static bool not_dir(char *f)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
struct stat st;
|
|
Packit |
8f70b4 |
f=tilde_expand(f);
|
|
Packit |
8f70b4 |
bool res=(stat(f,&st)==-1 || !S_ISDIR(st.st_mode));
|
|
Packit |
8f70b4 |
free(f);
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int ignore_non_dirs(char **matches)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// filter out non-dirs.
|
|
Packit |
8f70b4 |
int out=1;
|
|
Packit |
8f70b4 |
for(int i=1; matches[i]; i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!not_dir(matches[i]))
|
|
Packit |
8f70b4 |
matches[out++]=matches[i];
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
free(matches[i]);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
matches[out]=0;
|
|
Packit |
8f70b4 |
if(out==1)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// we have only the LCD prefix. Handle it carefully.
|
|
Packit |
8f70b4 |
char *f=matches[0];
|
|
Packit |
8f70b4 |
int len=strlen(f);
|
|
Packit |
8f70b4 |
if((len>2 && f[len-1]=='/') // all files, no dirs.
|
|
Packit |
8f70b4 |
|| not_dir(f)) // or single non dir.
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// all files, no dirs.
|
|
Packit |
8f70b4 |
free(f);
|
|
Packit |
8f70b4 |
matches[0]=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static const char *find_word(const char *p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
while(CmdExec::is_space(*p))
|
|
Packit |
8f70b4 |
p++;
|
|
Packit |
8f70b4 |
return p;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
static bool copy_word_skip(const char **p_in,char *buf,int n)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *&p=*p_in;
|
|
Packit |
8f70b4 |
char in_quotes=0;
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!*p)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(*p==in_quotes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=0,p++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(*p=='\\' && CmdExec::quotable(p[1],in_quotes))
|
|
Packit |
8f70b4 |
p++;
|
|
Packit |
8f70b4 |
if(buf && n>0)
|
|
Packit |
8f70b4 |
*buf++=*p,n--;
|
|
Packit |
8f70b4 |
p++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(CmdExec::is_space(*p))
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(*p=='\\' && CmdExec::quotable(p[1],in_quotes))
|
|
Packit |
8f70b4 |
p++;
|
|
Packit |
8f70b4 |
else if(CmdExec::is_quote(*p))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
in_quotes=*p++;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(buf && n>0)
|
|
Packit |
8f70b4 |
*buf++=*p,n--;
|
|
Packit |
8f70b4 |
p++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(n>0 && buf)
|
|
Packit |
8f70b4 |
*buf=0;
|
|
Packit |
8f70b4 |
return n>0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
// returns false when buffer overflows
|
|
Packit |
8f70b4 |
static bool copy_word(char *buf,const char *p,int n)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return copy_word_skip(&p,buf,n);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
static const char *skip_word(const char *p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
copy_word_skip(&p,0,0);
|
|
Packit |
8f70b4 |
return p;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
enum completion_type
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LOCAL, LOCAL_DIR, REMOTE_FILE, REMOTE_DIR, BOOKMARK, COMMAND,
|
|
Packit |
8f70b4 |
STRING_ARRAY, VARIABLE, NO_COMPLETION
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// cmd: ptr to command line being completed
|
|
Packit |
8f70b4 |
// start: location of the word being completed
|
|
Packit |
8f70b4 |
static completion_type cmd_completion_type(const char *cmd,int start)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *w=0;
|
|
Packit |
8f70b4 |
char buf[20]; // no commands longer
|
|
Packit |
8f70b4 |
TouchedAlias *used_aliases=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// try to guess whether the completion word is remote
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
w=find_word(cmd);
|
|
Packit |
8f70b4 |
if(w-cmd == start) // first word is command
|
|
Packit |
8f70b4 |
return COMMAND;
|
|
Packit |
8f70b4 |
if(w[0]=='!')
|
|
Packit |
8f70b4 |
shell_cmd=true;
|
|
Packit |
8f70b4 |
if(w[0]=='#')
|
|
Packit |
8f70b4 |
return NO_COMPLETION;
|
|
Packit |
8f70b4 |
if(w[0]=='!')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
shell_cmd=quote_glob=true;
|
|
Packit |
8f70b4 |
return LOCAL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(w[0]=='?') // help
|
|
Packit |
8f70b4 |
return COMMAND;
|
|
Packit |
8f70b4 |
if(w[0]=='(')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
start-=(w+1-cmd);
|
|
Packit |
8f70b4 |
cmd=w+1;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!copy_word(buf,w,sizeof(buf))
|
|
Packit |
8f70b4 |
|| buf[0]==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
TouchedAlias::FreeChain(used_aliases);
|
|
Packit |
8f70b4 |
return LOCAL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *alias=Alias::Find(buf);
|
|
Packit |
8f70b4 |
if(alias && !TouchedAlias::IsTouched(alias,used_aliases))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
used_aliases=new TouchedAlias(alias,used_aliases);
|
|
Packit |
8f70b4 |
int buf_len=strlen(buf);
|
|
Packit |
8f70b4 |
const char *cmd1=xstring::cat(alias,w+buf_len,NULL);
|
|
Packit |
8f70b4 |
cmd=alloca_strdup(cmd1);
|
|
Packit |
8f70b4 |
start=start-buf_len+strlen(alias);
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *full=CmdExec::GetFullCommandName(buf);
|
|
Packit |
8f70b4 |
if(full!=buf)
|
|
Packit |
8f70b4 |
strcpy(buf,full);
|
|
Packit |
8f70b4 |
TouchedAlias::FreeChain(used_aliases);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for(const char *p=cmd+start; p>cmd; )
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
p--;
|
|
Packit |
8f70b4 |
if((*p=='>' || *p=='|')
|
|
Packit |
8f70b4 |
&& !lftp_char_is_quoted(cmd,p-cmd))
|
|
Packit |
8f70b4 |
return LOCAL;
|
|
Packit |
8f70b4 |
if(!CmdExec::is_space(*p))
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"shell"))
|
|
Packit |
8f70b4 |
shell_cmd=quote_glob=true;
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"glob")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mget")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mput")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mrm"))
|
|
Packit |
8f70b4 |
quote_glob=true;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"cls"))
|
|
Packit |
8f70b4 |
quote_glob_basename=true;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"cd")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mkdir"))
|
|
Packit |
8f70b4 |
return REMOTE_DIR; /* append slash automatically */
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"cat")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"ls")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"cls")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"du")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"edit")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"find")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"recls")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"rels")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"more")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mrm")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mv")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mmv")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"nlist")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"rm")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"rmdir")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"bzcat")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"bzmore")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"zcat")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"zmore"))
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"open")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"lftp"))
|
|
Packit |
8f70b4 |
return BOOKMARK;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"help"))
|
|
Packit |
8f70b4 |
return COMMAND;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool was_o=false;
|
|
Packit |
8f70b4 |
bool was_N=false;
|
|
Packit |
8f70b4 |
bool was_O=false;
|
|
Packit |
8f70b4 |
bool have_R=false;
|
|
Packit |
8f70b4 |
bool have_N=false;
|
|
Packit |
8f70b4 |
bool second=false;
|
|
Packit |
8f70b4 |
int second_start=-1;
|
|
Packit |
8f70b4 |
for(int i=start; i>4; i--)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!CmdExec::is_space(rl_line_buffer[i-1]))
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(!strncmp(rl_line_buffer+i-3,"-o",2) && CmdExec::is_space(rl_line_buffer[i-4]))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
was_o=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strncmp(rl_line_buffer+i-3,"-N",2) && CmdExec::is_space(rl_line_buffer[i-4]))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
was_N=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(i-14 >= 0 && !strncmp(rl_line_buffer+i-13, "--newer-than",12) && CmdExec::is_space(rl_line_buffer[i-14]))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
was_N=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strncmp(rl_line_buffer+i-3,"-O",2) && CmdExec::is_space(rl_line_buffer[i-4]))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
was_O=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
w=skip_word(find_word(cmd));
|
|
Packit |
8f70b4 |
if(*w)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
w=find_word(w);
|
|
Packit |
8f70b4 |
second_start=w-cmd;
|
|
Packit |
8f70b4 |
if(w-cmd==start) // we complete second word
|
|
Packit |
8f70b4 |
second=true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(re_match(cmd," -[a-zA-Z]*R[a-zA-Z]* ",0))
|
|
Packit |
8f70b4 |
have_R=true;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int arg=0;
|
|
Packit |
8f70b4 |
bool opt_stop=false;
|
|
Packit |
8f70b4 |
for(w=cmd; *w; )
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
w=find_word(w);
|
|
Packit |
8f70b4 |
if(w-cmd==start || w-cmd+CmdExec::is_quote(*w)==start)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(w[0]!='-' || opt_stop)
|
|
Packit |
8f70b4 |
arg++;
|
|
Packit |
8f70b4 |
else if(re_match(w,"^-[a-zA-Z]*N ",0))
|
|
Packit |
8f70b4 |
have_N=true;
|
|
Packit |
8f70b4 |
if(!strncmp(w,"-- ",3))
|
|
Packit |
8f70b4 |
opt_stop=true;
|
|
Packit |
8f70b4 |
w=skip_word(w);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"get")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"pget")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"get1"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(was_O)
|
|
Packit |
8f70b4 |
return LOCAL_DIR;
|
|
Packit |
8f70b4 |
if(!was_o)
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"mget"))
|
|
Packit |
8f70b4 |
if(!was_O)
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"put"))
|
|
Packit |
8f70b4 |
if(was_o)
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"put")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"mput"))
|
|
Packit |
8f70b4 |
if(was_O)
|
|
Packit |
8f70b4 |
return REMOTE_DIR;
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"mirror"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(was_N)
|
|
Packit |
8f70b4 |
return LOCAL;
|
|
Packit |
8f70b4 |
if(have_N)
|
|
Packit |
8f70b4 |
arg--;
|
|
Packit |
8f70b4 |
if(have_R ^ (arg!=1))
|
|
Packit |
8f70b4 |
return LOCAL_DIR;
|
|
Packit |
8f70b4 |
if(arg>2)
|
|
Packit |
8f70b4 |
return NO_COMPLETION;
|
|
Packit |
8f70b4 |
return REMOTE_DIR;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"bookmark"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(second)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
array=bookmark_subcmd;
|
|
Packit |
8f70b4 |
return STRING_ARRAY;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return BOOKMARK;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"chmod"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(second)
|
|
Packit |
8f70b4 |
return NO_COMPLETION;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"glob")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"command")
|
|
Packit |
8f70b4 |
|| !strcmp(buf,"queue"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(second)
|
|
Packit |
8f70b4 |
return COMMAND;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// FIXME: infinite alias expansion is possible.
|
|
Packit |
8f70b4 |
if(second_start>0 && start>second_start && (int)strlen(cmd)>second_start)
|
|
Packit |
8f70b4 |
return cmd_completion_type(cmd+second_start,start-second_start);
|
|
Packit |
8f70b4 |
return REMOTE_FILE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"cache"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(second)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
array=cache_subcmd;
|
|
Packit |
8f70b4 |
return STRING_ARRAY;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return NO_COMPLETION;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"set"))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(second)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!vars)
|
|
Packit |
8f70b4 |
vars=ResMgr::Generator();
|
|
Packit |
8f70b4 |
return VARIABLE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return NO_COMPLETION;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!strcmp(buf,"lcd"))
|
|
Packit |
8f70b4 |
return LOCAL_DIR;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return LOCAL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static void glob_quote(char *out,const char *in,int len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
while(len>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(*in)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case '*': case '?': case '[': case ']':
|
|
Packit |
8f70b4 |
if(!quote_glob)
|
|
Packit |
8f70b4 |
*out++='\\';
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case '\\':
|
|
Packit |
8f70b4 |
switch(in[1])
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case '*': case '?': case '[': case ']': case '\\':
|
|
Packit |
8f70b4 |
*out++=*in++; // copy the backslash.
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
default:
|
|
Packit |
8f70b4 |
in++; // skip it.
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*out++=*in;
|
|
Packit |
8f70b4 |
in++;
|
|
Packit |
8f70b4 |
len--;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*out=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
CmdExec *completion_shell;
|
|
Packit |
8f70b4 |
int remote_completion=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static bool force_remote=false;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Attempt to complete on the contents of TEXT. START and END show the
|
|
Packit |
8f70b4 |
region of TEXT that contains the word to complete. We can use the
|
|
Packit |
8f70b4 |
entire line in case we want to do some simple parsing. Return the
|
|
Packit |
8f70b4 |
array of matches, or NULL if there aren't any. */
|
|
Packit |
8f70b4 |
static char **lftp_completion (const char *text,int start,int end)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileSetOutput fso;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
completion_shell->RestoreCWD();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(start>end) // workaround for a bug in readline
|
|
Packit |
8f70b4 |
start=end;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
GlobURL *rg=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
rl_completion_append_character=' ';
|
|
Packit |
8f70b4 |
rl_ignore_some_completions_function=0;
|
|
Packit |
8f70b4 |
shell_cmd=false;
|
|
Packit |
8f70b4 |
quote_glob=false;
|
|
Packit |
8f70b4 |
quote_glob_basename=false;
|
|
Packit |
8f70b4 |
inhibit_tilde=false;
|
|
Packit |
8f70b4 |
delete glob_res;
|
|
Packit |
8f70b4 |
glob_res=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
completion_type type=cmd_completion_type(rl_line_buffer,start);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
len=end-start;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char *(*generator)(const char *text,int state) = 0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
switch(type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case NO_COMPLETION:
|
|
Packit |
8f70b4 |
rl_attempted_completion_over = 1;
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
case COMMAND:
|
|
Packit |
8f70b4 |
generator = command_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case BOOKMARK:
|
|
Packit |
8f70b4 |
bookmark_prepend_bm=false;
|
|
Packit |
8f70b4 |
generator = bookmark_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case STRING_ARRAY:
|
|
Packit |
8f70b4 |
generator = array_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case VARIABLE:
|
|
Packit |
8f70b4 |
generator = vars_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char *pat;
|
|
Packit |
8f70b4 |
case LOCAL:
|
|
Packit |
8f70b4 |
case LOCAL_DIR: {
|
|
Packit |
8f70b4 |
if(force_remote || (url::is_url(text) && remote_completion))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(type==LOCAL_DIR)
|
|
Packit |
8f70b4 |
type=REMOTE_DIR;
|
|
Packit |
8f70b4 |
goto really_remote;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
really_local:
|
|
Packit |
8f70b4 |
fso.parse_res(ResMgr::Query("cmd:cls-completion-default", 0));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool tilde_expanded=false;
|
|
Packit |
8f70b4 |
const char *home=getenv("HOME");
|
|
Packit |
8f70b4 |
int home_len=xstrlen(home);
|
|
Packit |
8f70b4 |
pat=string_alloca((len+home_len)*2+10);
|
|
Packit |
8f70b4 |
if(len>0 && home_len>0 && text[0]=='~' && (len==1 || text[1]=='/'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
glob_quote(pat,home,home_len);
|
|
Packit |
8f70b4 |
glob_quote(pat+strlen(pat),text+1,len-1);
|
|
Packit |
8f70b4 |
if(len==1)
|
|
Packit |
8f70b4 |
strcat(pat,"/");
|
|
Packit |
8f70b4 |
tilde_expanded=true;
|
|
Packit |
8f70b4 |
inhibit_tilde=false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
glob_quote(pat,text,len);
|
|
Packit |
8f70b4 |
inhibit_tilde=true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* if we want case-insensitive matching, we need to match everything
|
|
Packit |
8f70b4 |
* in the dir and weed it ourselves (let the generator do it), since
|
|
Packit |
8f70b4 |
* glob() has no casefold option */
|
|
Packit |
8f70b4 |
if(fso.patterns_casefold) {
|
|
Packit |
8f70b4 |
rl_variable_bind("completion-ignore-case", "1");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* strip back to the last / */
|
|
Packit |
8f70b4 |
char *sl = strrchr(pat, '/');
|
|
Packit |
8f70b4 |
if(sl) *++sl = 0;
|
|
Packit |
8f70b4 |
else pat[0] = 0;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
rl_variable_bind("completion-ignore-case", "0");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
strcat(pat,"*");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
glob_t pglob;
|
|
Packit |
8f70b4 |
glob(pat,GLOB_PERIOD,NULL,&pglob);
|
|
Packit |
8f70b4 |
glob_res=new FileSet;
|
|
Packit |
8f70b4 |
for(int i=0; i<(int)pglob.gl_pathc; i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *src=pglob.gl_pathv[i];
|
|
Packit |
8f70b4 |
if(tilde_expanded && home_len>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
src+=home_len-1;
|
|
Packit |
8f70b4 |
*src='~';
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!strcmp(basename_ptr(src), ".")) continue;
|
|
Packit |
8f70b4 |
if(!strcmp(basename_ptr(src), "..")) continue;
|
|
Packit |
8f70b4 |
if(type==LOCAL_DIR && not_dir(src)) continue;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FileInfo *f = new FileInfo;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
f->LocalFile(src, false);
|
|
Packit |
8f70b4 |
glob_res->Add(f);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
globfree(&pglob);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
rl_filename_completion_desired=1;
|
|
Packit |
8f70b4 |
generator = file_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case REMOTE_FILE:
|
|
Packit |
8f70b4 |
case REMOTE_DIR: {
|
|
Packit |
8f70b4 |
if(!remote_completion && !force_remote)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(type==REMOTE_DIR)
|
|
Packit |
8f70b4 |
type=LOCAL_DIR;
|
|
Packit |
8f70b4 |
goto really_local;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
really_remote:
|
|
Packit |
8f70b4 |
if(!strncmp(text,"bm:",3) && !strchr(text,'/'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bookmark_prepend_bm=true;
|
|
Packit |
8f70b4 |
generator=bookmark_generator;
|
|
Packit |
8f70b4 |
rl_completion_append_character='/';
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
pat=string_alloca(len*2+10);
|
|
Packit |
8f70b4 |
glob_quote(pat,text,len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(pat[0]=='~' && pat[1]==0)
|
|
Packit |
8f70b4 |
strcat(pat,"/");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(pat[0]=='~' && pat[1]=='/')
|
|
Packit |
8f70b4 |
inhibit_tilde=false;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
inhibit_tilde=true;
|
|
Packit |
8f70b4 |
strcat(pat,"*");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
completion_shell->session->DontSleep();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SignalHook::ResetCount(SIGINT);
|
|
Packit |
8f70b4 |
glob_res=NULL;
|
|
Packit |
8f70b4 |
rg=new GlobURL(completion_shell->session,pat,
|
|
Packit |
8f70b4 |
type==REMOTE_DIR?GlobURL::DIRS_ONLY:GlobURL::ALL);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
rl_save_prompt();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
fso.parse_res(ResMgr::Query("cmd:cls-completion-default", 0));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(rg)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
rg->NoInhibitTilde();
|
|
Packit |
8f70b4 |
if(fso.patterns_casefold) {
|
|
Packit |
8f70b4 |
rl_variable_bind("completion-ignore-case", "1");
|
|
Packit |
8f70b4 |
rg->CaseFold();
|
|
Packit |
8f70b4 |
} else
|
|
Packit |
8f70b4 |
rl_variable_bind("completion-ignore-case", "0");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Timer status_timer;
|
|
Packit |
8f70b4 |
status_timer.SetMilliSeconds(20);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SMTask::Schedule();
|
|
Packit |
8f70b4 |
if(rg->Done())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(SignalHook::GetCount(SIGINT))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SignalHook::ResetCount(SIGINT);
|
|
Packit |
8f70b4 |
rl_attempted_completion_over = 1;
|
|
Packit |
8f70b4 |
delete rg;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
rl_restore_prompt();
|
|
Packit |
8f70b4 |
rl_clear_message();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!fso.quiet)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
/* don't set blank status; if we're operating from cache,
|
|
Packit |
8f70b4 |
* that's all we'll get and it'll look ugly: */
|
|
Packit |
8f70b4 |
const char *ret = rg->Status();
|
|
Packit |
8f70b4 |
if(*ret)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(status_timer.Stopped())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
rl_message ("%s> ", ret);
|
|
Packit |
8f70b4 |
status_timer.SetResource("cmd:status-interval",0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SMTask::Block();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
glob_res=new FileSet(rg->GetResult());
|
|
Packit |
8f70b4 |
glob_res->rewind();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
rl_restore_prompt();
|
|
Packit |
8f70b4 |
rl_clear_message();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(glob_res->get_fnum()==1)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileInfo *info=glob_res->curr();
|
|
Packit |
8f70b4 |
rl_completion_append_character=' ';
|
|
Packit |
8f70b4 |
if(info->defined&info->TYPE && info->filetype==info->DIRECTORY)
|
|
Packit |
8f70b4 |
rl_completion_append_character='/';
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
rl_filename_completion_desired=1;
|
|
Packit |
8f70b4 |
generator = file_generator;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} /* end switch */
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
assert(generator);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char quoted=((lftp_char_is_quoted(rl_line_buffer,start) &&
|
|
Packit |
8f70b4 |
strchr(rl_completer_quote_characters,rl_line_buffer[start-1]))
|
|
Packit |
8f70b4 |
? rl_line_buffer[start-1] : 0);
|
|
Packit |
8f70b4 |
xstring_ca textq(bash_dequote_filename(text, quoted));
|
|
Packit |
8f70b4 |
len=strlen(textq);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char **matches=lftp_rl_completion_matches(textq,generator);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(rg)
|
|
Packit |
8f70b4 |
delete rg;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(vars)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// delete vars?
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!matches)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
rl_attempted_completion_over = 1;
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(type==REMOTE_DIR)
|
|
Packit |
8f70b4 |
rl_completion_append_character='/';
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return matches;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
extern "C" void lftp_line_complete()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
delete glob_res;
|
|
Packit |
8f70b4 |
glob_res=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
enum { COMPLETE_DQUOTE,COMPLETE_SQUOTE,COMPLETE_BSQUOTE };
|
|
Packit |
8f70b4 |
#define completion_quoting_style COMPLETE_BSQUOTE
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* **************************************************************** */
|
|
Packit |
8f70b4 |
/* */
|
|
Packit |
8f70b4 |
/* Functions for quoting strings to be re-read as input */
|
|
Packit |
8f70b4 |
/* */
|
|
Packit |
8f70b4 |
/* **************************************************************** */
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Return a new string which is the single-quoted version of STRING.
|
|
Packit |
8f70b4 |
Used by alias and trap, among others. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
single_quote (char *string)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int c;
|
|
Packit |
8f70b4 |
char *result, *r, *s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
result = (char *)xmalloc (3 + (4 * strlen (string)));
|
|
Packit |
8f70b4 |
r = result;
|
|
Packit |
8f70b4 |
*r++ = '\'';
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for (s = string; s && (c = *s); s++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
*r++ = c;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if (c == '\'')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
*r++ = '\\'; /* insert escaped single quote */
|
|
Packit |
8f70b4 |
*r++ = '\'';
|
|
Packit |
8f70b4 |
*r++ = '\''; /* start new quoted string */
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
*r++ = '\'';
|
|
Packit |
8f70b4 |
*r = '\0';
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return (result);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Quote STRING using double quotes. Return a new string. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
double_quote (char *string)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int c;
|
|
Packit |
8f70b4 |
char *result, *r, *s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
result = (char *)xmalloc (3 + (2 * strlen (string)));
|
|
Packit |
8f70b4 |
r = result;
|
|
Packit |
8f70b4 |
*r++ = '"';
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for (s = string; s && (c = *s); s++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch (c)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case '$':
|
|
Packit |
8f70b4 |
case '`':
|
|
Packit |
8f70b4 |
if(!shell_cmd)
|
|
Packit |
8f70b4 |
goto def;
|
|
Packit |
8f70b4 |
case '"':
|
|
Packit |
8f70b4 |
case '\\':
|
|
Packit |
8f70b4 |
*r++ = '\\';
|
|
Packit |
8f70b4 |
default: def:
|
|
Packit |
8f70b4 |
*r++ = c;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
*r++ = '"';
|
|
Packit |
8f70b4 |
*r = '\0';
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return (result);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Quote special characters in STRING using backslashes. Return a new
|
|
Packit |
8f70b4 |
string. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
backslash_quote (char *string)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int c;
|
|
Packit |
8f70b4 |
char *result, *r, *s;
|
|
Packit |
8f70b4 |
char *bn = basename_ptr(string);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
result = (char*)xmalloc (2 * strlen (string) + 1);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for (r = result, s = string; s && (c = *s); s++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch (c)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case '(': case ')':
|
|
Packit |
8f70b4 |
case '{': case '}': /* reserved words */
|
|
Packit |
8f70b4 |
case '^':
|
|
Packit |
8f70b4 |
case '$': case '`': /* expansion chars */
|
|
Packit |
8f70b4 |
if(!shell_cmd)
|
|
Packit |
8f70b4 |
goto def;
|
|
Packit |
8f70b4 |
case '*': case '[': case '?': case ']': /* globbing chars */
|
|
Packit |
8f70b4 |
if(!shell_cmd && !quote_glob && (!quote_glob_basename || s
|
|
Packit |
8f70b4 |
goto def;
|
|
Packit |
8f70b4 |
/*fallthrough*/
|
|
Packit |
8f70b4 |
case ' ': case '\t': case '\n': /* IFS white space */
|
|
Packit |
8f70b4 |
case '"': case '\'': case '\\': /* quoting chars */
|
|
Packit |
8f70b4 |
case '|': case '&': case ';': /* shell metacharacters */
|
|
Packit |
8f70b4 |
case '<': case '>': case '!':
|
|
Packit |
8f70b4 |
*r++ = '\\';
|
|
Packit |
8f70b4 |
*r++ = c;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case '~': /* tilde expansion */
|
|
Packit |
8f70b4 |
if (s == string && inhibit_tilde)
|
|
Packit |
8f70b4 |
*r++ = '.', *r++ = '/';
|
|
Packit |
8f70b4 |
goto def;
|
|
Packit |
8f70b4 |
case '#': /* comment char */
|
|
Packit |
8f70b4 |
if(!shell_cmd)
|
|
Packit |
8f70b4 |
goto def;
|
|
Packit |
8f70b4 |
if (s == string)
|
|
Packit |
8f70b4 |
*r++ = '\\';
|
|
Packit |
8f70b4 |
/* FALLTHROUGH */
|
|
Packit |
8f70b4 |
default: def:
|
|
Packit |
8f70b4 |
*r++ = c;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
*r = '\0';
|
|
Packit |
8f70b4 |
return (result);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if 0 // no need yet
|
|
Packit |
8f70b4 |
static int
|
|
Packit |
8f70b4 |
contains_shell_metas (char *string)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for (s = string; s && *s; s++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch (*s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case ' ': case '\t': case '\n': /* IFS white space */
|
|
Packit |
8f70b4 |
case '\'': case '"': case '\\': /* quoting chars */
|
|
Packit |
8f70b4 |
case '|': case '&': case ';': /* shell metacharacters */
|
|
Packit |
8f70b4 |
case '(': case ')': case '<': case '>':
|
|
Packit |
8f70b4 |
case '!': case '{': case '}': /* reserved words */
|
|
Packit |
8f70b4 |
case '*': case '[': case '?': case ']': /* globbing chars */
|
|
Packit |
8f70b4 |
case '^':
|
|
Packit |
8f70b4 |
case '$': case '`': /* expansion chars */
|
|
Packit |
8f70b4 |
return (1);
|
|
Packit |
8f70b4 |
case '#':
|
|
Packit |
8f70b4 |
if (s == string) /* comment char */
|
|
Packit |
8f70b4 |
return (1);
|
|
Packit |
8f70b4 |
/* FALLTHROUGH */
|
|
Packit |
8f70b4 |
default:
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return (0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif //0
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Filename quoting for completion. */
|
|
Packit |
8f70b4 |
/* A function to strip quotes that are not protected by backquotes. It
|
|
Packit |
8f70b4 |
allows single quotes to appear within double quotes, and vice versa.
|
|
Packit |
8f70b4 |
It should be smarter. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
bash_dequote_filename (const char *text, int quote_char)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *ret;
|
|
Packit |
8f70b4 |
const char *p;
|
|
Packit |
8f70b4 |
char *r;
|
|
Packit |
8f70b4 |
int l, quoted;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
l = strlen (text);
|
|
Packit |
8f70b4 |
ret = (char*)xmalloc (l + 1);
|
|
Packit |
8f70b4 |
for (quoted = quote_char, p = text, r = ret; p && *p; p++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
/* Allow backslash-quoted characters to pass through unscathed. */
|
|
Packit |
8f70b4 |
if (*p == '\\')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
*r++ = *++p;
|
|
Packit |
8f70b4 |
if (*p == '\0')
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
/* Close quote. */
|
|
Packit |
8f70b4 |
if (quoted && *p == quoted)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
quoted = 0;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
/* Open quote. */
|
|
Packit |
8f70b4 |
if (quoted == 0 && (*p == '\'' || *p == '"'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
quoted = *p;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*r++ = *p;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*r = '\0';
|
|
Packit |
8f70b4 |
return ret;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Quote characters that the readline completion code would treat as
|
|
Packit |
8f70b4 |
word break characters with backslashes. Pass backslash-quoted
|
|
Packit |
8f70b4 |
characters through without examination. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
quote_word_break_chars (char *text)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *ret, *r, *s;
|
|
Packit |
8f70b4 |
int l;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
l = strlen (text);
|
|
Packit |
8f70b4 |
ret = (char*)xmalloc ((2 * l) + 1);
|
|
Packit |
8f70b4 |
for (s = text, r = ret; *s; s++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
/* Pass backslash-quoted characters through, including the backslash. */
|
|
Packit |
8f70b4 |
if (*s == '\\')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
*r++ = '\\';
|
|
Packit |
8f70b4 |
*r++ = *++s;
|
|
Packit |
8f70b4 |
if (*s == '\0')
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
/* OK, we have an unquoted character. Check its presence in
|
|
Packit |
8f70b4 |
rl_completer_word_break_characters. */
|
|
Packit |
8f70b4 |
if (strchr (rl_completer_word_break_characters, *s))
|
|
Packit |
8f70b4 |
*r++ = '\\';
|
|
Packit |
8f70b4 |
*r++ = *s;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*r = '\0';
|
|
Packit |
8f70b4 |
return ret;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Quote a filename using double quotes, single quotes, or backslashes
|
|
Packit |
8f70b4 |
depending on the value of completion_quoting_style. If we're
|
|
Packit |
8f70b4 |
completing using backslashes, we need to quote some additional
|
|
Packit |
8f70b4 |
characters (those that readline treats as word breaks), so we call
|
|
Packit |
8f70b4 |
quote_word_break_chars on the result. */
|
|
Packit |
8f70b4 |
static char *
|
|
Packit |
8f70b4 |
bash_quote_filename (char *s, int rtype, char *qcp)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *rtext, *mtext, *ret;
|
|
Packit |
8f70b4 |
int rlen, cs;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
rtext = (char *)NULL;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If RTYPE == MULT_MATCH, it means that there is
|
|
Packit |
8f70b4 |
more than one match. In this case, we do not add
|
|
Packit |
8f70b4 |
the closing quote or attempt to perform tilde
|
|
Packit |
8f70b4 |
expansion. If RTYPE == SINGLE_MATCH, we try
|
|
Packit |
8f70b4 |
to perform tilde expansion, because single and double
|
|
Packit |
8f70b4 |
quotes inhibit tilde expansion by the shell. */
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
mtext = s;
|
|
Packit |
8f70b4 |
#if 0
|
|
Packit |
8f70b4 |
if (mtext[0] == '~' && rtype == SINGLE_MATCH)
|
|
Packit |
8f70b4 |
mtext = bash_tilde_expand (s);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
cs = completion_quoting_style;
|
|
Packit |
8f70b4 |
/* Might need to modify the default completion style based on *qcp,
|
|
Packit |
8f70b4 |
since it's set to any user-provided opening quote. */
|
|
Packit |
8f70b4 |
if (*qcp == '"')
|
|
Packit |
8f70b4 |
cs = COMPLETE_DQUOTE;
|
|
Packit |
8f70b4 |
else if (*qcp == '\'')
|
|
Packit |
8f70b4 |
cs = COMPLETE_SQUOTE;
|
|
Packit |
8f70b4 |
#if defined (BANG_HISTORY)
|
|
Packit |
8f70b4 |
else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
|
|
Packit |
8f70b4 |
history_expansion_inhibited == 0 && strchr (mtext, '!'))
|
|
Packit |
8f70b4 |
cs = COMPLETE_BSQUOTE;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
|
|
Packit |
8f70b4 |
history_expansion_inhibited == 0 && strchr (mtext, '!'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
cs = COMPLETE_BSQUOTE;
|
|
Packit |
8f70b4 |
*qcp = '\0';
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
switch (cs)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case COMPLETE_DQUOTE:
|
|
Packit |
8f70b4 |
rtext = double_quote (mtext);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case COMPLETE_SQUOTE:
|
|
Packit |
8f70b4 |
rtext = single_quote (mtext);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case COMPLETE_BSQUOTE:
|
|
Packit |
8f70b4 |
rtext = backslash_quote (mtext);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if (mtext != s)
|
|
Packit |
8f70b4 |
free (mtext);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* We may need to quote additional characters: those that readline treats
|
|
Packit |
8f70b4 |
as word breaks that are not quoted by backslash_quote. */
|
|
Packit |
8f70b4 |
if (rtext && cs == COMPLETE_BSQUOTE)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
mtext = quote_word_break_chars (rtext);
|
|
Packit |
8f70b4 |
free (rtext);
|
|
Packit |
8f70b4 |
rtext = mtext;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Leave the opening quote intact. The readline completion code takes
|
|
Packit |
8f70b4 |
care of avoiding doubled opening quotes. */
|
|
Packit |
8f70b4 |
rlen = strlen (rtext);
|
|
Packit |
8f70b4 |
ret = (char*)xmalloc (rlen + 1);
|
|
Packit |
8f70b4 |
strcpy (ret, rtext);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* If there are multiple matches, cut off the closing quote. */
|
|
Packit |
8f70b4 |
if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
|
|
Packit |
8f70b4 |
ret[rlen - 1] = '\0';
|
|
Packit |
8f70b4 |
free (rtext);
|
|
Packit |
8f70b4 |
return ret;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static int skip_quoted(const char *s, int i, char q)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
while(s[i] && s[i]!=q)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(s[i]=='\\' && s[i+1])
|
|
Packit |
8f70b4 |
i++;
|
|
Packit |
8f70b4 |
i++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(s[i])
|
|
Packit |
8f70b4 |
i++;
|
|
Packit |
8f70b4 |
return i;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int lftp_char_is_quoted(const char *string,int eindex)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int i, pass_next;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for (i = pass_next = 0; i <= eindex; i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if (pass_next)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
pass_next = 0;
|
|
Packit |
8f70b4 |
if (i >= eindex)
|
|
Packit |
8f70b4 |
return 1;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if (string[i] == '"' || string[i] == '\'')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char quote = string[i];
|
|
Packit |
8f70b4 |
i = skip_quoted (string, ++i, quote);
|
|
Packit |
8f70b4 |
if (i > eindex)
|
|
Packit |
8f70b4 |
return 1;
|
|
Packit |
8f70b4 |
i--; /* the skip functions increment past the closing quote. */
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if (string[i] == '\\')
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
pass_next = 1;
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return (0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
extern "C" int (*rl_last_func)(int,int);
|
|
Packit |
8f70b4 |
static int lftp_complete_remote(int count,int key)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(rl_last_func == lftp_complete_remote)
|
|
Packit |
8f70b4 |
rl_last_func = rl_complete;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
force_remote = true;
|
|
Packit |
8f70b4 |
int ret=rl_complete(count,key);
|
|
Packit |
8f70b4 |
force_remote = false;
|
|
Packit |
8f70b4 |
return ret;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int lftp_rl_getc(FILE *file)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SignalHook::DoCount(SIGINT);
|
|
Packit |
8f70b4 |
SMTaskRef<CharReader> rr(new CharReader(fileno(file)));
|
|
Packit |
8f70b4 |
CharReader& r=*rr.get_non_const();
|
|
Packit |
8f70b4 |
for(;;)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SMTask::Schedule();
|
|
Packit |
8f70b4 |
int res=r.GetChar();
|
|
Packit |
8f70b4 |
if(res==r.EOFCHAR)
|
|
Packit |
8f70b4 |
return EOF;
|
|
Packit |
8f70b4 |
if(res!=r.NOCHAR)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
lftp_rl_redisplay_maybe();
|
|
Packit |
8f70b4 |
SMTask::Block();
|
|
Packit |
8f70b4 |
if(SignalHook::GetCount(SIGINT)>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(rl_line_buffer && rl_end>0)
|
|
Packit |
8f70b4 |
rl_kill_full_line(0,0);
|
|
Packit |
8f70b4 |
return '\n';
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
extern int lftp_slot(int,int);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
/* Tell the GNU Readline library how to complete. We want to try to complete
|
|
Packit |
8f70b4 |
on command names if this is the first word in the line, or on filenames
|
|
Packit |
8f70b4 |
if not. */
|
|
Packit |
8f70b4 |
void lftp_readline_init ()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
lftp_rl_init(
|
|
Packit |
8f70b4 |
"lftp", // rl_readline_name
|
|
Packit |
8f70b4 |
lftp_completion, // rl_attempted_completion_function
|
|
Packit |
8f70b4 |
lftp_rl_getc, // rl_getc_function
|
|
Packit |
8f70b4 |
"\"'", // rl_completer_quote_characters
|
|
Packit |
8f70b4 |
" \t\n\"'", // rl_completer_word_break_characters
|
|
Packit |
8f70b4 |
" \t\n\\\"'>;|&()*?[]~!",// rl_filename_quote_characters
|
|
Packit |
8f70b4 |
bash_quote_filename, // rl_filename_quoting_function
|
|
Packit |
8f70b4 |
bash_dequote_filename, // rl_filename_dequoting_function
|
|
Packit |
8f70b4 |
lftp_char_is_quoted); // rl_char_is_quoted_p
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
lftp_rl_add_defun("complete-remote",lftp_complete_remote,-1);
|
|
Packit |
8f70b4 |
lftp_rl_bind("Meta-Tab","complete-remote");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
lftp_rl_add_defun("slot-change",lftp_slot,-1);
|
|
Packit |
8f70b4 |
char key[7];
|
|
Packit |
8f70b4 |
strcpy(key,"Meta-N");
|
|
Packit |
8f70b4 |
for(int i=0; i<10; i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
key[5]='0'+i;
|
|
Packit |
8f70b4 |
lftp_rl_bind(key,"slot-change");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
extern "C" void completion_display_list (char **matches, int len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
JobRef<OutputJob> b(new OutputJob((FDStream *) NULL, "completion"));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(glob_res) {
|
|
Packit |
8f70b4 |
/* Our last completion action was of files, and we kept that
|
|
Packit |
8f70b4 |
* data around. Take the files in glob_res which are in matches
|
|
Packit |
8f70b4 |
* and put them in another FileSet. (This is a little wasteful,
|
|
Packit |
8f70b4 |
* since we're going to use it briefly and discard it, but it's
|
|
Packit |
8f70b4 |
* not worth adding temporary-filtering options to FileSet.) */
|
|
Packit |
8f70b4 |
FileSet tmp;
|
|
Packit |
8f70b4 |
for(int i = 1; i <= len; i++) {
|
|
Packit |
8f70b4 |
FileInfo *fi = glob_res->FindByName(matches[i]);
|
|
Packit |
8f70b4 |
assert(fi);
|
|
Packit |
8f70b4 |
tmp.Add(new FileInfo(*fi));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FileSetOutput fso;
|
|
Packit |
8f70b4 |
fso.config(b);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
fso.parse_res(ResMgr::Query("cmd:cls-completion-default", 0));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
fso.print(tmp, b);
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
/* Just pass it through ColumnInfo. */
|
|
Packit |
8f70b4 |
ColumnOutput c;
|
|
Packit |
8f70b4 |
for(int i = 1; i <= len; i++) {
|
|
Packit |
8f70b4 |
c.append();
|
|
Packit |
8f70b4 |
c.add(matches[i], "");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
c.print(b, b->GetWidth(), b->IsTTY());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
b->PutEOF();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while(!b->Done())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SMTask::Schedule();
|
|
Packit |
8f70b4 |
if(SignalHook::GetCount(SIGINT))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SignalHook::ResetCount(SIGINT);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|