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 . |
(C) 2000-2002 |