Blame src/FileGlob.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
#include "trio.h"
Packit Service a2489d
#include "xstring.h"
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <stdlib.h>
Packit Service a2489d
#include <fnmatch.h>
Packit Service a2489d
#include <stddef.h>
Packit Service a2489d
Packit Service a2489d
#include "FileGlob.h"
Packit Service a2489d
#include "misc.h"
Packit Service a2489d
#include "url.h"
Packit Service a2489d
#include "ResMgr.h"
Packit Service a2489d
Packit Service a2489d
ResDecl res_nullglob("cmd:nullglob","yes",ResMgr::BoolValidate,ResMgr::NoClosure);
Packit Service a2489d
Packit Service a2489d
// Glob implementation
Packit Service a2489d
Glob::Glob(FileAccess *s,const char *p)
Packit Service a2489d
   : FileAccessOperation(s), pattern(p)
Packit Service a2489d
{
Packit Service a2489d
   dirs_only=false;
Packit Service a2489d
   files_only=false;
Packit Service a2489d
   match_period=true;
Packit Service a2489d
   inhibit_tilde=true;
Packit Service a2489d
   casefold=false;
Packit Service a2489d
Packit Service a2489d
   if(pattern[0]=='~')
Packit Service a2489d
   {
Packit Service a2489d
      const char *slash=strchr(pattern,'/');
Packit Service a2489d
      if(slash)
Packit Service a2489d
	 inhibit_tilde=HasWildcards(xstring::get_tmp(pattern,slash-pattern));
Packit Service a2489d
      else
Packit Service a2489d
	 inhibit_tilde=HasWildcards(pattern);
Packit Service a2489d
   }
Packit Service a2489d
   if(pattern[0] && !HasWildcards(pattern))
Packit Service a2489d
   {
Packit Service a2489d
      // no need to glob, just unquote
Packit Service a2489d
      char *u=alloca_strdup(pattern);
Packit Service a2489d
      UnquoteWildcards(u);
Packit Service a2489d
      add(new FileInfo(u));
Packit Service a2489d
      done=true;
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Glob::~Glob()
Packit Service a2489d
{
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void Glob::add_force(const FileInfo *info)
Packit Service a2489d
{
Packit Service a2489d
   // insert new file name into list
Packit Service a2489d
   list.Add(new FileInfo(*info));
Packit Service a2489d
}
Packit Service a2489d
void Glob::add(const FileInfo *info)
Packit Service a2489d
{
Packit Service a2489d
   if(info->defined&info->TYPE)
Packit Service a2489d
   {
Packit Service a2489d
      if(dirs_only && info->filetype==info->NORMAL)
Packit Service a2489d
	 return;   // note that symlinks can point to directories,
Packit Service a2489d
		   // so skip normal files only.
Packit Service a2489d
      if(files_only && info->filetype==info->DIRECTORY)
Packit Service a2489d
	 return;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   const char *s=info->name;
Packit Service a2489d
   if(s==0)
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   int flags=FNM_PATHNAME;
Packit Service a2489d
   if(match_period)
Packit Service a2489d
      flags|=FNM_PERIOD;
Packit Service a2489d
Packit Service a2489d
   if(casefold)
Packit Service a2489d
      flags|=FNM_CASEFOLD;
Packit Service a2489d
Packit Service a2489d
   if(pattern[0]!=0
Packit Service a2489d
   && fnmatch(pattern, s, flags)!=0)
Packit Service a2489d
      return; // unmatched
Packit Service a2489d
Packit Service a2489d
   if(s[0]=='~' && inhibit_tilde)
Packit Service a2489d
   {
Packit Service a2489d
      char *new_name=alloca_strdup2(s,2);
Packit Service a2489d
      strcpy(new_name,"./");
Packit Service a2489d
      strcat(new_name,s);
Packit Service a2489d
      FileInfo new_info(*info);
Packit Service a2489d
      new_info.SetName(new_name);
Packit Service a2489d
      add_force(&new_info);
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      add_force(info);
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool Glob::HasWildcards(const char *s)
Packit Service a2489d
{
Packit Service a2489d
   while(*s)
Packit Service a2489d
   {
Packit Service a2489d
      switch(*s)
Packit Service a2489d
      {
Packit Service a2489d
      case '\\':
Packit Service a2489d
	 if(s[1])
Packit Service a2489d
	    s++;
Packit Service a2489d
	 break;
Packit Service a2489d
      case '*':
Packit Service a2489d
      case '[':
Packit Service a2489d
      case ']':
Packit Service a2489d
      case '?':
Packit Service a2489d
	 return true;
Packit Service a2489d
      }
Packit Service a2489d
      s++;
Packit Service a2489d
   }
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void Glob::UnquoteWildcards(char *s)
Packit Service a2489d
{
Packit Service a2489d
   char *store=s;
Packit Service a2489d
   for(;;)
Packit Service a2489d
   {
Packit Service a2489d
      if(*s=='\\')
Packit Service a2489d
      {
Packit Service a2489d
	 if(s[1]=='*'
Packit Service a2489d
	 || s[1]=='['
Packit Service a2489d
	 || s[1]==']'
Packit Service a2489d
	 || s[1]=='?'
Packit Service a2489d
	 || s[1]=='\\')
Packit Service a2489d
	    s++;
Packit Service a2489d
      }
Packit Service a2489d
      *store=*s;
Packit Service a2489d
      if(*s==0)
Packit Service a2489d
	 break;
Packit Service a2489d
      s++;
Packit Service a2489d
      store++;
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int NoGlob::Do()
Packit Service a2489d
{
Packit Service a2489d
   if(!done)
Packit Service a2489d
   {
Packit Service a2489d
      if(!HasWildcards(pattern))
Packit Service a2489d
      {
Packit Service a2489d
	 char *p=alloca_strdup(pattern);
Packit Service a2489d
	 UnquoteWildcards(p);
Packit Service a2489d
	 add(new FileInfo(p));
Packit Service a2489d
      }
Packit Service a2489d
      done=true;
Packit Service a2489d
      return MOVED;
Packit Service a2489d
   }
Packit Service a2489d
   return STALL;
Packit Service a2489d
}
Packit Service a2489d
NoGlob::NoGlob(const char *p) : Glob(0,p)
Packit Service a2489d
{
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void GlobURL::NewGlob(const char *p)
Packit Service a2489d
{
Packit Service a2489d
   glob=0;
Packit Service a2489d
   session=orig_session;
Packit Service a2489d
Packit Service a2489d
   url_prefix.set(p);
Packit Service a2489d
   url_prefix.truncate(url::path_index(p));
Packit Service a2489d
Packit Service a2489d
   ParsedURL p_url(p,true);
Packit Service a2489d
   if(p_url.proto && p_url.path)
Packit Service a2489d
   {
Packit Service a2489d
      session=my_session=FA::New(&p_url);
Packit Service a2489d
      if(session)
Packit Service a2489d
	 glob=session->MakeGlob(p_url.path);
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      glob=session->MakeGlob(p);
Packit Service a2489d
   }
Packit Service a2489d
   if(!glob)
Packit Service a2489d
      glob=new NoGlob(p);
Packit Service a2489d
   if(type==FILES_ONLY)
Packit Service a2489d
      glob->FilesOnly();
Packit Service a2489d
   else if(type==DIRS_ONLY)
Packit Service a2489d
      glob->DirectoriesOnly();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
GlobURL::GlobURL(const FileAccessRef& s,const char *p,type_select t)
Packit Service a2489d
   : orig_session(s), session(orig_session), type(t)
Packit Service a2489d
{
Packit Service a2489d
   nullglob=ResMgr::QueryBool("cmd:nullglob",0);
Packit Service a2489d
   NewGlob(p);
Packit Service a2489d
}
Packit Service a2489d
GlobURL::~GlobURL() {}
Packit Service a2489d
Packit Service a2489d
FileSet *GlobURL::GetResult()
Packit Service a2489d
{
Packit Service a2489d
   FileSet &list=*glob->GetResult();
Packit Service a2489d
   if(list.count()==0 && !nullglob)
Packit Service a2489d
      list.Add(new FileInfo(glob->GetPattern()));
Packit Service a2489d
   if(session==orig_session)
Packit Service a2489d
      return &list;
Packit Service a2489d
   for(int i=0; list[i]; i++)
Packit Service a2489d
      list[i]->SetName(url_file(url_prefix,list[i]->name));
Packit Service a2489d
   return &list;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
// GenericGlob implementation
Packit Service a2489d
GenericGlob::GenericGlob(FileAccess *s,const char *n_pattern)
Packit Service a2489d
   : Glob(s,n_pattern)
Packit Service a2489d
{
Packit Service a2489d
   dir_list=0;
Packit Service a2489d
   curr_dir=0;
Packit Service a2489d
Packit Service a2489d
   if(done)
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   char *dir=alloca_strdup(pattern);
Packit Service a2489d
   char *slash=strrchr(dir,'/');
Packit Service a2489d
   if(!slash)
Packit Service a2489d
      dir=0;
Packit Service a2489d
   else if(slash>dir)
Packit Service a2489d
      *slash=0;	  // non-root directory
Packit Service a2489d
   else
Packit Service a2489d
      dir[1]=0;	  // root directory
Packit Service a2489d
Packit Service a2489d
   if(dir)
Packit Service a2489d
   {
Packit Service a2489d
      updir_glob=new GenericGlob(s,dir);
Packit Service a2489d
      updir_glob->DirectoriesOnly();
Packit Service a2489d
      updir_glob->Suspend(); // don't run now, wait for options.
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int GenericGlob::Do()
Packit Service a2489d
{
Packit Service a2489d
   int m=STALL;
Packit Service a2489d
Packit Service a2489d
   if(done)
Packit Service a2489d
      return m;
Packit Service a2489d
Packit Service a2489d
   if(!dir_list && updir_glob)
Packit Service a2489d
   {
Packit Service a2489d
      if(updir_glob->IsSuspended())
Packit Service a2489d
      {
Packit Service a2489d
	 // pass the options.
Packit Service a2489d
	 updir_glob->MatchPeriod(match_period);
Packit Service a2489d
	 updir_glob->InhibitTilde(inhibit_tilde);
Packit Service a2489d
	 updir_glob->CaseFold(casefold);
Packit Service a2489d
	 updir_glob->Resume();
Packit Service a2489d
      }
Packit Service a2489d
      if(updir_glob->Error())
Packit Service a2489d
      {
Packit Service a2489d
	 SetError(updir_glob->ErrorText());
Packit Service a2489d
	 updir_glob=0;
Packit Service a2489d
	 done=true;
Packit Service a2489d
	 return MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(!updir_glob->Done())
Packit Service a2489d
	 return m;
Packit Service a2489d
      dir_list=updir_glob->GetResult();
Packit Service a2489d
      dir_list->rewind();
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      if(dir_list==0 || dir_list->curr()==0)
Packit Service a2489d
      {
Packit Service a2489d
	 done=true;
Packit Service a2489d
	 return m;
Packit Service a2489d
      }
Packit Service a2489d
      curr_dir=dir_list->curr()->name;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(li)
Packit Service a2489d
   {
Packit Service a2489d
      if(!li->Done() && !li->Error())
Packit Service a2489d
	 return m;
Packit Service a2489d
Packit Service a2489d
      if(li->Done() && !li->Error())
Packit Service a2489d
      {
Packit Service a2489d
	 FileSet *set=li->GetResult();
Packit Service a2489d
	 set->rewind();
Packit Service a2489d
	 for(FileInfo *info=set->curr(); info!=NULL; info=set->next())
Packit Service a2489d
	 {
Packit Service a2489d
	    const char *name=info->name;
Packit Service a2489d
	    if(name[0]=='.' && name[1]=='/')
Packit Service a2489d
	       name+=2;
Packit Service a2489d
	    if(curr_dir && curr_dir[0])
Packit Service a2489d
	       name=dir_file(curr_dir,name);
Packit Service a2489d
	    info->SetName(name);
Packit Service a2489d
	    add(info);
Packit Service a2489d
	 }
Packit Service a2489d
	 delete set;
Packit Service a2489d
      }
Packit Service a2489d
      if(dir_list)
Packit Service a2489d
	 dir_list->next();
Packit Service a2489d
      if(!dir_list || dir_list->curr()==0)
Packit Service a2489d
      {
Packit Service a2489d
	 if(li && li->Error())
Packit Service a2489d
	    SetError(li->ErrorText());
Packit Service a2489d
	 li=0;
Packit Service a2489d
	 done=true;
Packit Service a2489d
	 return MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      li=0;
Packit Service a2489d
      curr_dir=dir_list->curr()->name;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   li=session->MakeListInfo(curr_dir);
Packit Service a2489d
   if(!li)
Packit Service a2489d
   {
Packit Service a2489d
      // Cannot glob. Just unquote wildcards.
Packit Service a2489d
      char *p=alloca_strdup(pattern);
Packit Service a2489d
      UnquoteWildcards(p);
Packit Service a2489d
      add(new FileInfo(p));
Packit Service a2489d
      done=true;
Packit Service a2489d
      return MOVED;
Packit Service a2489d
   }
Packit Service a2489d
   li->UseCache(use_cache);
Packit Service a2489d
   return MOVED;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *GenericGlob::Status()
Packit Service a2489d
{
Packit Service a2489d
   if(updir_glob && !dir_list)
Packit Service a2489d
      return updir_glob->Status();
Packit Service a2489d
   if(!li)
Packit Service a2489d
      return "";
Packit Service a2489d
Packit Service a2489d
   const char *st = li->Status();
Packit Service a2489d
   if(!*st)
Packit Service a2489d
      return "";
Packit Service a2489d
Packit Service a2489d
   if(!curr_dir)
Packit Service a2489d
      return st;
Packit Service a2489d
Packit Service a2489d
   static xstring buf;
Packit Service a2489d
   buf.vset(curr_dir,": ",st,NULL);
Packit Service a2489d
   return buf;
Packit Service a2489d
}