dirstream.h

00001 
00002 //      file            :       dirstream.h
00003 //  copyright   :       (C) 2002-2004 Benjamin Kaufmann
00004 //  email               :       hume@c-plusplus.de
00005 //      internet        :       http://bens.c-plusplus.info/
00006 //
00007 //  Definition der Klassen dirstream und dirstream_iterator sowie
00008 //  einiger Filter und Hilfsfunktionen.
00009 //
00010 //  Hinweis:
00011 //      Auf Win32-Platformen verwendet dirstream Wrapper-Funktionen
00012 //      von Kevlin Henney. Dieser Wrapper basieren auf den Funktionen _findfirst,
00013 //      _findnext und _findclose.
00014 //      http://www.two-sdg.demon.co.uk/curbralan/code/dirent/dirent.html
00015 //
00016 //      Die ursprüngliche Idee für diesen dirstream stammt aus dem Artikel
00017 //      "Promoting Polymorphism" von Kevlin Henney.
00018 //      http://www.appdevadvisor.co.uk/Downloads/ada5_8/Henney5_8.pdf
00019 //
00021 //
00022 /****************************************************************************
00023  *                                                                              *
00024  *      This program is free software; you can redistribute it and/or modify    *
00025  *      it under the terms of the GNU General Public License as published by    *
00026  *      the Free Software Foundation; either version 2 of the License, or       *
00027  *      (at your option) any later version.                                     *
00028  *                                                                                                                                                      *
00029  *      This program is distributed in the hope that it will be useful,                 *
00030  *      but WITHOUT ANY WARRANTY; without even the implied warranty of                  *
00031  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                   *
00032  *      GNU General Public License for more details.                            *
00033  *                                                                                                                                                      *
00034  ****************************************************************************/
00035 #ifndef DIR_STREAM_H_INCLUDED
00036 #define DIR_STREAM_H_INCLUDED
00037 #include <cctype>
00038 #include <string>
00039 #if defined _MSC_VER && _MSC_VER <= 1300
00040 #       define for if(0);else for
00041 #       pragma warning(disable:4786)
00042 #       define HAS_STD_ITERATOR
00043 namespace std {
00044         using ::tolower;
00045         using ::size_t;
00046 }
00047 #endif
00048 
00049 #include <stack>
00050 #include <iterator>
00051 #include <algorithm>
00052 #include <cassert>
00053 #include <stdexcept>
00054 #include <memory>
00055 #include <utility>
00056 
00057 #include "filter_base.h"
00058 #include "detail/filter_asserter.h"
00060 // Platform-spezifischer Code 
00062 #ifdef _WIN32
00063 #include "dirstreamw32.h"
00064 #else
00065 // Wenn vorhanden, POSIX Funktionen verwenden.
00066 #include <dirent.h>
00067 #include <sys/types.h>
00068 #include <sys/stat.h>
00069 namespace dirstr
00070 {
00071         const char PATH_SEPERATOR = '/';
00072 }
00073 #endif
00075 namespace dirstr
00076 {
00077 
00082 enum entry_type{
00083         dirs = 1,                                               
00084         dirs_not_dot_or_dot_dot = 2,    
00085         files = 4,                                              
00086         files_and_dirs = dirs | files,  
00087         files_and_dirs_not_dot_or_dot_dot = dirs_not_dot_or_dot_dot | files 
00088 };
00089 
00094 enum recurse_mode {
00095         recursive_yes,  
00096         recursive_no    
00097 };
00098 
00104 class open_error : public std::runtime_error
00105 {
00106 public:
00107         open_error(const char* dir)
00108                 : std::runtime_error(dir)
00109         {}
00110 };
00111 
00116 struct no_f : public filter_base
00117 {
00118 public:
00119         bool operator()(const std::string&) const {return true;}
00120         filter_base* clone() const {return new no_f();}
00121 };
00122 
00123 template <class F> struct expr;
00125 // class dirstream
00127 
00154 class dirstream
00155 {
00156 typedef filter_base* (std::auto_ptr<filter_base>::*unspecified_bool_type)() const;
00157 public:
00178         dirstream               (const char* dirName, const filter_base& entryFilter = no_f(),
00179                                          recurse_mode recMode = recursive_no, const filter_base& subDirFilter = no_f());
00180                 
00187         ~dirstream              ();
00188 
00194         operator unspecified_bool_type() const
00195         {
00196                 return m_DirHandle ? &std::auto_ptr<filter_base>::get : 0;
00197         }
00198     
00214         dirstream& operator>>(std::string& name);
00215 
00228         std::streamsize readsome(std::string* r, std::streamsize n);
00229 
00235         bool open(const char* dirName, const filter_base& entryFilter,
00236                           recurse_mode recMode = recursive_no, const filter_base& subDirFilter = no_f());
00237         
00245         bool open(const char* dirName, recurse_mode recMode = recursive_no);
00246         
00250         bool is_open            ()      const;
00251 
00260         void rewind                     ();
00261         
00271         void close();
00272 private:
00273         // Streamobjekte können nicht kopiert werden, da diese
00274         // Operation keinen Sinn macht.
00275         dirstream(const dirstream&);
00276         dirstream& operator=(const dirstream&);
00277         
00278         bool isDirectory(const char*) const;
00279         bool selectSubDir(const std::string& name) const;
00280         bool selectEntry(const std::string& name) const;
00281         void closeDir();
00282     void commonInit();
00283     bool dotOrDotDot(const std::string& name) const;
00284     
00285         typedef std::auto_ptr<filter_base> Filter;
00286         Filter                                  m_EntryFilter;
00287         Filter                                  m_SubDirFilter;
00288         recurse_mode                    m_RecurseMode;
00289         std::stack<std::string> m_Dirs;
00290         std::string                             m_DirName;
00291     std::string             m_CurrentPath;
00292         DIR*                                    m_DirHandle;
00293         dirent*                                 m_CurrentEntry;
00294 };
00295 
00310 class dirstream_iterator
00311 #ifdef HAS_STD_ITERATOR
00312         : public std::iterator<std::input_iterator_tag, std::string>
00313 #endif
00314 {
00315 public:
00316 // Wer std::iterator nicht hat, muss selbst für die benötigten
00317 // typedefs sorgen.
00318 #ifndef HAS_STD_ITERATOR
00319 typedef std::input_iterator_tag         iterator_category;
00320 typedef std::string                                     value_type;
00321 typedef ptrdiff_t                                       difference_type;
00322 typedef std::string*                            pointer;
00323 typedef std::string&                            reference;
00324 #endif
00325 public:
00326         typedef dirstream stream_type;
00330         dirstream_iterator() 
00331                 : dstr_(0)
00332         {}
00333         
00341         explicit dirstream_iterator(dirstream& dstr)
00342                 : dstr_(&dstr)
00343         {
00344                 readOne();
00345         }
00346         
00351         const std::string& operator*() const
00352         {
00353                 return currVal_;
00354         }
00355 
00360         const std::string* operator->() const
00361         {
00362                 return &**this; 
00363         }
00364 
00371         dirstream_iterator& operator++()
00372         {
00373                 readOne();
00374                 return *this;   
00375         }
00376 
00381         dirstream_iterator operator++(int)
00382         {
00383                 dirstream_iterator t(*this);
00384                 ++*this;
00385                 return t;       
00386         }
00387         friend bool operator==(const dirstream_iterator& lhs, const dirstream_iterator& rhs);
00388         friend bool operator!=(const dirstream_iterator& lhs, const dirstream_iterator& rhs);
00389 private:
00390         void readOne()
00391         {
00392                 assert(dstr_);
00393                 if ( !(*dstr_ >> currVal_) )
00394                         dstr_ = 0;
00395         }
00396         dirstream* dstr_;
00397         std::string currVal_;
00398 };
00399 
00404 inline bool operator==(const dirstream_iterator& lhs, const dirstream_iterator& rhs)
00405 {
00406         return lhs.dstr_ == rhs.dstr_;
00407 }
00408 
00419 inline bool operator!=(const dirstream_iterator& lhs, const dirstream_iterator& rhs)
00420 {
00421         return !(lhs == rhs);
00422 }
00423 
00424 
00426 // Ein paar nützliche Funktionen
00428 
00438 bool is_directory(const std::string& entry);
00439 
00448 std::string full_path(const std::string& entry);
00449 
00457 bool create_directory(const std::string& path);
00458 
00466 bool directory_exists(const std::string& path);
00467 
00469 // Standard-Filter
00471 
00519 #define ADD_CLONE_IMPL(Name) \
00520 filter_base* clone() const {return new Name(*this);}
00521 
00522 
00528 struct type_f : public filter_base {
00533         type_f(entry_type t)
00534                 : t_(t)
00535         {}
00536         
00540         bool operator()(const std::string& entryName) const
00541         {
00542                 bool isDir = is_directory(entryName.c_str());
00543                 if ((t_ & dirs) && isDir)
00544                         return true;
00545                 if (!isDir && (t_ & files))
00546                         return true;
00547                 if (isDir && (t_ & dirs_not_dot_or_dot_dot))
00548                 {
00549                         std::string::size_type pathEnd = entryName.rfind(PATH_SEPERATOR);
00550                         if (pathEnd == std::string::npos)
00551                                 pathEnd = 0;
00552                         else 
00553                                 ++pathEnd;
00554                         return  entryName.compare(pathEnd, std::string::npos, ".") != 0 &&
00555                                         entryName.compare(pathEnd, std::string::npos, "..") != 0;
00556                 }
00557                 return false;
00558         }               
00559         ADD_CLONE_IMPL(type_f);
00560 private:
00561         entry_type t_;
00562 };
00563 
00564 
00611 struct pattern_f : public filter_base
00612 {
00617         class invalid_pattern : public std::logic_error
00618         {
00619         public:
00620                 invalid_pattern(const char* msg) : std::logic_error(msg)
00621                 {}
00622         };
00623         
00640         explicit pattern_f(const char* pattern, 
00641                                                    entry_type types = files_and_dirs_not_dot_or_dot_dot, 
00642                                                    bool checkPath = false, 
00643                                                    bool ignoreCase = false
00644                                                    )
00645                 : pattern_(simplify(pattern))
00646                 , filter_(types)
00647                 , ignoreCase_(ignoreCase)
00648                 , checkPath_(checkPath)
00649         {}
00650         
00655         bool operator()(const std::string& entryName) const
00656         {
00657                 const char* fname = getName(entryName);
00658                 if (!filter_(entryName)) return false;
00659                 if (entryName.empty()) return pattern_ == "*" || pattern_.empty();
00660                 return match(fname, pattern_.c_str());
00661         }
00662         ADD_CLONE_IMPL(pattern_f);
00663 private:
00664         // @pre pattern does not contain consecutive '*'s.
00665         bool match(const char* str, const char* pattern) const
00666         {
00667                 const char* strRun = 0;
00668                 const char* patternRun = 0;
00669                 bool starSeen = false;
00670                 bool end = false;
00671                 do {
00672                         for (strRun = str, patternRun = pattern; !(end = *strRun == 0); ++strRun, ++patternRun) 
00673                         {
00674                                 if (*patternRun == '*')
00675                                 {       // conceptually a star starts a new segment
00676                                         // we now have to match the new segment against the
00677                                         // rest of the string...
00678                                         starSeen = true;
00679                                         str = strRun;
00680                                         pattern = patternRun;
00681                                         // ...unless the '*' is the last char of pattern because
00682                                         // this means "match everything"
00683                                         if (!*++pattern) return true;
00684                                         break;
00685                                 }
00686                                 else if (*patternRun == '[')
00687                                 {       // match set
00688                                         ++patternRun;
00689                                         bool first = true;              // allow ] as first char of set - e.g []abc]
00690                                         bool found = false;
00691                                         while (*patternRun != ']' || first)
00692                                         {
00693                                                 if (!found && *patternRun == *strRun)
00694                                                         found = true;
00695                                                 first = false;
00696                                                 ++patternRun;
00697                                         }
00698                                         if (!found)
00699                                         {
00700                                                 if (!starSeen) return false;
00701                                                 ++str;
00702                                                 break;
00703                                         }
00704                                 }
00705                                 else if (!equal(*patternRun, *strRun) && *patternRun != '?')
00706                                 {       // mismatch...
00707                                         if (!starSeen) return false;
00708                                         // ...but since the current segment started with a star
00709                                         // we match this char against the star and then continue our search 
00710                                         // 
00711                                         ++str;  
00712                                         break;
00713                                 }
00714                         }
00715                         if (end)
00716                         {
00717                                 if (*patternRun == '*') ++patternRun;
00718                                 return (!*patternRun);
00719                         }
00720                 } while(!end);
00721                 return false;
00722         }
00723         bool equal(char c1, char c2) const 
00724         {
00725                 return ignoreCase_ ? 
00726                         std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2))
00727                         : c1 == c2;
00728         }
00729         
00730         // simplifies a pattern containing the wild cards * and ?
00731         // by replacing '**' with '*' and '*?' with '?*'.
00732         // Example: "a****?b*?*?c*?***" -> "a?*b??*c?*"
00733         std::string simplify(const char* pattern)
00734         {
00735                 if (!pattern || !*pattern) return "";
00736                 checkPattern(pattern);
00737                 const char* cur = pattern;
00738                 const char* next = cur + 1;
00739                 bool starPending = false;
00740                 std::string ret;
00741                 ret.reserve(16);
00742                 while (*cur)
00743                 {
00744                         if (*cur == '*' || starPending)
00745                         {
00746                                 while (*next == '*') ++next;    // treat consecutive '*' as one
00747                                 if (*next == '?')                               // handle '*?'
00748                                 {
00749                                         ++cur;
00750                                         ++next;
00751                                         ret += '?';                                     // we don't write the * here to
00752                                         starPending = true;                     // simplify handling of consecutive '*?'
00753                                 }
00754                                 else
00755                                 {
00756                                         ret += starPending ? '*' : *cur;
00757                                         starPending = false;
00758                                         cur = next++;
00759                                 }
00760                         }
00761                         else
00762                         {
00763                                 ret += *cur;
00764                                 cur = next++;
00765                         }
00766                 }
00767                 return ret;
00768         }
00769         
00770         void checkPattern(const char* pattern) const
00771         {
00772                 for(;*pattern;++pattern)
00773                 {
00774                         if (*pattern == '[')
00775                         {
00776                                 pattern += 2;
00777                                 while (*pattern && *pattern != ']')
00778                                         ++pattern;
00779                                 if (!*pattern)
00780                                         throw invalid_pattern("missing ']'");
00781                         }
00782                 }
00783         }
00784         const char* getName(const std::string& entryName) const
00785         {
00786                 if (checkPath_)
00787                         return entryName.c_str();
00788                 std::string::size_type p = entryName.rfind(PATH_SEPERATOR);
00789                 if (p == std::string::npos)
00790                         return entryName.c_str();
00791                 else
00792                         return entryName.c_str() + p + 1;
00793         }       
00794         std::string pattern_;
00795         type_f filter_;
00796         bool ignoreCase_;
00797         bool checkPath_;
00798 };
00799 
00801 // Filter-Adapter
00803 
00816 template <class F>
00817 struct adapter_f_t : public filter_base, private detail::assert_is_filter<F>
00818 {
00819 public:
00823         adapter_f_t(const F& t)
00824                 : f_(t)
00825         {}
00826         
00831         bool operator()(const std::string& e) const
00832         {
00833                 return f_(e) != 0;
00834         }
00835         filter_base* clone() const
00836         {
00837                 return new adapter_f_t<F>(f_);
00838         }
00839 private:
00840         F f_;
00841 };
00842 
00855 template <class R>
00856 adapter_f_t<R (*)(const std::string&)> ptr_fun_f(R (*ptr_fun)(const std::string&))
00857 {
00858         return adapter_f_t< R (*)(const std::string&) >(ptr_fun);       
00859 }
00860 
00873 template <class T>
00874 adapter_f_t<T> pred_f(const T& f)
00875 {
00876         return adapter_f_t<T>(f);
00877 }
00878 
00902 template <class T, class MemFun>
00903 struct mem_fun_f_t : public filter_base
00904 {
00905 public:
00906         mem_fun_f_t(T obj, MemFun memFun)
00907                 : obj_(obj)
00908                 , memFun_(memFun)
00909         {}
00914         bool operator()(const std::string& entryName) const 
00915         {return (obj_->*memFun_)(entryName);}
00916         filter_base* clone() const
00917         {
00918                 return new mem_fun_f_t<T, MemFun>(obj_, memFun_);
00919         }
00920 private:
00921         T obj_;
00922         MemFun memFun_;
00923 };
00924 
00943 template <class PtrT, class M>
00944 mem_fun_f_t<PtrT, M> mem_fun_f(PtrT o, M memFun)
00945 {
00946         return mem_fun_f_t<PtrT, M>(o, memFun);
00947 }
00948 
00949  
00950 }
00951 
00952 #undef ADD_CLONE_IMPL
00953 #endif

Generated on Fri Apr 27 13:12:35 2007 for Highlight Code Converter by  doxygen 1.5.2