Blame src/LsCache.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 "FileAccess.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "plural.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
Packit 8f70b4
int LsCacheEntry::EstimateSize() const
Packit 8f70b4
{
Packit 8f70b4
   int size=sizeof(*this);
Packit 8f70b4
   size+=LsCacheEntryLoc::EstimateSize();
Packit 8f70b4
   size+=LsCacheEntryData::EstimateSize();
Packit 8f70b4
   return size;
Packit 8f70b4
}
Packit 8f70b4
LsCacheEntryLoc::LsCacheEntryLoc(const FileAccess *p_loc,const char *a,int m)
Packit 8f70b4
   : arg(a), mode(m)
Packit 8f70b4
{
Packit 8f70b4
   loc=p_loc->Clone();
Packit 8f70b4
   loc->Suspend();
Packit 8f70b4
}
Packit 8f70b4
const char *LsCacheEntryLoc::GetClosure() const
Packit 8f70b4
{
Packit 8f70b4
   return loc->GetHostName();
Packit 8f70b4
}
Packit 8f70b4
LsCacheEntryData::LsCacheEntryData(int e,const char *d,int l,const FileSet *fs)
Packit 8f70b4
{
Packit 8f70b4
   SetData(e,d,l,fs);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
LsCacheEntry::LsCacheEntry(const FileAccess *p_loc,const char *a,int m,int e,const char *d,int l,const FileSet *fs)
Packit 8f70b4
   : LsCacheEntryLoc(p_loc,a,m), LsCacheEntryData(e,d,l,fs)
Packit 8f70b4
{
Packit 8f70b4
   SetResource(e==FA::OK?"cache:expire":"cache:expire-negative",GetClosure());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void LsCacheEntryData::SetData(int e,const char *d,int l,const FileSet *fs)
Packit 8f70b4
{
Packit 8f70b4
   afset=fs?new FileSet(fs):0;
Packit 8f70b4
   data.nset(d,l);
Packit 8f70b4
   err_code=e;
Packit 8f70b4
}
Packit 8f70b4
void LsCacheEntryData::GetData(int *e,const char **d,int *l,const FileSet **fs)
Packit 8f70b4
{
Packit 8f70b4
   if(d && l)
Packit 8f70b4
   {
Packit 8f70b4
      *d=data;
Packit 8f70b4
      *l=data.length();
Packit 8f70b4
   }
Packit 8f70b4
   if(fs)
Packit 8f70b4
      *fs=afset;
Packit 8f70b4
   *e=err_code;
Packit 8f70b4
}
Packit 8f70b4
bool LsCacheEntryLoc::Matches(const FileAccess *p_loc,const char *a,int m)
Packit 8f70b4
{
Packit 8f70b4
   return (m==-1 || mode==m) && arg.eq(a) && p_loc->SameLocationAs(loc);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
ResDecl res_cache_empty_listings("cache:cache-empty-listings","no",ResMgr::BoolValidate,0);
Packit 8f70b4
ResDecl res_cache_enable("cache:enable","yes",ResMgr::BoolValidate,0);
Packit 8f70b4
ResDecl res_cache_expire("cache:expire","60m",ResMgr::TimeIntervalValidate,0);
Packit 8f70b4
ResDecl res_cache_expire_neg("cache:expire-negative","1m",ResMgr::TimeIntervalValidate,0);
Packit 8f70b4
ResDecl res_cache_size  ("cache:size","16M",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
Packit 8f70b4
LsCache::LsCache() : Cache(&res_cache_size,&res_cache_enable) {}
Packit 8f70b4
Packit 8f70b4
void LsCache::Add(const FileAccess *p_loc,const char *a,int m,int e,const char *d,int l,const FileSet *fs)
Packit 8f70b4
{
Packit 8f70b4
   if(!strcmp(p_loc->GetProto(),"file"))
Packit 8f70b4
      return;  // don't cache local objects
Packit 8f70b4
   if(l == 0 &&
Packit 8f70b4
	 !res_cache_empty_listings.QueryBool(p_loc->GetHostName()))
Packit 8f70b4
      return;
Packit 8f70b4
   if(e!=FA::OK && e!=FA::NO_FILE && e!=FA::NOT_SUPP)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   Trim();
Packit 8f70b4
Packit 8f70b4
   LsCacheEntry *c=Find(p_loc,a,m);
Packit 8f70b4
   if(!c)
Packit 8f70b4
   {
Packit 8f70b4
      if(!IsEnabled(p_loc->GetHostName()))
Packit 8f70b4
	 return;
Packit 8f70b4
      AddCacheEntry(new LsCacheEntry(p_loc,a,m,e,d,l,fs));
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      c->SetData(e,d,l,fs);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void LsCache::Add(const FileAccess *p_loc,const char *a,int m,int e,const Buffer *ubuf,const FileSet *fs)
Packit 8f70b4
{
Packit 8f70b4
   if(!ubuf->IsSaving())
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   const char *cache_buffer;
Packit 8f70b4
   int cache_buffer_size;
Packit 8f70b4
   if(e)
Packit 8f70b4
   {
Packit 8f70b4
      cache_buffer=ubuf->ErrorText();
Packit 8f70b4
      cache_buffer_size=strlen(cache_buffer)+1;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
      ubuf->GetSaved(&cache_buffer,&cache_buffer_size);
Packit 8f70b4
   LsCache::Add(p_loc,a,m,e,cache_buffer,cache_buffer_size,fs);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
LsCacheEntry *LsCache::Find(const FileAccess *p_loc,const char *a,int m)
Packit 8f70b4
{
Packit 8f70b4
   if(!IsEnabled(p_loc->GetHostName()))
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   LsCacheEntry *c;
Packit 8f70b4
   for(c=IterateFirst(); c; c=IterateNext())
Packit 8f70b4
   {
Packit 8f70b4
      if(c->Matches(p_loc,a,m))
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   if(c && c->Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      Trim();
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   return c;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool LsCache::Find(const FileAccess *p_loc,const char *a,int m,int *e,const char **d,int *l,const FileSet **fs)
Packit 8f70b4
{
Packit 8f70b4
   LsCacheEntry *c=Find(p_loc,a,m);
Packit 8f70b4
   if(!c)
Packit 8f70b4
      return false;
Packit 8f70b4
   c->GetData(e,d,l,fs);
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const FileSet *LsCache::FindFileSet(const FileAccess *p_loc,const char *a,int m)
Packit 8f70b4
{
Packit 8f70b4
   LsCacheEntry *c=Find(p_loc,a,m);
Packit 8f70b4
   if(!c)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return c->GetFileSet(c->loc);
Packit 8f70b4
}
Packit 8f70b4
const FileSet *LsCacheEntryData::GetFileSet(const FileAccess *parser)
Packit 8f70b4
{
Packit 8f70b4
   if(afset)
Packit 8f70b4
      return afset;
Packit 8f70b4
   if(err_code!=FA::OK)
Packit 8f70b4
      return 0;
Packit 8f70b4
   afset=parser->ParseLongList(data, data.length());
Packit 8f70b4
   return afset;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void LsCache::UpdateFileSet(const FileAccess *p_loc,const char *a,int m,const FileSet *fs)
Packit 8f70b4
{
Packit 8f70b4
   if(!fs)
Packit 8f70b4
      return;
Packit 8f70b4
   LsCacheEntry *c=Find(p_loc,a,m);
Packit 8f70b4
   if(!c)
Packit 8f70b4
      return;
Packit 8f70b4
   c->UpdateFileSet(fs);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void LsCache::List()
Packit 8f70b4
{
Packit 8f70b4
   Trim();
Packit 8f70b4
Packit 8f70b4
   long vol=0;
Packit 8f70b4
   for(LsCacheEntry *c=IterateFirst(); c; c=IterateNext())
Packit 8f70b4
      vol+=c->EstimateSize();
Packit 8f70b4
Packit 8f70b4
   printf(plural("%ld $#l#byte|bytes$ cached",vol),vol);
Packit 8f70b4
Packit 8f70b4
   long sizelimit=res_cache_size.Query(0);
Packit 8f70b4
   if(sizelimit<0)
Packit 8f70b4
      puts(_(", no size limit"));
Packit 8f70b4
   else
Packit 8f70b4
      printf(_(", maximum size %ld\n"),sizelimit);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void LsCache::Changed(change_mode m,const FileAccess *f,const char *dir)
Packit 8f70b4
{
Packit 8f70b4
   xstring fdir(dir_file(f->GetCwd(),dir));
Packit 8f70b4
   if(m==FILE_CHANGED)
Packit 8f70b4
      dirname_modify(fdir);
Packit 8f70b4
Packit 8f70b4
   LsCacheEntry *c=IterateFirst();
Packit 8f70b4
   while(c)
Packit 8f70b4
   {
Packit 8f70b4
      const FileAccess *sloc=c->loc;
Packit 8f70b4
      if(f->SameLocationAs(sloc) || (f->SameSiteAs(sloc)
Packit 8f70b4
	       && (m==TREE_CHANGED?
Packit 8f70b4
		     !strncmp(fdir,dir_file(sloc->GetCwd(),c->arg),fdir.length())
Packit 8f70b4
		   : !strcmp (fdir,dir_file(sloc->GetCwd(),c->arg)))))
Packit 8f70b4
	 c=IterateDelete();
Packit 8f70b4
      else
Packit 8f70b4
	 c=IterateNext();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Mark a path as a directory or file. (We have other ways of knowing this;
Packit 8f70b4
 * this is the most explicit and least expensive.) */
Packit 8f70b4
void LsCache::SetDirectory(const FileAccess *p_loc, const char *path, bool dir)
Packit 8f70b4
{
Packit 8f70b4
   if(!path)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   FileAccess::Path new_cwd = p_loc->GetCwd();
Packit 8f70b4
   new_cwd.Change(path,!dir);
Packit 8f70b4
   SMTaskRef<FileAccess> new_p_loc(p_loc->Clone());
Packit 8f70b4
   new_p_loc->SetCwd(new_cwd);
Packit 8f70b4
Packit 8f70b4
   const char *entry = dir? "1":"0";
Packit 8f70b4
   LsCache::Add(new_p_loc,"",FileAccess::CHANGE_DIR, dir?FA::OK:FA::NO_FILE, entry, strlen(entry));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* This is a hint function. If file type is really needed, use GetFileInfo
Packit 8f70b4
 * with showdir set to true. (GetFileInfo uses this function.)
Packit 8f70b4
 * Returns -1 if type is not known, 1 if a directory, 0 if a file. */
Packit 8f70b4
Packit 8f70b4
int LsCache::IsDirectory(const FileAccess *p_loc,const char *dir_c)
Packit 8f70b4
{
Packit 8f70b4
   FileAccess::Path new_cwd(p_loc->GetCwd());
Packit 8f70b4
   new_cwd.Change(dir_c);
Packit 8f70b4
   FileAccessRef new_p_loc(p_loc->Clone());
Packit 8f70b4
   new_p_loc->SetCwd(new_cwd);
Packit 8f70b4
Packit 8f70b4
   int ret = -1;
Packit 8f70b4
Packit 8f70b4
   /* Cheap tests first:
Packit 8f70b4
    *
Packit 8f70b4
    * First, we know the path is a directory or not if we have an expicit
Packit 8f70b4
    * CHANGE_DIR entry for it. */
Packit 8f70b4
   const char *buf_c;
Packit 8f70b4
   int bufsiz;
Packit 8f70b4
   int e;
Packit 8f70b4
   if(Find(new_p_loc, "", FileAccess::CHANGE_DIR, &e, &buf_c,&bufsiz))
Packit 8f70b4
   {
Packit 8f70b4
      assert(bufsiz==1);
Packit 8f70b4
      return (e==FA::OK);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   /* We know the path is a directory if we have a cache entry for it.  This is
Packit 8f70b4
    * true regardless of the list type.  (Unless it's a CHANGE_DIR entry; do this
Packit 8f70b4
    * test after the CHANGE_DIR check.) */
Packit 8f70b4
   if(Find(new_p_loc, "", FA::LONG_LIST, &e, 0,0))
Packit 8f70b4
      return(e==FA::OK);
Packit 8f70b4
   if(Find(new_p_loc, "", FA::MP_LIST, &e, 0,0))
Packit 8f70b4
      return(e==FA::OK);
Packit 8f70b4
   if(Find(new_p_loc, "", FA::LIST, &e, 0,0))
Packit 8f70b4
      return(e==FA::OK);
Packit 8f70b4
Packit 8f70b4
   /* We know this is a file or a directory if the dirname is cached and
Packit 8f70b4
    * contains the basename. */
Packit 8f70b4
   {
Packit 8f70b4
      const char *bn=basename_ptr(new_cwd.path);
Packit 8f70b4
      bn=alloca_strdup(bn); // save basename
Packit 8f70b4
Packit 8f70b4
      new_cwd.Change("..");
Packit 8f70b4
      new_p_loc->SetCwd(new_cwd);
Packit 8f70b4
Packit 8f70b4
      const FileSet *fs=FindFileSet(new_p_loc, "", FA::MP_LIST);
Packit 8f70b4
      if(!fs)
Packit 8f70b4
	 fs=FindFileSet(new_p_loc, "", FA::LONG_LIST);
Packit 8f70b4
      if(fs)
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=fs->FindByName(bn);
Packit 8f70b4
	 if(fi && (fi->defined&fi->TYPE))
Packit 8f70b4
	    return(fi->filetype == fi->DIRECTORY);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return ret;
Packit 8f70b4
}