WvStreams
wvdiriter.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *
00005  * Directory iterator.  Recursively uses opendir and readdir, so you don't
00006  * have to.  Basically implements 'find'.
00007  *
00008  */
00009 
00010 #include "wvdiriter.h"
00011 
00012 #if defined(_WIN32) && !defined(S_ISDIR)
00013 #define S_ISDIR(x) (_S_IFDIR | (x))
00014 #endif
00015 #ifdef _WIN32
00016 #define lstat stat
00017 #endif
00018 
00019 WvDirIter::WvDirIter( WvStringParm _dirname,
00020                       bool _recurse, bool _skip_mounts, size_t sizeof_stat )
00021     : relpath(""), dir(dirs)
00022 /****************************************************************************/
00023 {
00024     // if this assertion fails, then you probably used different compiler
00025     // options for the wvstreams library and the calling program.  Check
00026     // for defines like _FILE_OFFSET_BITS=64 and _LARGEFILE_SOURCE.
00027     assert(sizeof_stat == sizeof(struct stat));
00028     
00029     recurse = _recurse;
00030     go_up   = false;
00031     skip_mounts = _skip_mounts;
00032     found_top = false;
00033 
00034     WvString dirname(_dirname);
00035     int dl = strlen(dirname);
00036     if (dl != 0 && dirname[dl-1] == '/')
00037         dirname.edit()[dl-1] = 0;
00038 
00039     DIR * d = opendir( dirname );
00040     if( d ) {
00041         Dir * dd = new Dir( d, dirname );
00042         dirs.prepend( dd, true );
00043     }
00044 }
00045 
00046 WvDirIter::~WvDirIter()
00047 /*********************/
00048 {
00049     dirs.zap();
00050 }
00051 
00052 bool WvDirIter::isok() const
00053 /**************************/
00054 {
00055     return( !dirs.isempty() );
00056 }
00057 
00058 bool WvDirIter::isdir() const
00059 /***************************/
00060 {
00061     return( S_ISDIR( info.st_mode ) );
00062 }
00063 
00064 void WvDirIter::rewind()
00065 /**********************/
00066 {
00067     // have to closedir() everything that isn't the one we started with,
00068     // and rewind that.
00069     while( dirs.count() > 1 ) {
00070         dir.rewind();
00071         dir.next();
00072         dir.unlink();
00073     }
00074 
00075     if( isok() ) {
00076         dir.rewind();
00077         dir.next();
00078         rewinddir( dir->d );
00079     }
00080 }
00081 
00082 
00083 bool WvDirIter::next()
00084 /********************/
00085 // use readdir... and if that returns a directory, opendir() it and prepend
00086 // it to dirs, so we start reading it until it's done.
00087 {
00088     struct dirent * dent = NULL;
00089 
00090     if( !isok() )
00091         return( false );
00092 
00093     bool tryagain;
00094     do {
00095         bool ok = false;
00096         tryagain = false;
00097 
00098         // unrecurse if the user wants to
00099         if( go_up ) {
00100             go_up = false;
00101             if( dirs.count() > 1 ) {
00102                 dir.unlink();
00103                 dir.rewind();
00104                 dir.next();
00105             } else
00106                 return( false );
00107         }
00108 
00109         do {
00110             dent = readdir( dir->d ); 
00111             if( dent ) { 
00112                 info.fullname = WvString( "%s/%s", dir->dirname, dent->d_name );
00113                 info.name = dent->d_name;
00114 
00115                 if (relpath == "")
00116                     info.relname = info.name;
00117                 else
00118                     info.relname = WvString("%s%s", relpath, info.name);
00119 
00120                 ok = ( lstat( info.fullname, &info ) == 0
00121                             && strcmp( dent->d_name, "." )
00122                             && strcmp( dent->d_name, ".." ) );
00123 
00124                 if (ok && !found_top)
00125                 {
00126                     lstat(info.fullname, &topdir);
00127                     topdir.fullname = info.fullname;
00128                     topdir.name = info.name;
00129                     topdir.relname = info.relname;
00130                     found_top = true;
00131                 }
00132             }
00133         } while( dent && !ok );
00134 
00135         if( dent ) {
00136             // recurse?
00137             if( recurse && S_ISDIR( info.st_mode ) &&
00138                     ( !skip_mounts || info.st_dev == topdir.st_dev) ) {
00139                 DIR * d = opendir( info.fullname );
00140                 if( d ) {
00141                     relpath = WvString( "%s%s/", relpath, info.name );
00142                     Dir * dd = new Dir( d, info.fullname );
00143                     dirs.prepend( dd, true );
00144                     dir.rewind();
00145                     dir.next();
00146                 }
00147             }
00148         } else {
00149             // end of directory.  if we recursed, unlink it and go up a 
00150             // notch.  if this is the top level, DON'T close it, so that
00151             // the user can ::rewind() again if he wants.
00152             if( dirs.count() > 1 ) {
00153                 if (dirs.count() == 2)
00154                     relpath = WvString("");
00155                 else
00156                     relpath = WvString( "%s/", getdirname(relpath) );
00157 
00158                 dir.unlink();
00159                 dir.rewind();
00160                 dir.next();
00161                 tryagain = true;
00162             }
00163         }
00164     } while( tryagain );
00165 
00166     return( dent != NULL );
00167 }
00168