(bugfixes)

FileSetOutput.cc: don't add a type suffix to cls output if we don't know the type.

GetFileInfo.cc: Propagate use_cache to child ListInfos; fixes recls.
Don't EACCESS if we don't know the file type.

misc.cc, misc.h: revert to va_copy.

You ended up with my VA_COPY but you didn't end up with my va_copy tests; I
don't know how this was compiling for you.

(arguably a bugfix, but see below)
ftpclass.cc: Quiet error message of closed connection if it's an aborted
connection and we aren't using use-abor.

(non-fixes)

FileAccess.cc, FileAccess.h: add a simple list parser interface.

FileInfo.cc, FileInfo.h: make parsing a ListParser.

FtpSplitList.cc, FtpSplitList.h, FileInfo.cc: Made FtpSplitList return a
char **, not a FileSet.  Putting lines from a long list into a FileSet
doesn't really make sense.  Globbing with FtpSplitList (via its Glob
parent) wouldn't have worked, since it would glob the whole long line
(and other glob functions wouldn't work in similar ways); it's now a
simple FileAccessOperation.

FtpListInfo.cc, FtpListInfo.h: Merge GETTING_LONG_LIST and
GETTING_SHORT_LIST; they're identical now.

FileAccess.cc, FileAccess.h, ftpclass.cc, ftpclass.h: Added
MakeListParser.  This is probably extensible to the other protocols, but
I havn't looked at any of them.

LsCache.cc, LsCache.h: let Find() ignore mode.  Add IsDirectory().  

misc.cc, misc.h: add split_toks.

---
We do want to print the error message of a closed data connection if
it's unexpected; I'm not sure if this is happening.  (I'd prefer not
displaying the error in either case to displaying it in both--it makes
reusing connections with connection-limit ugly.)

ParseFtpLongList_OS2 and ParseFtpLongList_MacWebStar never seem to
instantiate fi.  Didn't touch it, though.

IsDirectory comments:
Well, this does parse the list twice to find out if a path is a directory
or not, but it only does this once per operation, so it's not *too* bad.
Possible fix: don't have LsCache cache FileSets (same reason: memory usage).
Instead, add a Find() call that returns a FileSet (ie. call the regular
Find(), parse the results and return them), and keep *one* of these around.
If the same one is requested twice in a row, we don't have to reparse it.
This avoids the (currently) unneeded complexity of generic caching and the
memory problems of caching all FileSets.  I'll hold off on this for now, at
least until this code is better tested.

You might want to move FtpListParser into its own file.  I didn't do this to
keep the patch small.

FtpSplitList could probably be easily merged into FileInfo, perhaps using a
FileCopy.  FtpSplitList is doing very little now.  (One of the things that was
a bit confusing at first is the number of ways there are to get listings:
FtpSplitList, *ListInfo, *Glob, FileCopyPeerDirList; they're all very
similar.)  Nothing else uses FtpSplitList.

Glitch: cls is stalling somewhere.  I havn't yet figured out where.  It seems
to happen during the CD test of GetFileInfo.  It doesn't happen when the cls
causes a connection to open.  It seems to lag *before* sending the CWD command,
but after the Chdir() call.

You have this in acconfig.h:
#if !HAVE_DECL_VA_COPY && HAVE_DECL___VA_COPY
# define va_copy __va_copy
# undef HAVE_DECL_VA_COPY
# define HAVE_DECL_VA_COPY 1
#endif

This is getting mangled by the time it gets to config.h:

#if !HAVE_DECL_VA_COPY && HAVE_DECL___VA_COPY
# define va_copy __va_copy
# define HAVE_DECL_VA_COPY 0
# define HAVE_DECL_VA_COPY 0
#endif

Is "@BOTTOM@" intended to stop acconfig.h parsing?  I can't find it in
my autoconf (2.52).

-- 
Glenn Maynard
Index: FileAccess.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileAccess.cc,v
retrieving revision 1.88
diff -u -r1.88 FileAccess.cc
--- FileAccess.cc       2001/11/30 16:23:03     1.88
+++ FileAccess.cc       2001/12/02 07:46:51
@@ -914,6 +914,7 @@
 ListInfo *FileAccess::MakeListInfo() { return 0; }
 Glob *FileAccess::MakeGlob(const char *pattern) { return 0; }
 DirList *FileAccess::MakeDirList(ArgV *a) { if(a) delete a; return 0; }
+ListParser *FileAccess::MakeListParser() { return 0; }
 
 DirList::~DirList()
 {
@@ -1253,6 +1254,17 @@
    for(int i=0; list[i]; i++)
       list[i]->SetName(url_file(url_prefix,list[i]->name));
    return &list;
+}
+
+FileSet *ListParser::ParseList(char *buf, int len)
+{
+   char **list;
+   
+   split_toks(buf, len, list);
+
+   FileSet *ret = ParseList(list);
+   xfree(list);
+   return ret;
 }
 
 #include "modconfig.h"
Index: FileAccess.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileAccess.h,v
retrieving revision 1.66
diff -u -r1.66 FileAccess.h
--- FileAccess.h        2001/11/15 17:06:48     1.66
+++ FileAccess.h        2001/12/02 07:46:51
@@ -48,6 +48,7 @@
 #define NO_DATE_YET  ((time_t)-2L)
 
 class ListInfo;
+class ListParser;
 class Glob;
 class NoGlob;
 class DirList;
@@ -287,6 +288,7 @@
    virtual ListInfo *MakeListInfo();
    virtual Glob *MakeGlob(const char *pattern);
    virtual DirList *MakeDirList(ArgV *a);
+   virtual ListParser *MakeListParser();
 
    static bool NotSerious(int err);
 
@@ -447,6 +449,26 @@
    void Need(unsigned mask) { need|=mask; }
    void NoNeed(unsigned mask) { need&=~mask; }
    void FollowSymlinks() { follow_symlinks=true; }
+};
+
+class ListParser
+{
+protected:
+   /* FA::LONG_LIST or FA::LIST */
+   int mode;
+
+   /* number of unparsable lines */
+   int err;
+
+public:
+   ListParser(): mode(FileAccess::LONG_LIST), err(0) { }
+   virtual ~ListParser() { }
+
+   virtual FileSet *ParseList(const char * const *buf)=0;
+   FileSet *ParseList(char *buf, int len);
+
+   void SetMode(int m) { mode = m; }
+   int GetErr() const { return err; }
 };
 
 #include "buffer.h"
