On Mon, Nov 12, 2001 at 12:03:54PM +0300, Alexander V. Lukyanov wrote:
> > Second, chdir results aren't cached, so it keeps doing the test chdirs
> > every time.  Of course, most chdir results shouldn't be cached; it's only
> > useful for this case.  Do you think we could use a more generalized cache?
> > Perhaps a simple CacheObject base class, containing a type and a virtual
> > destructor; allow searching for cached objects by name (path or whatever
> > is appropriate) and type.  (It'd mean using MI, but I don't think that's
> > a problem.)
> >
> > Third, "glob -a cls *" is slow for large directories: cls has to
> > re-parse the directory for each file.  This is another incentive to have
> > generalized caching: we could cache FileSets, too.  We could also cache
> > FileSets inside LsCache explicitely, alongside ls text, but that's a hack.
> 
> Do it carefully not to use wrong cached object, e.g. you would have
> to store all options of ListInfo along with the resulting FileSet.

I'm rethinking this; it might be more work than I'd like to do right
now.

For example, "ls *" in a large directory, pre-globbed, would look up
the CWD once for each file.  Currently, it's fast, since globbing is
done directly.  For this to be fast out of cache like that, the cache
might need to be a binary search, which would mean a good deal of
change.  It might be worth it, but there are other things to do first
... and I can fix the "cls dir vs cls dir/" thing without this.

> > Not going to bother you with a patch until I work out these issues, since
> > it's not useful yet.  (I've attached the class source, if you want to look
> > at it; don't drop it in yet, though.)
> 
> The message was without an attachement.

Duh.  Sorry.

-- 
Glenn Maynard
#include <config.h>
#include <errno.h>

#include "GetFileInfo.h"
#include "misc.h"

GetFileInfo::GetFileInfo(FileAccess *a, const char *_dir, bool _showdir)
{
   session=a;
   dir=xstrdup(_dir? _dir:"");
   showdir=_showdir;
   state=CHANGE_DIR;
   tried_dir=tried_file=false;
   result=0;
   realdir=0;
   li=0;

   if(_showdir) tried_dir = true;

   saved_error_text=0;
}

GetFileInfo::~GetFileInfo()
{
   Delete(li);
   xfree(saved_error_text);
   xfree(dir);
   xfree(realdir);
}

int GetFileInfo::Do()
{
   int res;

   if(Done())
      return STALL;

   switch(state)
   {
   case CHANGE_DIR:
      if(tried_dir && tried_file) {
	 /* We tried both; no luck.  Fail. */
	 SetError(saved_error_text);
	 done=true;

	 return MOVED;
      }

      if(!tried_dir) 
      {
	 /* First, try to treat the path as a directory. */
	 tried_dir=true;
	 realdir = xstrdup(dir);
      }
      else if(!tried_file) 
      {
	 /* No need to save the initial directory, since we only chdir()
	  * twice when the first fails (hence leaving our cwd intact). */
	 tried_file=true;

	 xfree(realdir);
	 realdir = xstrdup(dir);
	 /* If the path ends with a slash, and we're showing directories, remove it. */
	 if(*realdir && realdir[strlen(realdir)-1] == '/')
	    realdir[strlen(realdir)-1] = 0;
	 
	 char *slash = strrchr(realdir, '/');
	 if(!slash)
	    realdir=xstrdup(""); /* file with no path */
	 else
	    *slash=0;
      }

      session->Chdir(realdir, true);
      state=CHANGING_DIR;
      return MOVED;

   case CHANGING_DIR:
      res=session->Done();
      if(res==FA::IN_PROGRESS)
	 return STALL;
      if(res<0)
      {
	 /* Failed.  Save the error, then go back and try to CD again. 
	  * Only save the first error, so error texts contain the full
	  * path. */
	 if(!saved_error_text)
	    saved_error_text = xstrdup(session->StrError(res));
	 state=CHANGE_DIR;
	 return MOVED;
      }
      
      /* Get a listing: */
      li=session->MakeListInfo();
      if(follow_symlinks) li->FollowSymlinks();
      li->SetNeed(need);
      SetExclude(path, rxc_exclude, rxc_include);
      state=GETTING_LIST;
      return MOVED;

   case GETTING_LIST:
      if(li->Error()) {
	 SetError(li->ErrorText());
	 return MOVED;
      }

      if(!li->Done())
	 return STALL;

      done=true;

      /* Got the list.  Steal it from the listinfo: */ 
      result=li->GetResult();
      Delete(li); li=0;

      /* If this was a listing of the dirname: */
      if(strcmp(realdir, dir)) {
	 char *filename = xstrdup(basename_ptr(dir));
	 if(filename[strlen(filename)-1] == '/')
	    filename[strlen(filename)-1] = 0;
	 
	 /* Find the file with our filename: */
	 FileInfo *file = result->FindByName(filename);
	 xfree(filename);

	 if(!file) {
	    /* The file doesn't exist; fail. */
	    SetError(strerror(ENOENT));
	    delete result; result=0;
	    return MOVED;
	 }

	 /* If we're not listing directories as files, and the file is a
	  * directory, we should have been able to Chdir into it to begin
	  * with.  We probably got Access Denied.  Fail. */
	 if(!showdir && file->filetype==FileInfo::DIRECTORY) {
	    SetError(strerror(EACCES));
	    delete result; result=0;
	    return MOVED;
	 }

	 FileSet *newresult=new FileSet();
	 newresult->Add(new FileInfo(*file));
	 delete result;
	 result=newresult;
      }

      result->PrependPath(realdir);

      return MOVED;
   }

   abort();
}

const char *GetFileInfo::Status()
{
   if(li && !li->Done()) return li->Status();

   return session->CurrentStatus();
}

/*
 * lftp and utils
 *
 * Copyright (c) 2001 by Alexander V. Lukyanov ([EMAIL PROTECTED])
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef GETFILEINFO_H
#define GETFILEINFO_H

#include <stdio.h>

#include "SMTask.h"
#include "FileAccess.h"

class GetFileInfo: public ListInfo
{
   FileAccess *session;
   ListInfo *li;
   
   /* file or dir we're listing: */
   char *dir;

   /* directory we've actually listed: */
   char *realdir;

   bool showdir;
   
   enum state_t { CHANGE_DIR, CHANGING_DIR, GETTING_LIST } state;
   /* whether we've tried to cd to the whole dir (treating it as a dir): */
   bool tried_dir;
   /* and whether we've tried to cd to the basename (treating it as a file): */
   bool tried_file;

   char *saved_error_text;

public:
   GetFileInfo(FileAccess *a, const char *path, bool showdir);
   virtual ~GetFileInfo();

   int Do();
   const char *Status();
};

#endif /* GETFILEINFO_H */

Reply via email to