|
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 |
}
|