#include <cerrno>
#include <dirent.h>
#include <sys/stat.h>
+#ifdef WIN32
+#include <shlobj.h>
+#endif
#include <msp/core/except.h>
+#include <msp/strings/regex.h>
#include "dir.h"
#include "path.h"
#include "stat.h"
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;
list<string> list_files(const Path &path)
{
+ return list_filtered(path, string());
+}
+
+list<string> list_filtered(const Path &path, const string &filter)
+{
+ Regex r_filter(filter);
+
list<string> result;
DIR *dir=opendir(path.str().c_str());
if(dir)
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);
}
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
}
/// 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<std::string> list_files(const Path &path);
+/// Lists the contents of a directory, filtered with a regex
+std::list<std::string> 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 &);
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;
}
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;
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
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
#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);
}
/**
-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
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))
#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
{
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 && end<path.path.size() && path.path[end]!=DIRCHAR) --end;
- start=path.path.rfind(DIRCHAR, end-1);
+ if(end>1 && end<path.path.size() && path.path[end]!=DIRSEP)
+ --end;
+
+ start=path.path.rfind(DIRSEP, end-1);
if(start==string::npos)
start=0;
else if(start<end-1)
++start;
+
return *this;
}
string Path::Iterator::operator*() const
{
- if(start>=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);
}
enum
{
#ifdef WIN32
- DIRCHAR='\\'
+ DIRSEP='\\'
#else
- DIRCHAR='/'
+ DIRSEP='/'
#endif
};
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<unsigned>(-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<unsigned>(-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 &);
};
*/
#include <cerrno>
+#ifdef WIN32
+#include <io.h>
+#endif
#include <msp/core/except.h>
#include "path.h"
#include "stat.h"
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;
/// 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);
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;
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();
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
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