Blame src/FileGlob.cc

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