Index: FileSetOutput.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileSetOutput.cc,v
retrieving revision 1.15
diff -u -r1.15 FileSetOutput.cc
--- FileSetOutput.cc    2001/11/23 15:19:41     1.15
+++ FileSetOutput.cc    2001/12/02 07:46:51
@@ -198,6 +198,8 @@
 
 const char *FileSetOutput::FileInfoSuffix(const FileInfo &fi) const
 {
+   if(!(fi.defined&fi.TYPE))
+      return "";
    if(fi.filetype == FileInfo::DIRECTORY)
       return "/";
    else if(fi.filetype == FileInfo::SYMLINK)
Index: FtpListInfo.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FtpListInfo.cc,v
retrieving revision 1.45
diff -u -r1.45 FtpListInfo.cc
--- FtpListInfo.cc      2001/11/30 16:31:15     1.45
+++ FtpListInfo.cc      2001/12/02 07:46:52
@@ -57,7 +57,7 @@
    FileInfo *file;
    int res;
    int m=STALL;
-   FileSet *slist_res;
+   const char * const * slist_res;
    int err;
 
    if(done)
@@ -66,12 +66,17 @@
    switch(state)
    {
    case(INITIAL):
-      slist=new FtpSplitList(session,session->LONG_LIST);
-      slist->UseCache(use_cache);
       state=GETTING_LONG_LIST;
       m=MOVED;
 
    case(GETTING_LONG_LIST):
+   case(GETTING_SHORT_LIST):
+      if(!slist)
+      {
+        slist=new FtpSplitList(session,state==GETTING_LONG_LIST? 
+session->LONG_LIST:session->LIST);
+        slist->UseCache(use_cache);
+      }
+
       if(!slist->Done())
         return m;
       if(slist->Error())
@@ -83,12 +88,13 @@
       }
       slist_res=slist->GetResult();
 
-      // don't consider empty list to be valid
-      if(slist_res->get_fnum()>0)
-        result=ParseFtpLongList(*slist_res,&err,
-           ResMgr::Query("ftp:timezone",session->GetHostName()));
-      else
-        err=1;
+      {
+        ListParser *parser = session->MakeListParser();
+        parser->SetMode(state==GETTING_LONG_LIST? session->LONG_LIST:session->LIST);
+        result=parser->ParseList(slist_res);
+        err=parser->GetErr();
+        delete parser;
+      }
 
       if(!result)
         result=new FileSet;
@@ -96,40 +102,25 @@
       Delete(slist);
       slist=0;
       slist_res=0; // note: slist_res is pointer to part of slist
-
-      if(err==0)
-        goto pre_GETTING_INFO;
 
-      // there were parse errors, try another method
-      slist=new FtpSplitList(session,session->LIST);
-      slist->UseCache(use_cache);
-      state=GETTING_SHORT_LIST;
-      m=MOVED;
-
-   case(GETTING_SHORT_LIST):
-      if(!slist->Done())
-        return m;
-      if(slist->Error())
+      if(err)
       {
-        SetError(slist->ErrorText());
-        Delete(slist);
-        slist=0;
-        return MOVED;
+        // there were parse errors, try another method
+        if(state == GETTING_LONG_LIST)
+        {
+           state=GETTING_SHORT_LIST;
+           return MOVED;
+        }
+        /* tried both, no luck; fall through with empty list */
       }
-      slist_res=slist->GetResult();
-      result->Merge(slist_res);
-      Delete(slist);
-      slist=0;
-      slist_res=0;
 
-   pre_GETTING_INFO:
+      m=MOVED;
+      state=GETTING_INFO;
+
       result->ExcludeDots();
       if(rxc_exclude || rxc_include)
         result->Exclude(path,rxc_exclude,rxc_include);
 
-      state=GETTING_INFO;
-      m=MOVED;
-
       get_info_cnt=result->get_fnum();
       if(get_info_cnt==0)
         goto info_done;
@@ -182,8 +173,6 @@
       if(get_info_cnt==0)
         goto info_done;
       session->GetInfoArray(get_info,get_info_cnt);
-      state=GETTING_INFO;
-      m=MOVED;
    case(GETTING_INFO):
       res=session->Done();
       if(res==Ftp::DO_AGAIN)
@@ -248,6 +237,15 @@
    abort();
 }
 
