Blame src/FindJob.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2012 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 <assert.h>
Packit 8f70b4
#include <fnmatch.h>
Packit 8f70b4
Packit 8f70b4
#include "FindJob.h"
Packit 8f70b4
#include "CmdExec.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "GetFileInfo.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "PatternSet.h"
Packit 8f70b4
#include "buffer_std.h"
Packit 8f70b4
Packit 8f70b4
#define top (*stack.last())
Packit 8f70b4
#define stack_ptr (stack.count()-1)
Packit 8f70b4
#define super SessionJob
Packit 8f70b4
#define orig_session super::session
Packit 8f70b4
Packit 8f70b4
int FinderJob::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   prf_res pres;
Packit 8f70b4
   Job *j;
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case START_INFO:
Packit 8f70b4
   {
Packit 8f70b4
      if(stack_ptr==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 ParsedURL u(dir,true);
Packit 8f70b4
	 if(u.proto)
Packit 8f70b4
	 {
Packit 8f70b4
	    session=my_session=FileAccess::New(&u);
Packit 8f70b4
	    session->SetPriority(fg?1:0);
Packit 8f70b4
	    init_dir=session->GetCwd();
Packit 8f70b4
	    Down(u.path?u.path.get():init_dir.path.get());
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      /* If we're not validating, and this is an argument (first-level path),
Packit 8f70b4
       * pretend the file exists. */
Packit 8f70b4
      if((file_info_need|FileInfo::NAME) == FileInfo::NAME &&
Packit 8f70b4
	    !validate_args && stack_ptr == -1)
Packit 8f70b4
      {
Packit 8f70b4
	 FileSet *fs = new FileSet();
Packit 8f70b4
	 fs->Add(new FileInfo(dir));
Packit 8f70b4
	 Push(fs);
Packit 8f70b4
	 state=LOOP;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      /* The first time we get here (stack_ptr == -1), dir is an actual
Packit 8f70b4
       * argument, so it might be a file.  (Every other time, it's guaranteed
Packit 8f70b4
       * to be a directory.)  Set show_dirs to true, so it'll end up actually
Packit 8f70b4
       * being on the stack, with type information. */
Packit 8f70b4
      li=new GetFileInfo(session, dir, stack_ptr == -1);
Packit 8f70b4
Packit 8f70b4
      /* Prepend for the argument level entry only: */
Packit 8f70b4
      if(stack_ptr != -1)
Packit 8f70b4
	 li->DontPrependPath();
Packit 8f70b4
Packit 8f70b4
      int need = file_info_need|FileInfo::NAME;
Packit 8f70b4
Packit 8f70b4
      /* We only explicitely need the type if we're recursing further. */
Packit 8f70b4
      if(stack_ptr+1 < maxdepth)
Packit 8f70b4
	 need |= FileInfo::TYPE;
Packit 8f70b4
Packit 8f70b4
      li->Need(need);
Packit 8f70b4
      if(use_cache)
Packit 8f70b4
	 li->UseCache();
Packit 8f70b4
      state=INFO;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   case INFO:
Packit 8f70b4
      if(!li->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(li->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 if(!quiet)
Packit 8f70b4
	    eprintf("%s: %s\n",op,li->ErrorText());
Packit 8f70b4
	 li=0;
Packit 8f70b4
	 errors++;
Packit 8f70b4
	 depth_done=true;
Packit 8f70b4
	 state=LOOP;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(stack_ptr != -1 && li->WasDirectory())
Packit 8f70b4
	 Enter(dir);
Packit 8f70b4
Packit 8f70b4
      Push(li->GetResult());
Packit 8f70b4
      top.fset->rewind();
Packit 8f70b4
Packit 8f70b4
      li=0;
Packit 8f70b4
      state=LOOP;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case LOOP:
Packit 8f70b4
      if(stack_ptr==-1 || top.fset->curr()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 Up();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      session->SetCwd(init_dir);
Packit 8f70b4
      session->Chdir(top.path,false);
Packit 8f70b4
      // at this point either is true:
Packit 8f70b4
      // 1. we just process another file (!depth_done)
Packit 8f70b4
      // 2. we just returned from a subdir (depth_done)
Packit 8f70b4
      if(depth_first && !depth_done && (maxdepth == -1 || stack_ptr+1 < maxdepth))
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *f=top.fset->curr();
Packit 8f70b4
	 if((f->defined&f->TYPE) && f->filetype==f->DIRECTORY)
Packit 8f70b4
	 {
Packit 8f70b4
	    Down(f->name);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      state=PROCESSING;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case PROCESSING:
Packit 8f70b4
      pres=ProcessFile(top.path,top.fset->curr());
Packit 8f70b4
Packit 8f70b4
      if(pres==PRF_LATER)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      depth_done=false;
Packit 8f70b4
Packit 8f70b4
      switch(pres)
Packit 8f70b4
      {
Packit 8f70b4
      case(PRF_FATAL):
Packit 8f70b4
	 errors++;
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      case(PRF_ERR):
Packit 8f70b4
	 errors++;
Packit 8f70b4
	 break;
Packit 8f70b4
      case(PRF_WAIT):
Packit 8f70b4
	 state=WAIT;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      case(PRF_OK):
Packit 8f70b4
	 break;
Packit 8f70b4
      case(PRF_LATER):
Packit 8f70b4
	 abort();
Packit 8f70b4
      }
Packit 8f70b4
   post_WAIT:
Packit 8f70b4
      state=LOOP;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      if(stack_ptr==-1)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(!depth_first && (maxdepth == -1 || stack_ptr+1 < maxdepth))
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *f=top.fset->curr();
Packit 8f70b4
	 if((f->defined&f->TYPE) && f->filetype==f->DIRECTORY)
Packit 8f70b4
	 {
Packit 8f70b4
	    top.fset->next();
Packit 8f70b4
	    Down(f->name);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      top.fset->next();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   case WAIT:
Packit 8f70b4
      j=FindDoneAwaitedJob();
Packit 8f70b4
      if(!j)
Packit 8f70b4
	 return m;
Packit 8f70b4
      RemoveWaiting(j);
Packit 8f70b4
      Delete(j);
Packit 8f70b4
      goto post_WAIT;
Packit 8f70b4
Packit 8f70b4
   case DONE:
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::Up()
Packit 8f70b4
{
Packit 8f70b4
   if(stack_ptr==-1)
Packit 8f70b4
   {
Packit 8f70b4
   done:
Packit 8f70b4
      state=DONE;
Packit 8f70b4
      Finish();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   /* stack[0] is the dir entry for the argument (ie. ls -d dir), and
Packit 8f70b4
    * stack[1] is the contents (ls dir); don't exit for the first. */
Packit 8f70b4
   if(stack_ptr)
Packit 8f70b4
      Exit();
Packit 8f70b4
   stack.chop();
Packit 8f70b4
   if(stack_ptr==-1)
Packit 8f70b4
      goto done;
Packit 8f70b4
   depth_done=true;
Packit 8f70b4
   state=LOOP;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::Push(FileSet *fset)
Packit 8f70b4
{
Packit 8f70b4
   const char *old_path=0;
Packit 8f70b4
   if(stack_ptr>=0)
Packit 8f70b4
   {
Packit 8f70b4
      old_path=top.path;
Packit 8f70b4
      fset->ExcludeDots(); /* don't need . and .. (except for stack[0]) */
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *new_path="";
Packit 8f70b4
   if(old_path) // the first path will be empty
Packit 8f70b4
      new_path=alloca_strdup(dir_file(old_path,dir));
Packit 8f70b4
Packit 8f70b4
   /* matching exclusions don't include the path, so they operate
Packit 8f70b4
    * on the filename portion only */
Packit 8f70b4
   if(exclude)
Packit 8f70b4
      fset->Exclude(0, exclude);
Packit 8f70b4
   stack.append(new place(new_path,fset));
Packit 8f70b4
Packit 8f70b4
   /* give a chance to operate on the list as a whole, and
Packit 8f70b4
    * possibly sort it */
Packit 8f70b4
   ProcessList(fset);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::Down(const char *p)
Packit 8f70b4
{
Packit 8f70b4
#ifdef FIND_DEBUG
Packit 8f70b4
   printf("Down(%s)\n",p.get());
Packit 8f70b4
#endif
Packit 8f70b4
   dir.set(p);
Packit 8f70b4
   state=START_INFO;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FinderJob::prf_res FinderJob::ProcessFile(const char *d,const FileInfo *f)
Packit 8f70b4
{
Packit 8f70b4
   return PRF_OK;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::Init()
Packit 8f70b4
{
Packit 8f70b4
   op="find";
Packit 8f70b4
   errors=0;
Packit 8f70b4
   li=0;
Packit 8f70b4
Packit 8f70b4
   show_sl=true;
Packit 8f70b4
Packit 8f70b4
   depth_first=false; // useful for rm -r
Packit 8f70b4
   depth_done=false;
Packit 8f70b4
Packit 8f70b4
   file_info_need=0;
Packit 8f70b4
   use_cache=true;
Packit 8f70b4
   validate_args=false;
Packit 8f70b4
Packit 8f70b4
   quiet=false;
Packit 8f70b4
   maxdepth=-1;
Packit 8f70b4
   exclude=0;
Packit 8f70b4
Packit 8f70b4
   state=START_INFO;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FinderJob::FinderJob(FileAccess *s)
Packit 8f70b4
   : SessionJob(s), orig_init_dir(orig_session->GetCwd()),
Packit 8f70b4
     session(orig_session), init_dir(session->GetCwd())
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::NextDir(const char *d)
Packit 8f70b4
{
Packit 8f70b4
   if(session!=orig_session)
Packit 8f70b4
   {
Packit 8f70b4
      session=orig_session;
Packit 8f70b4
      init_dir=orig_init_dir;
Packit 8f70b4
   }
Packit 8f70b4
   session->SetCwd(init_dir);
Packit 8f70b4
   Down(d);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FinderJob::~FinderJob()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::ShowRunStatus(const SMTaskRef<StatusLine>& sl)
Packit 8f70b4
{
Packit 8f70b4
   if(!show_sl)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case INFO:
Packit 8f70b4
      sl->Show("%s: %s",dir_file(stack_ptr>=0?top.path.get():0,dir),li->Status());
Packit 8f70b4
      break;
Packit 8f70b4
   case WAIT:
Packit 8f70b4
      Job::ShowRunStatus(sl);
Packit 8f70b4
      break;
Packit 8f70b4
   default:
Packit 8f70b4
      sl->Clear();
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& FinderJob::FormatStatus(xstring& s,int v,const char *prefix)
Packit 8f70b4
{
Packit 8f70b4
   SessionJob::FormatStatus(s,v,prefix);
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case INFO:
Packit 8f70b4
      s.appendf("\t%s: %s\n",dir_file(stack_ptr>=0?top.path.get():0,dir),li->Status());
Packit 8f70b4
      break;
Packit 8f70b4
   case WAIT:
Packit 8f70b4
      break;
Packit 8f70b4
   default:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob::Fg()
Packit 8f70b4
{
Packit 8f70b4
   super::Fg();
Packit 8f70b4
   if(orig_session!=session)
Packit 8f70b4
      session->SetPriority(1);
Packit 8f70b4
}
Packit 8f70b4
void FinderJob::Bg()
Packit 8f70b4
{
Packit 8f70b4
   if(orig_session!=session)
Packit 8f70b4
      session->SetPriority(0);
Packit 8f70b4
   super::Bg();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FinderJob_List implementation
Packit 8f70b4
// find files and write list to a stream
Packit 8f70b4
FinderJob::prf_res FinderJob_List::ProcessFile(const char *d,const FileInfo *fi)
Packit 8f70b4
{
Packit 8f70b4
   if(buf->Broken())
Packit 8f70b4
      return PRF_FATAL;
Packit 8f70b4
   if(buf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      eprintf("%s: %s\n",op,buf->ErrorText());
Packit 8f70b4
      return PRF_FATAL;
Packit 8f70b4
   }
Packit 8f70b4
   if(fg_data==0)
Packit 8f70b4
      fg_data=buf->GetFgData(fg);
Packit 8f70b4
   if(buf->Size()>0x10000)
Packit 8f70b4
      return PRF_LATER;
Packit 8f70b4
Packit 8f70b4
   xstring path_to_show;
Packit 8f70b4
   if(ProcessingURL())
Packit 8f70b4
   {
Packit 8f70b4
      FileAccess::Path old_cwd=session->GetCwd();
Packit 8f70b4
      session->SetCwd(init_dir);
Packit 8f70b4
      path_to_show.set(session->GetFileURL(dir_file(d,fi->name)));
Packit 8f70b4
      session->SetCwd(old_cwd);
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
      path_to_show.set(dir_file(d,fi->name));
Packit 8f70b4
   if((fi->defined&fi->TYPE) && fi->filetype==fi->DIRECTORY && strcmp(fi->name,"/"))
Packit 8f70b4
      path_to_show.append('/');
Packit 8f70b4
Packit 8f70b4
   if(long_listing) {
Packit 8f70b4
      FileInfo n(*fi);
Packit 8f70b4
      n.SetName(path_to_show);
Packit 8f70b4
      n.MakeLongName();
Packit 8f70b4
      buf->Put(n.longname);
Packit 8f70b4
   } else {
Packit 8f70b4
      buf->Put(path_to_show);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   buf->Put("\n");
Packit 8f70b4
   return FinderJob::ProcessFile(d,fi);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FinderJob_List::FinderJob_List(FileAccess *s,ArgV *a,FDStream *o)
Packit 8f70b4
   : FinderJob(s), args(a), long_listing(false)
Packit 8f70b4
{
Packit 8f70b4
   if(o)
Packit 8f70b4
      buf=new IOBufferFDStream(o,IOBuffer::PUT);
Packit 8f70b4
   else
Packit 8f70b4
      buf=new IOBuffer_STDOUT(this);
Packit 8f70b4
   show_sl = !o || !o->usesfd(1);
Packit 8f70b4
   NextDir(a->getcurr());
Packit 8f70b4
   ValidateArgs();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FinderJob_List::Finish()
Packit 8f70b4
{
Packit 8f70b4
   const char *d=args->getnext();
Packit 8f70b4
   if(!d) {
Packit 8f70b4
      buf->PutEOF();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   NextDir(d);
Packit 8f70b4
}