Blame src/complete.cc

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