+FtpListParser::FtpListParser(const char *hostname)
+{
+   tz=xstrdup(ResMgr::Query("ftp:timezone",hostname));
+}
+
+FtpListParser::~FtpListParser()
+{
+   xfree(tz);
+}
 
 /*
 total 123
@@ -257,12 +255,11 @@
 lrwxrwxrwx   1 lav      root           33 Feb 14 17:45 ltconfig -> 
/usr/share/libtool/ltconfig
 NOTE: group may be missing.
 */
-static
-FileInfo *ParseFtpLongList_UNIX(const char *line_c,int *err,const char *tz)
+FileInfo *FtpListParser::ParseFtpLongList_UNIX(const char *line_c)
 {
 #define FIRST_TOKEN strtok(line," \t")
 #define NEXT_TOKEN  strtok(NULL," \t")
-#define ERR do{(*err)++;delete fi;return(0);}while(0)
+#define ERR do{err++;delete fi;return(0);}while(0)
    char         *line=alloca_strdup(line_c);
    int          len=strlen(line);
    int  tmp;
@@ -417,8 +414,7 @@
 03-18-98  06:01AM              2109440 nlxb318e.tar
 07-02-98  11:17AM                13844 Whatsnew.txt
 */
-static
-FileInfo *ParseFtpLongList_NT(const char *line_c,int *err,const char *tz)
+FileInfo *FtpListParser::ParseFtpLongList_NT(const char *line_c)
 {
    char         *line=alloca_strdup(line_c);
    int          len=strlen(line);
@@ -504,8 +500,7 @@
  up - permissions in octal
  \t - file name follows.
 */
-static
-FileInfo *ParseFtpLongList_EPLF(const char *line,int *err,const char *)
+FileInfo *FtpListParser::ParseFtpLongList_EPLF(const char *line)
 {
    int len=strlen(line);
    const char *b=line;
@@ -600,8 +595,7 @@
                  0          DIR  06-27-96  11:57  PROTOCOL
                169               11-29-94  09:20  SYSLEVEL.MPT
 */
-static
-FileInfo *ParseFtpLongList_OS2(const char *line_c,int *err,const char *tz)
+FileInfo *FtpListParser::ParseFtpLongList_OS2(const char *line_c)
 {
    char         *line=alloca_strdup(line_c);
    int          len=strlen(line);
@@ -666,8 +660,7 @@
    return fi;
 }
 
-static
-FileInfo *ParseFtpLongList_MacWebStar(const char *line_c,int *err,const char *tz)
+FileInfo *FtpListParser::ParseFtpLongList_MacWebStar(const char *line_c)
 {
    char         *line=alloca_strdup(line_c);
    int          len=strlen(line);
@@ -797,31 +790,49 @@
    return fi;
 }
 
-typedef FileInfo *(*ListParser)(const char *line,int *err,const char *tz);
-static ListParser list_parsers[]={
-   ParseFtpLongList_UNIX,
-   ParseFtpLongList_NT,
-   ParseFtpLongList_EPLF,
-   ParseFtpLongList_OS2,
-   ParseFtpLongList_MacWebStar,
+FtpListParser::FtpListParserType FtpListParser::list_parsers[]={
+   &FtpListParser::ParseFtpLongList_UNIX,
+   &FtpListParser::ParseFtpLongList_NT,
+   &FtpListParser::ParseFtpLongList_EPLF,
+   &FtpListParser::ParseFtpLongList_OS2,
+   &FtpListParser::ParseFtpLongList_MacWebStar,
    0
 };
 
-FileSet *FtpListInfo::ParseFtpLongList(const FileSet &lines,int *err_ret,const char 
*tz)
+FileSet *FtpListParser::ParseList(const char * const *lines)
 {
    FileSet *result;
-   int err;
+
+   if(mode == FA::LIST) {
+      result = new FileSet;
+      for(int i=0; lines && lines[i]; ++i)
+      {
+        result->Add(new FileInfo(lines[i]));
+      }
+
+      return result;
+   }
+   assert(mode == FA::LONG_LIST);
+
+   // don't consider empty list to be valid
+   if(!lines || !lines[0])
+   {
+      err=1;
+      return 0;
+   }
    FileSet *best_result=0;
    int best_err=0x10000000;
+
+#define callMemberFunction(object,ptrToMember)  ((object).*(ptrToMember)) 
 
-   for(ListParser *parser=list_parsers; *parser; parser++)
+   for(int p = 0; list_parsers[p]; ++p)
    {
       err=0;
       result=new FileSet;
 
       for(int i=0; lines[i]; i++)
       {
-        FileInfo *fi=(*parser)(lines[i]->name,&err,tz);
+        FileInfo *fi=callMemberFunction(*this, list_parsers[p]) (lines[i]);
         if(fi)
         {
            if(!strchr(fi->name,'/'))
@@ -847,6 +858,6 @@
       if(best_err==0)
         break;   // look no further
    }
-   *err_ret=best_err;
+   err=best_err;
    return best_result;
 }
Index: FtpListInfo.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FtpListInfo.h,v
retrieving revision 1.7
diff -u -r1.7 FtpListInfo.h
--- FtpListInfo.h       2001/10/25 13:42:56     1.7
+++ FtpListInfo.h       2001/12/02 07:46:52
@@ -25,6 +25,8 @@
 
 #include "ftpclass.h"
 
+#include "FileAccess.h"
+
 class FtpListInfo : public ListInfo
 {
    enum state_t
@@ -44,13 +46,30 @@
    class FtpSplitList *slist;
 
 public:
-   static FileSet *ParseFtpLongList(const FileSet &lines,int *err,const char *tz);
-
-public:
    FtpListInfo(Ftp *session);
    virtual ~FtpListInfo();
    int Do();
    const char *Status();
+};
+
+class FtpListParser: public ListParser
+{
+   /* timezone we believe the server to be in */
+   char *tz;
+
+   typedef FileInfo * (FtpListParser::*FtpListParserType) (const char *line);
+   static FtpListParserType list_parsers[];
+
+   FileInfo *FtpListParser::ParseFtpLongList_UNIX(const char *line);
+   FileInfo *FtpListParser::ParseFtpLongList_NT(const char *line);
+   FileInfo *FtpListParser::ParseFtpLongList_EPLF(const char *line);
+   FileInfo *FtpListParser::ParseFtpLongList_OS2(const char *line);
+   FileInfo *FtpListParser::ParseFtpLongList_MacWebStar(const char *line);
+
+public:
+   FtpListParser(const char *hostname);
+   virtual ~FtpListParser();
+   virtual FileSet *ParseList(const char * const *buf);
 };
 
 #endif//FTPLISTINFO_H
Index: FtpSplitList.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FtpSplitList.cc,v
retrieving revision 1.10
diff -u -r1.10 FtpSplitList.cc
--- FtpSplitList.cc     2001/11/23 15:22:12     1.10
+++ FtpSplitList.cc     2001/12/02 07:46:52
@@ -42,12 +42,11 @@
    buf=0;
    from_cache=false;
    state=INITIAL;
-   ptr=buf;
    rate=new Speedometer();
+   result=0;
 }
 
 FtpSplitList::FtpSplitList(FileAccess *session,FA::open_mode n_mode)
-   : Glob("")
 {
    Init(session,n_mode);
 }
@@ -56,23 +55,13 @@
 {
    if(f)
       f->Close();
-   xfree(buf);
    Delete(rate);
-}
-
-void FtpSplitList::add(const char *ptr,int len)
-{
-   char *s=string_alloca(len+1);
-   memcpy(s,ptr,len);
-   s[len]=0;
-   FileInfo fi(s);
-   Glob::add(&fi);
+   xfree(buf);
+   xfree(result);
 }
 
 int   FtpSplitList::Do()
 {
-   int  res;
-   char         *nl;
    int   m=STALL;
 
    if(state==DONE)
@@ -80,6 +69,23 @@
       if(!done)
       {
         done=true;
+        
+        /* generate result */
+        split_toks(buf, inbuf, result);
+
+         /* workaround for some ftp servers:
+         * "./foo" -> "foo" and "//foo" -> "/foo".
+         * Optimization: assume that if any lines have this problem, they
+         * all have it, so we don't have to scan through our list all the time. 
+         * If this doesn't work out, remove the third else. */
+        for(int i = 0; mode == FA::LIST && result && result[i]; ++i) {
+           if(result[i][0] == '.' && result[i][1] == '/')
+              result[i] += 2;
+           else if(result[i][0] == '/' && result[i][1] == '/')
+              result[i]++;
+           else break;
+        }
+
         return MOVED;
       }
       return m;
@@ -94,7 +100,6 @@
         {
            buf=(char*)xmemdup(b,inbuf);
            from_cache=true;
-           ptr=buf;
         }
       }
       if(!from_cache)
@@ -108,15 +113,14 @@
 
    if(!from_cache)
    {
-      char tmpbuf[0x1000];
       if(f->GetRealPos()==0 && f->GetPos()>0)
       {
         f->SeekReal();
         inbuf=0;
-        ptr=buf;
-        list.Empty();
       }
-      res=f->Read(tmpbuf,sizeof(tmpbuf));
+
+      buf=(char*)xrealloc(buf,inbuf+0x1000);
+      int res=f->Read(buf+inbuf,0x1000);
       if(res==f->DO_AGAIN)
         return m;
       if(res<0)
@@ -138,34 +142,7 @@
         return MOVED;
       }
       rate->Add(res);
-      int offs=ptr-buf;
-      buf=(char*)xrealloc(buf,inbuf+res);
-      ptr=buf+offs;
-      memcpy(buf+inbuf,tmpbuf,res);
       inbuf+=res;
-   }
-
-   for( ; 0!=(nl=(char*)memchr(ptr,'\n',inbuf-(ptr-buf))); ptr=nl+1 )
-   {
-      int len=nl-ptr;
-      if(nl[-1]=='\r')
-        len--;
-
-      if(mode==FA::LIST)
-      {
-        // workaround for some ftp servers
-        if(ptr[0]=='.' && ptr[1]=='/')
-        {
-           ptr+=2;
-           len-=2;
-        }
-        if(ptr[0]=='/' && ptr[1]=='/')
-        {
-           ptr++;
-           len--;
-        }
-      }
-      add(ptr,len);
    }
 
    if(from_cache)
Index: FtpSplitList.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FtpSplitList.h,v
retrieving revision 1.3
diff -u -r1.3 FtpSplitList.h
--- FtpSplitList.h      2000/10/12 07:34:11     1.3
+++ FtpSplitList.h      2001/12/02 07:46:52
@@ -26,7 +26,7 @@
 #include "FileAccess.h"
 #include "Speedometer.h"
 
-class FtpSplitList : public Glob
+class FtpSplitList : public FileAccessOperation
 {
    enum state_t
       {
@@ -40,16 +40,16 @@
 
    int  inbuf;
    char         *buf;
-   char         *ptr;
    bool         from_cache;
 
    FileAccess  *f;
 
    void Init(FileAccess *session,FA::open_mode n_mode);
-   void add(const char *ptr,int len);
 
    Speedometer *rate;
 
+   char **result;
+
 public:
    int  Do();
    const char *Status();
@@ -60,6 +60,7 @@
 
    bool RateValid() { return rate->Valid(); }
    const char *GetRateStr() { return rate->GetStr(); }
+   const char * const *GetResult() const { return result; }
 };
 
 #endif
Index: GetFileInfo.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/GetFileInfo.cc,v
retrieving revision 1.3
diff -u -r1.3 GetFileInfo.cc
--- GetFileInfo.cc      2001/11/23 15:27:44     1.3
+++ GetFileInfo.cc      2001/12/02 07:46:52
@@ -4,6 +4,7 @@
 
 #include "GetFileInfo.h"
 #include "misc.h"
+#include "LsCache.h"
 
 GetFileInfo::GetFileInfo(FileAccess *a, const char *_dir, bool _showdir)
 {
@@ -20,10 +21,16 @@
 
    if(_showdir) tried_dir = true;
 
-   /* if it ends with a slash and we're not showing directories,
-    * don't try it as a file at all */
-   if(!_showdir && *dir && dir[strlen(dir)-1] == '/')
-      tried_file = true;
+   /* if we're not showing directories, try to skip tests we don't need */
+   if(!_showdir) switch(LsCache::IsDirectory(a,dir))
+   {
+   case 0:
+      tried_dir = true; /* it's a file */
+      break;
+   case 1:
+      tried_file = true; /* it's a dir */
+      break;
+   }
 
    assert(!tried_dir || !tried_file); /* always do at least one */
    saved_error_text=0;
@@ -64,8 +71,6 @@
       }
       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);
@@ -103,6 +108,7 @@
       /* Get a listing: */
       li=session->MakeListInfo();
       if(follow_symlinks) li->FollowSymlinks();
+      li->UseCache(use_cache);
       li->NoNeed(FileInfo::ALL_INFO); /* clear need */
       li->Need(need);
       SetExclude(path, rxc_exclude, rxc_include);
@@ -146,7 +152,7 @@
         /* 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) {
+        if(!showdir && (file->defined&file->TYPE) && 
+file->filetype==FileInfo::DIRECTORY) {
            char *buf = xasprintf("%s: %s", dir, strerror(EACCES));
            SetError(buf);
            xfree(buf);
Index: LsCache.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/LsCache.cc,v
retrieving revision 1.12
diff -u -r1.12 LsCache.cc
--- LsCache.cc  2000/03/22 14:12:48     1.12
+++ LsCache.cc  2001/12/02 07:46:52
@@ -117,7 +117,7 @@
    LsCache *scan;
    for(scan=chain; scan; scan=scan->next)
    {
-      if(scan->mode==m && !strcmp(scan->arg,a) && p_loc->SameLocationAs(scan->loc))
+      if((m == -1 || scan->mode==m) && !strcmp(scan->arg,a) && 
+p_loc->SameLocationAs(scan->loc))
       {
         if(d && l)
         {
@@ -243,3 +243,65 @@
       scan=&scan[0]->next;
    }
 }
+
+int LsCache::IsDirectory(FileAccess *p_loc,const char *dir_c)
+{
+   if(dir_c[strlen(dir_c)-1] == '/')
+      return 1;
+
+   char *origdir = xstrdup(p_loc->GetCwd());
+   
+   /* (This is cheap, so do this first.)  We know the path is a directory
+    * if we have a cache entry for it.  This is true regardless of the list
+    * type. */
+   /* TODO: or if we have a subdirectory cached; ie /foo is a dir if we
+    * know about /foo/bar */
+   p_loc->Chdir(dir_c, false);
+   int ret = Find(p_loc, "", -1, 0,0);
+   p_loc->Chdir(origdir, false);
+   if(ret)
+   {
+      xfree(origdir);
+      return 1;
+   }
+   
+   ListParser *parser = p_loc->MakeListParser();
+   if(!parser)
+   {
+      xfree(origdir);
+      return -1; /* can't parse this type like this */
+   }
+
+   /* We know this is a file or a directory if the dirname is cached and
+    * contains the basename. */
+   char *dir = xstrdup(dir_c);
+   char *sl = strrchr(dir, '/');
+   if(sl)
+   {
+      *sl = 0;
+      p_loc->Chdir(dir, false);
+   }
+   xfree(dir);
+
+   ret = -1; /* don't know */
+   const char *buf_c;
+   int bufsiz;
+   if(Find(p_loc, "", FA::LONG_LIST, &buf_c, &bufsiz))
+   {
+      char *buf = (char *) xmemdup(buf_c, bufsiz);
+      FileSet *fs = parser->ParseList(buf, bufsiz);
+      
+      FileInfo *fi=0;
+      if(fs) fi = fs->FindByName(basename_ptr(dir_c));
+      if(fi && (fi->defined&fi->TYPE))
+        ret = (fi->filetype == fi->DIRECTORY);
+      xfree(buf);
+      delete fs;
+   }
+      
+   p_loc->Chdir(origdir, false);
+   xfree(origdir);
+   delete parser;
+   return ret;
+}
+
Index: LsCache.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/LsCache.h,v
retrieving revision 1.11
diff -u -r1.11 LsCache.h
--- LsCache.h   2001/11/30 10:28:30     1.11
+++ LsCache.h   2001/12/02 07:46:52
@@ -65,6 +65,8 @@
    static void Add(FileAccess *p_loc,const char *a,int m,const Buffer *ubuf);
    static int Find(FileAccess *p_loc,const char *a,int m,const char **d, int *l);
 
+   static int IsDirectory(FileAccess *p_loc,const char *dir);
+
    enum change_mode { FILE_CHANGED, DIR_CHANGED, TREE_CHANGED };
    static void Changed(change_mode m,FileAccess *f,const char *what);
    static void FileChanged(FileAccess *f,const char *file)
Index: ftpclass.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ftpclass.cc,v
retrieving revision 1.220
diff -u -r1.220 ftpclass.cc
--- ftpclass.cc 2001/11/30 16:22:17     1.220
+++ ftpclass.cc 2001/12/02 07:46:55
@@ -2139,6 +2139,8 @@
       return 3;
    if(code==250 && mode==CHANGE_DIR)
       return 3;
+   if(code==451 && mode==CLOSED && !QueryBool("use-abor",hostname))
+      return 4;
    if(code==550 && mode==ARRAY_INFO
    && !RespQueueIsEmpty() && RespQueue[RQ_head].check_case==CHECK_MDTM)
       return 4;
@@ -3827,6 +3829,10 @@
 DirList *Ftp::MakeDirList(ArgV *args)
 {
    return new FtpDirList(args,this);
+}
+ListParser *Ftp::MakeListParser()
+{
+   return new FtpListParser(hostname);
 }
 
 
Index: ftpclass.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ftpclass.h,v
retrieving revision 1.82
diff -u -r1.82 ftpclass.h
--- ftpclass.h  2001/11/21 10:00:00     1.82
+++ ftpclass.h  2001/12/02 07:46:56
@@ -373,6 +373,7 @@
    ListInfo *MakeListInfo();
    Glob *MakeGlob(const char *pattern);
    DirList *MakeDirList(ArgV *args);
+   ListParser *MakeListParser();
 
    void SetCopyMode(copy_mode_t cm,bool rp,int rnum,time_t tt)
       {
Index: misc.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.cc,v
retrieving revision 1.44
diff -u -r1.44 misc.cc
--- misc.cc     2001/11/26 14:20:02     1.44
+++ misc.cc     2001/12/02 07:46:56
@@ -727,7 +727,7 @@
    {
       ret = (char *) xrealloc(ret, siz);
       va_list tmp;
-      VA_COPY(tmp,ap);
+      va_copy(tmp,ap);
       int res=vsnprintf(ret, siz, format, tmp);
       va_end(tmp);
       if(res>=0 && res<siz)
@@ -748,3 +748,51 @@
    va_end(va);
    return ret;
 }
+
+int split_toks(char *buf, int bufsiz, char ** &array, char sep)
+{
+   char *bufend = buf+bufsiz;
+
+   array=0;
+   if(!buf)
+      return 0;
+
+   /* generate result */
+   int line = 0;
+   char *bufptr = buf;
+   int alloced = 0;
+
+   while(char *p = (char *) memchr(bufptr, '\n', bufend-bufptr))
+   {
+      if(line >= alloced)
+      {
+        if(!alloced) alloced=8;
+        else alloced *= 2;
+        /* +1 for null terminator */
+        array=(char **) xrealloc(array, sizeof(char *) * alloced);
+      }
+
+      if(bufptr == p)
+      {
+        /* blank line? */
+        bufptr++;
+        continue;
+      }
+
+      *p = 0;
+      if(sep == '\n' && p[-1] == '\r')
+        p[-1] = 0;
+
+      array[line++] = bufptr;
+      bufptr = p+1;
+   }
+
+   if(line >= alloced)
+   {
+      alloced++;
+      array=(char **) xrealloc(array, sizeof(char *) * alloced);
+   }
+   array[line]=0;
+   return line;
+}
+
Index: misc.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.h,v
retrieving revision 1.28
diff -u -r1.28 misc.h
--- misc.h      2001/11/26 14:20:02     1.28
+++ misc.h      2001/12/02 07:46:56
@@ -109,4 +109,11 @@
 char *xvasprintf(const char *format, va_list ap);
 char *xasprintf(const char *format, ...);
 
+/* split a buffer in-place into an array; if sep == \n, \r\n is dealt
+ * with also.  array must be freed by caller.
+ *
+ * if the last line does not end with a newline, it will not be in the
+ * array (we won't have any space to null terminate it.) */
+int split_toks(char *buf, int bufsiz, char ** &array, char sep = '\n');
+       
 #endif // MISC_H

Reply via email to