From a17d6ad286e19e2222ab8b6c9a762a83bf2c6c56 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 20 Jul 2008 12:12:55 +0000 Subject: [PATCH] Refactor the API Shuffle things around a bit in Path --- source/dir.cpp | 40 +++++++++++----- source/dir.h | 10 +++- source/path.cpp | 119 ++++++++++++++++++++++++++++++++--------------- source/path.h | 56 ++++++++++++++-------- source/stat.cpp | 11 +++++ source/stat.h | 3 ++ source/utils.cpp | 61 ++++++++++-------------- source/utils.h | 34 ++++++++------ 8 files changed, 214 insertions(+), 120 deletions(-) diff --git a/source/dir.cpp b/source/dir.cpp index c402ec9..9d23a13 100644 --- a/source/dir.cpp +++ b/source/dir.cpp @@ -9,7 +9,11 @@ Distributed under the LGPL #include #include #include +#ifdef WIN32 +#include +#endif #include +#include #include "dir.h" #include "path.h" #include "stat.h" @@ -35,14 +39,6 @@ void mkdir(const Path &path, int mode) throw SystemError("mkdir failed", errno); } -/** -Creates a directory and any parent directories if needed. - -@param path The path to create -@param mode Access mode for new directories - -@return 0 on success, -1 on error -*/ void mkpath(const Path &path, int mode) { Path p; @@ -92,6 +88,13 @@ void rmdirs(const Path &path) list list_files(const Path &path) { + return list_filtered(path, string()); +} + +list list_filtered(const Path &path, const string &filter) +{ + Regex r_filter(filter); + list result; DIR *dir=opendir(path.str().c_str()); if(dir) @@ -101,7 +104,8 @@ list list_files(const Path &path) const char *fn=de->d_name; if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) continue; - result.push_back(fn); + if(r_filter.match(fn)) + result.push_back(fn); } closedir(dir); } @@ -117,13 +121,27 @@ Path getcwd() Path get_home_dir() { -#ifndef WIN32 +#ifdef WIN32 + char home[MAX_PATH]; + if(SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, home)==S_OK) + return home; +#else const char *home=getenv("HOME"); if(home) return home; +#endif return "."; -#else +} + +Path get_user_data_dir(const string &appname) +{ +#ifdef WIN32 + char datadir[MAX_PATH]; + if(SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, datadir)==S_OK) + return Path(datadir)/appname; return "."; +#else + return get_home_dir()/("+"+appname); #endif } diff --git a/source/dir.h b/source/dir.h index 25ec776..3b68463 100644 --- a/source/dir.h +++ b/source/dir.h @@ -22,21 +22,27 @@ void mkdir(const Path &path, int mode); /// Creates a directory and any required parent directories void mkpath(const Path &path, int mode); -/// Removes a directory +/// Removes a directory, which must be empty void rmdir(const Path &path); /// Removes a directory and anything it contains -void rmdirs(const Path &path); +void rmpath(const Path &path); /// Lists the contents of a directory std::list list_files(const Path &path); +/// Lists the contents of a directory, filtered with a regex +std::list list_filtered(const Path &path, const std::string &filter); + /// Returns the current working directory Path getcwd(); /// Returns the user's home directory Path get_home_dir(); +/// Returns a directory suitable for storing user-specific data +Path get_user_data_dir(const std::string &appname); + /// Changes the current working directory void chdir(const Path &); diff --git a/source/path.cpp b/source/path.cpp index 35641f8..c0b91d6 100644 --- a/source/path.cpp +++ b/source/path.cpp @@ -11,22 +11,39 @@ Distributed under the LGPL using namespace std; +namespace { + +inline bool is_windows_drive(const std::string &p) +{ return (p.size()==2 && ((p[0]>='A' && p[0]<='Z') || (p[0]>='a' && p[0]<='z')) && p[1]==':'); } + +} + namespace Msp { namespace FS { -/** -Returns the number of components in the path. -*/ +Path::Path() +{ } + +Path::Path(const string &p) +{ + init(p); +} + +Path::Path(const char *p) +{ + init(p); +} + unsigned Path::size() const { if(path.empty()) return 0; - if(path.size()==1 && path[0]==DIRCHAR) + if(path.size()==1 && path[0]==DIRSEP) return 1; unsigned count=1; for(string::const_iterator i=path.begin(); i!=path.end(); ++i) - if(*i==DIRCHAR) ++count; + if(*i==DIRSEP) ++count; return count; } @@ -36,14 +53,11 @@ bool Path::is_absolute() const if(is_windows_drive((*this)[0])) return true; #endif - if(path[0]==DIRCHAR) + if(path[0]==DIRSEP) return true; return false; } -/** -Extracts the given component range from the path. -*/ Path Path::subpath(unsigned start, unsigned count) const { Path result; @@ -58,6 +72,13 @@ Path Path::subpath(unsigned start, unsigned count) const return result; } +Path Path::operator/(const Path &p) const +{ + Path a=*this; + a/=p; + return a; +} + /** Attaches another path to the end of this one. An absolute path replaces the existing data. ".." elements annihilate the last component and "." elements @@ -75,27 +96,26 @@ Path &Path::operator/=(const Path &p) return *this; } -/** -Returns the path component at the specified index. Negative indices count from -the end of the path. -*/ string Path::operator[](int n) const { if(n>=0) { for(Iterator i=begin(); i!=end(); ++i, --n) - if(!n) return *i; + if(!n) + return *i; } else { for(Iterator i=--end();; --i) { - if(!++n) return *i; - if(i==begin()) break; + if(!++n) + return *i; + if(i==begin()) + break; } } - return ""; + return string(); } bool Path::operator==(const Path &p) const @@ -107,11 +127,23 @@ bool Path::operator==(const Path &p) const #endif } +Path::Iterator Path::begin() const +{ + return Iterator(*this); +} + +Path::Iterator Path::end() const +{ + Iterator i(*this); + i.start=i.end=std::string::npos; + return i; +} + void Path::init(const string &p) { unsigned start=0; if(p[0]=='/' || p[0]=='\\') - add_component(string(1, DIRCHAR)); + add_component(string(1, DIRSEP)); while(1) { unsigned slash=p.find_first_of("/\\", start); @@ -124,17 +156,18 @@ void Path::init(const string &p) } /** -Adds a single component to the path, emulating the cd command. +Adds a single component to the path, emulating the cd command. Fails horribly +if comp contains a separator character. */ void Path::add_component(const string &comp) { if(comp.empty()) ; - else if(comp.size()==1 && comp[0]==DIRCHAR) + else if(comp.size()==1 && comp[0]==DIRSEP) { // Replace the path with the root directory #ifdef WIN32 - unsigned slash=path.find(DIRCHAR); + unsigned slash=path.find(DIRSEP); if(is_windows_drive(path.substr(0, slash))) path=path.substr(0, 2); else @@ -150,7 +183,7 @@ void Path::add_component(const string &comp) if(path.empty()) path=comp; // .. in root directory is a no-op - else if(path.size()==1 && path[0]==DIRCHAR) + else if(path.size()==1 && path[0]==DIRSEP) ; #ifdef WIN32 else if(is_windows_drive(path)) @@ -158,12 +191,12 @@ void Path::add_component(const string &comp) #endif else { - unsigned slash=path.rfind(DIRCHAR); - unsigned start=(slash==string::npos)?0:slash+1; + unsigned slash=path.rfind(DIRSEP); + unsigned start=(slash==string::npos ? 0 : slash+1); if(!path.compare(start, string::npos, "..")) { // If the last component already is a .., add another - path+=DIRCHAR; + path+=DIRSEP; path+=comp; } else @@ -175,54 +208,64 @@ void Path::add_component(const string &comp) { if(path==".") path=""; - if(path.size()>1 || (path.size()==1 && path[0]!=DIRCHAR)) - path+=DIRCHAR; + if(path.size()>1 || (path.size()==1 && path[0]!=DIRSEP)) + path+=DIRSEP; path+=comp; } } + Path::Iterator::Iterator(const Path &p): path(p), start(0) { if(path.path.empty()) start=end=string::npos; - else if(path.path[0]==DIRCHAR) + else if(path.path[0]==DIRSEP) end=1; #ifdef WIN32 - else if(path.path.size()>2 && path.path[2]==DIRCHAR && is_windows_drive(path.path.substr(0, 2))) + else if(path.path.size()>2 && path.path[2]==DIRSEP && is_windows_drive(path.path.substr(0, 2))) end=2; #endif else - end=path.path.find(DIRCHAR); + end=path.path.find(DIRSEP); } Path::Iterator &Path::Iterator::operator++() { start=end; - if(start>=path.path.size()) return *this; - if(path.path[start]==DIRCHAR) ++start; - end=path.path.find(DIRCHAR, start); + if(start>=path.path.size()) + return *this; + if(path.path[start]==DIRSEP) + ++start; + end=path.path.find(DIRSEP, start); return *this; } Path::Iterator &Path::Iterator::operator--() { + if(start==0) + return *this; + end=start; - if(end==0) return *this; - if(end>1 && end1 && end=path.path.size()) return ""; - if(start==end) return ""; + if(start>=path.path.size()) + return string(); + if(start==end) + return string(); return path.path.substr(start, end-start); } diff --git a/source/path.h b/source/path.h index 082a1ba..8c24f19 100644 --- a/source/path.h +++ b/source/path.h @@ -17,9 +17,9 @@ namespace FS { enum { #ifdef WIN32 - DIRCHAR='\\' + DIRSEP='\\' #else - DIRCHAR='/' + DIRSEP='/' #endif }; @@ -28,38 +28,56 @@ class Path public: class Iterator { + friend class Path; + + private: + const Path &path; + unsigned start,end; + + Iterator(const Path &); public: Iterator &operator++(); Iterator &operator--(); std::string operator*() const; bool operator==(const Iterator &i) const { return (start==i.start && end==i.end); } bool operator!=(const Iterator &i) const { return !(*this==i); } - private: - const Path &path; - unsigned start,end; + }; - Iterator(const Path &); +private: + std::string path; - friend class Path; - }; +public: + Path(); + Path(const std::string &); + Path(const char *); - Path() { } - Path(const std::string &p) { init(p); } - Path(const char *p) { init(p); } const std::string &str() const { return path; } + + /// Returns the number of components in the path. unsigned size() const; + bool empty() const { return path.empty(); } - bool is_absolute() const; - Path subpath(unsigned, unsigned =static_cast(-1)) const; - Path operator/(const Path &p) const { Path a=*this; a/=p; return a; } - Path &operator/=(const Path &); + + /// Determines whether the path starts from the root directory + bool is_absolute() const; + + /// Extracts a range of components from the path. + Path subpath(unsigned start, unsigned count=static_cast(-1)) const; + + /// Concatenates this path with another one, with usual filesystem semantics + Path operator/(const Path &p) const; + Path &operator/=(const Path &); + + /** + Extracts a single component from the path. Negative indices count from the + end of the path. + */ std::string operator[](int) const; + bool operator==(const Path &) const; - Iterator begin() const { return Iterator(*this); } - Iterator end() const { Iterator i(*this); i.start=i.end=std::string::npos; return i; } + Iterator begin() const; + Iterator end() const; private: - std::string path; - void init(const std::string &); void add_component(const std::string &); }; diff --git a/source/stat.cpp b/source/stat.cpp index 5a0ee45..3bfae74 100644 --- a/source/stat.cpp +++ b/source/stat.cpp @@ -6,6 +6,9 @@ Distributed under the LGPL */ #include +#ifdef WIN32 +#include +#endif #include #include "path.h" #include "stat.h" @@ -31,6 +34,14 @@ bool exists(const Path &path) return access(path.str().c_str(), F_OK)==0; } +bool is_reg(const Path &path) +{ + struct stat st; + if(stat(path, st)==0) + return S_ISREG(st.st_mode); + return false; +} + bool is_dir(const Path &path) { struct stat st; diff --git a/source/stat.h b/source/stat.h index f6b13d4..4970ff8 100644 --- a/source/stat.h +++ b/source/stat.h @@ -31,6 +31,9 @@ struct stat stat(const Path &fn); /// Tests for existence of a file bool exists(const Path &path); +/// Tests whether a path refers to an existing regular file +bool is_reg(const Path &path); + /// Tests whether a path refers to an existing directory bool is_dir(const Path &path); diff --git a/source/utils.cpp b/source/utils.cpp index 5c8c8d4..0ca1d60 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -22,11 +22,30 @@ using namespace std; namespace Msp { namespace FS { -/** -Fixes the case of the given path to match files / directories on the -filesystem. Intended to be used in programs that need to interact with -emulated Windows programs. -*/ +string basename(const Path &p) +{ + return p[-1]; +} + +Path dirname(const Path &p) +{ + return p.subpath(0, p.size()-1); +} + +string basepart(const string &fn) +{ + unsigned dot=fn.rfind('.'); + return fn.substr(0, dot); +} + +string extpart(const string &fn) +{ + unsigned dot=fn.rfind('.'); + if(dot==string::npos) + return string(); + return fn.substr(dot); +} + Path fix_case(const Path &path) { bool found=true; @@ -65,25 +84,6 @@ void unlink(const Path &path) throw SystemError("unlink failed", errno); } -Filename splitext(const string &fn) -{ - Filename result; - unsigned dot=fn.rfind('.'); - result.base=fn.substr(0, dot); - if(dot!=string::npos) - result.ext=fn.substr(dot); - return result; -} - -int fnmatch(const string &pat, const Path &fn) -{ -#ifdef WIN32 - return globcasematch(pat, fn.str()); -#else - return ::fnmatch(pat.c_str(), fn.str().c_str(), FNM_PATHNAME); -#endif -} - Path relative(const Path &path, const Path &base) { Path::Iterator i=path.begin(); @@ -99,18 +99,5 @@ Path relative(const Path &path, const Path &base) return result; } -/** -Extracts the basename from the given path. Same thing as Path::Path(p)[-1], -but faster. -*/ -string basename(const std::string &p) -{ - unsigned slash=p.rfind(DIRCHAR); - if(slash==string::npos) - return p; - else - return p.substr(slash+1); -} - } // namespace FS } // namespace Msp diff --git a/source/utils.h b/source/utils.h index a828c92..a6d9568 100644 --- a/source/utils.h +++ b/source/utils.h @@ -13,28 +13,36 @@ namespace FS { class Path; -struct Filename -{ - std::string base; - std::string ext; -}; +/// Extracts the last component of the path. +std::string basename(const Path &); +/// Removes the last component from the path. +Path dirname(const Path &); + +/** +Returns the base part of a filename. This includes everything up to the last +dot, but not the dot itself. +*/ +std::string basepart(const std::string &); + +/** +Returns the extension part of a filename. This includes the last dot and +everything after it. +*/ +std::string extpart(const std::string &); + +/** +Fixes the case of the given path to match files / directories on the +filesystem. +*/ Path fix_case(const Path &path); /// Removes a file void unlink(const Path &path); -Filename splitext(const std::string &); -int fnmatch(const std::string &, const Path &); - /// Makes a path relative to some base path. That is, base/result==path. Path relative(const Path &path, const Path &base); -std::string basename(const std::string &); - -inline bool is_windows_drive(const std::string &p) -{ return (p.size()==2 && ((p[0]>='A' && p[0]<='Z') || (p[0]>='a' && p[0]<='z')) && p[1]==':'); } - } // namespace FS } // namespace Msp -- 2.45.2