Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

Directory.cpp

Go to the documentation of this file.
00001 
00002 //
00003 // Copyright (C) 2000
00004 // Ralf Westram
00005 // Time-stamp: <Thu Dec/26/2002 01:47 MET Coder@ReallySoft.de>
00006 //
00007 // Permission to use, copy, modify, distribute and sell this software
00008 // and its documentation for any purpose is hereby granted without fee,
00009 // provided that the above copyright notice appear in all copies and
00010 // that both that copyright notice and this permission notice appear
00011 // in supporting documentation.  Ralf Westram makes no
00012 // representations about the suitability of this software for any
00013 // purpose.  It is provided "as is" without express or implied warranty.
00014 //
00015 // This code is part of my library.
00016 // You may find a more recent version at http://www.reallysoft.de/
00017 //
00019 
00020 #define FILELIST_KENNUNG "RS_FILELIST"
00021 #define FILELIST_VERSION 1
00022 
00023 #include "Directory.h"
00024 
00025 #include <dirent.h>
00026 #include <unistd.h>
00027 #include <list>
00028 
00029 #include <Tools.h>
00030 #include <sys_dep.h>
00031 #include <read_lnk.h>
00032 
00033 #ifdef DOS
00034 #include <sys/cygwin.h>
00035 // #include <windef.h>
00036 // #define MAX_PATH 256 (using PATH_MAX instead)
00037 #else
00038 // #include <stdlib.h>
00039 #endif
00040 
00041 
00042 using namespace std;
00043 using namespace rs;
00044 using namespace rs::str;
00045 using namespace rs::err;
00046 using namespace rs::file;
00047 using namespace rs::posix;
00048 
00049 inline time_t min(time_t t1, time_t t2) { return difftime(t1, t2)<0 ? t1 : t2; }
00050 inline time_t max(time_t t1, time_t t2) { return difftime(t1, t2)>0 ? t1 : t2; }
00051 
00052 // start of implementation of class Directory:
00053 
00054 // -----------------------------------------------------------------
00055 //      string Directory::fullPath(const string& filename) const
00056 // -----------------------------------------------------------------
00057 string Directory::fullPath(const string& filename) const
00058 {
00059     if (filename.rfind(DIR_SEPARATOR) == string::npos) { // no DIR_SEPARATOR
00060         return catDF(name(), filename);
00061     }
00062     return filename; // if filename already contains a path (happens i.e. with follow_links (see scanDir))
00063 }
00064 
00065 //  ------------------------------------------------------------------
00066 //      void Directory::addFile(const string& filename, time_t t)
00067 //  ------------------------------------------------------------------
00068 void Directory::addFile(const string& filename, time_t t) {
00069 
00070 //     if (filename.rfind(DIR_SEPARATOR) == string::npos); // don't insert paths
00071 
00072     if (files.empty()) {
00073         oldest_ = newest_ = t;
00074     }
00075     else {
00076         oldest_ = min(oldest_, t);
00077         newest_ = max(newest_, t);
00078     }
00079     files.insert(filename);
00080 }
00081 
00082 //  ----------------------------------------------------
00083 //      void Directory::save(BinaryFile& out) const
00084 //  ----------------------------------------------------
00085 void Directory::save(BinaryFile& out) const {
00086     out.put_string(name_);
00087     out.put_nat(files.size());
00088     for (set<string>::const_iterator f=files.begin(); f != files.end(); ++f) {
00089         out.put_string(*f);
00090     }
00091     put_binary(oldest_, out);
00092     put_binary(newest_, out);
00093 }
00094 //  --------------------------------------------------------------
00095 //      void Directory::load(BinaryFile& in, int /*version*/)
00096 //  --------------------------------------------------------------
00097 void Directory::load(BinaryFile& in, int /*version*/) {
00098     in.get_string(name_);
00099     unsigned long count;
00100     in.get_nat(count);
00101     files.clear();
00102     while (count--) {
00103         string s;
00104         in.get_string(s);
00105         files.insert(s);
00106     }
00107     get_binary(oldest_, in);
00108     get_binary(newest_, in);
00109 }
00110 
00111 // -end- of implementation of class Directory.
00112 
00113 namespace rs {
00114     namespace file {
00115 
00116 #ifdef DOS
00117         char DIR_SEPARATOR = '\\';
00118 #else
00119         char DIR_SEPARATOR = '/';
00120 #endif
00121 
00122         //  ----------------------------------------------------------------------
00123         //      string catDF(const string& directory, const string& filename)
00124         //  ----------------------------------------------------------------------
00125         string catDF(const string& directory, const string& filename) {
00126             return directory+DIR_SEPARATOR+filename;
00127             //             return directory+'/'+filename;
00128         }
00129         // ---------------------------------------------------
00130         //      void scanDir_internal(Directories&   dirs,
00131         // ---------------------------------------------------
00132         void scanDir_internal(Directories&   dirs,
00133                               const string&  rootdir,
00134                               bool           recurse,
00135                               bool           follow_links,
00136                               predicate      takeFile,
00137                               predicate      recurseDirectory,
00138                               ostream       *display,
00139                               set<string>&   visited_directories)
00140             // @@@ FIXME: all scanned directories should be inserted into visited_directories
00141             // and then repeated scanning should be avoided
00142         {
00143             Directory& dir = dirs[rootdir];
00144             dir.setName(rootdir);
00145 
00146             if (display) *display << rootdir << endl;
00147 
00148             DIR          *d = opendir(rootdir.c_str());
00149             list<string>  subdirs;
00150             list<string>  linked_dirs;
00151 
00152             if (!d) throw IOError(strf("opendir '%s' failed", rootdir.c_str()));
00153             while (1) {
00154                 struct dirent *entry = readdir(d);
00155                 if (!entry) break;
00156 
00157                 const char *name = entry->d_name;
00158                 if (name[0]=='.' && (name[1]==0 || (name[1]=='.' && name[2]==0))) continue; // '.' und '..'
00159 
00160                 struct stat st;
00161                 string      fullname = catDF(rootdir, entry->d_name);
00162 
00163                 if (::stat(fullname.c_str(), &st) != 0) throw IOError(fullname, "stat failed");
00164 
00165                 string linktarget;
00166                 if (follow_links && isLink(fullname, linktarget)) {
00167                     struct stat st_target;
00168                     if (::stat(linktarget.c_str(), &st_target) != 0) {
00169                         throw IOError(linktarget, string("stat to target of '")+fullname+"' failed");
00170                     }
00171 
00172                     if (S_ISDIR(st_target.st_mode)) {
00173                         if (recurseDirectory(extractDirectory(linktarget).c_str(), extractFilename(linktarget).c_str(), st_target)) {
00174                             linked_dirs.push_back(linktarget);
00175                         }
00176                     }
00177                     else if (takeFile(extractDirectory(linktarget).c_str(), extractFilename(linktarget).c_str(), st_target)) {
00178                         dir.addFile(linktarget, st_target.st_mtime);
00179                     }
00180                 }
00181                 else if (S_ISDIR(st.st_mode)) {
00182                     if (recurseDirectory(rootdir, name, st)) {
00183                         subdirs.push_back(entry->d_name);
00184                     }
00185                 }
00186                 else if (takeFile(rootdir, name, st)) {
00187                     dir.addFile(name, st.st_mtime);
00188                 }
00189             }
00190 
00191             closedir(d);
00192 
00193             if (!subdirs.empty() && recurse) {
00194                 for (list<string>::const_iterator s=subdirs.begin(); s != subdirs.end(); ++s) {
00195                     scanDir(dirs, catDF(rootdir, *s), recurse, follow_links, takeFile, recurseDirectory, display);
00196                 }
00197             }
00198         }
00199 
00200         void scanDir(Directories&   dirs,
00201                      const string&  rootdir,
00202                      bool           recurse,
00203                      bool           follow_links,
00204                      predicate      takeFile,
00205                      predicate      recurseDirectory,
00206                      ostream       *display)
00207         {
00208             set<string> visited_directories;
00209             scanDir_internal(dirs, rootdir, recurse, follow_links, takeFile, recurseDirectory, display, visited_directories);
00210         }
00211 
00212         //  ---------------------------------------------------------------------------------------------
00213         //      static bool recurseAnyDirectory(const char */*filename*/, const struct stat& /*st*/)
00214         //  ---------------------------------------------------------------------------------------------
00215         static bool recurseAnyDirectory(const string& /*dir*/, const char */*filename*/, const struct stat& /*st*/) {
00216             return true;
00217         }
00218 
00219         //  -----------------------------------------
00220         //      void scanDir(Directories&   dirs,
00221         //  -----------------------------------------
00222         void scanDir(Directories&   dirs,
00223                      const string&  rootdir,
00224                      bool           recurse,
00225                      bool           follow_links,
00226                      predicate      takeFile,
00227                      ostream       *display)
00228         {
00229             scanDir(dirs, rootdir, recurse, follow_links, takeFile, recurseAnyDirectory, display);
00230         }
00231         //  -----------------------------------------------------------------------
00232         //      void saveDirectories(const Directories& dirs, BinaryFile& out)
00233         //  -----------------------------------------------------------------------
00234         void saveDirectories(const Directories& dirs, BinaryFile& out) {
00235             out.put_string(FILELIST_KENNUNG);
00236             out.put_int(FILELIST_VERSION);
00237 
00238             out.put_nat(dirs.size());
00239 
00240             for (Directories::const_iterator d=dirs.begin(); d != dirs.end(); ++d) {
00241 
00242                 const Directory& dir = d->second;
00243                 dir.save(out);
00244             }
00245         }
00246 
00247         //  ----------------------------------------------------------------
00248         //      void loadDirectories(Directories& dirs, BinaryFile& in)
00249         //  ----------------------------------------------------------------
00250         void loadDirectories(Directories& dirs, BinaryFile& in) {
00251             string kennung;
00252             int version;
00253 
00254             in.get_string(kennung);
00255             if (kennung!=FILELIST_KENNUNG) throw Error(strf("Illegal filetype ('%s')", in.name().c_str()));
00256 
00257             in.get_int(version);
00258 
00259             unsigned long dcount;
00260             in.get_nat(dcount);
00261             while (dcount--) {
00262                 Directory d;
00263                 d.load(in, version);
00264                 dirs[d.name()] = d;
00265             }
00266         }
00267 
00268 
00269         // -----------------------------------------------------
00270         //      string canonicalize(const string& filename)
00271         // -----------------------------------------------------
00272         string canonicalize(const string& filename, bool to_win32) {
00273 //             string current          = getcwd();
00274 //             string dir              = extractDirectory(filename);
00275 //             if (chdir(dir.c_str()) != 0) throw IOError(filename, "canonicalize");
00276 
00277 //             string file_dir             = getcwd();
00278 //             if (chdir(current.c_str()) != 0) throw IOError(current, "canonicalize");
00279 
00280             //             string result = catDF(file_dir, extractFilename(filename));
00281 
00282             char buf[PATH_MAX+1];
00283 
00284             // @@@ FIXME: maybe canonicalize should have a second parameter specifying
00285             // whether to create a posix-path or a windows-path ?
00286 
00287 #ifdef DOS
00288             if (to_win32) {
00289                 cygwin_conv_to_full_win32_path(filename.c_str(), buf);
00290             }
00291             else {
00292                 cygwin_conv_to_full_posix_path(filename.c_str(), buf);
00293             }
00294 #else                           // LINUX
00295             // #error here a linux equivalent for cygwin_conv_to_full_win32_path is missing here.. sorry!
00296             if (to_win32) throw Error("canonicalize() does not support conversion to win32-pathname under Linux");
00297             realpath(filename.c_str(), buf);
00298 #endif
00299 
00300             assert(buf[0] != 0); // otherwise it couldnt be converted
00301             string result = buf;
00302 
00303 #if defined(DEBUG) && 0
00304             cout << "canonicalize(" << filename << ", " << to_win32 << ")='" << result << "'\n";
00305 #endif // DEBUG
00306             return result;
00307         }
00308 
00309         // -----------------------------------------------------------------
00310         //      inline size_t lastPathSeperator(const string& filename)
00311         // -----------------------------------------------------------------
00312         inline size_t lastPathSeperator(const string& filename) {
00313             return filename.find_last_of("/\\");
00314         }
00315 
00316         // ---------------------------------------------------------
00317         //      string extractDirectory(const string& fullpath)
00318         // ---------------------------------------------------------
00319         string extractDirectory(const string& fullpath) {
00320             size_t slash  = lastPathSeperator(fullpath);
00321             string result = (slash == string::npos) ? "" : fullpath.substr(0, slash);
00322 #if defined(DEBUG) && 0
00323             cout << "extractDirectory(" << fullpath << ")='" << result << "'\n";
00324 #endif // DEBUG
00325             return result;
00326         }
00327 
00328         // --------------------------------------------------------
00329         //      string extractFilename(const string& fullpath)
00330         // --------------------------------------------------------
00331         string extractFilename(const string& fullpath) {
00332             size_t slash  = lastPathSeperator(fullpath);
00333             string result = (slash == string::npos) ? fullpath : fullpath.substr(slash+1);
00334 #if defined(DEBUG) && 0
00335             cout << "extractFilename(" << fullpath << ")='" << result << "'\n";
00336 #endif // DEBUG
00337             return result;
00338         }
00339 #define BUFFERSIZE (PATH_MAX+1)
00340 
00341         // ----------------------------------------------------
00342         //      void backslashes2slashes(string& filename)
00343         // ----------------------------------------------------
00344         void backslashes2slashes(string& filename) {
00345             for (unsigned i=0; i<filename.length(); ++i) {
00346                 if (filename[i]=='\\') {
00347                     filename[i] = '/';
00348                 }
00349             }
00350         }
00351 
00352         // ---------------------------------------------------------
00353         //      bool isLink(const string& file, string& target)
00354         // ---------------------------------------------------------
00355         bool isLink(const string& file, string& target) {
00356             char buffer[BUFFERSIZE];
00357             buffer[0] = 0;
00358 
00359             bool   is_a_link = false;
00360             size_t lnk_pos   = file.find(".lnk");
00361 
00362             if (lnk_pos == file.length()-4) { // test for windows .lnk file
00363                 Lnk lnk(file);
00364 
00365                 if (lnk.points_to_file_or_directory()) {
00366                     target    = lnk.get_target();
00367                     is_a_link = true;
00368                 }
00369             }
00370 
00371             if (!is_a_link) {
00372                 // readlink does only work with unix
00373                 // or with *.lnk files created using cygwin
00374                 // (it does not work with 'real' windows links)
00375 
00376                 string unixpath = canonicalize(file, false);
00377                 int    result   = readlink(unixpath.c_str(), buffer, BUFFERSIZE);
00378                 if (result != -1) {
00379                     buffer[result] = 0;
00380                     target         = canonicalize(buffer);
00381                     is_a_link      = true;
00382                 }
00383 #if defined(DEBUG) && 0
00384                 else {
00385                     IOError err(unixpath, "readlink");
00386                     err.print(cerr);
00387                 }
00388 #endif // DEBUG
00389             }
00390 
00391             return is_a_link;
00392         }
00393 
00394     };
00395 };
00396 

Contact me in case of errors or questions.
This documentation is powered by Doxygen.
(C) 2000-2002 Doxygen