headers "msp/fs"
{
- source "source";
+ source "source/fs";
install true;
};
library "mspfs"
{
- source "source";
+ source "source/fs";
install true;
};
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#include <cstdlib>
-#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 <msp/strings/utils.h>
-#include "dir.h"
-#include "path.h"
-#include "stat.h"
-#include "utils.h"
-
-using namespace std;
-
-namespace Msp {
-namespace FS {
-
-namespace
-{
-
-/**
-Helper function to determine the location of the program's executable. Caches
-the last result to cut down filesystem access with repeated calls.
-*/
-const Path &get_bin_dir(const string &argv0)
-{
- static string last_argv0;
- static Path bin_dir;
-
- if(!(argv0==last_argv0))
- {
- Path exe;
- if(argv0.find('/')==string::npos)
- {
- const char *path = getenv("PATH");
- vector<string> dirs = split(path, ':');
- for(vector<string>::const_iterator i=dirs.begin(); i!=dirs.end(); ++i)
- if(exists(Path(*i)/argv0))
- {
- exe = realpath(Path(*i)/argv0);
- break;
- }
- }
- else
- exe = realpath(argv0);
-
- last_argv0 = argv0;
- bin_dir = dirname(exe);
- }
-
- return bin_dir;
-}
-
-}
-
-void mkdir(const Path &path, int mode)
-{
- int err;
-#ifdef WIN32
- // The win32 version of this function doesn't take the mode argument. Go figure.
- (void)mode;
- err = ::mkdir(path.str().c_str());
-#else
- err = ::mkdir(path.str().c_str(), mode);
-#endif
-
- if(err==-1)
- throw SystemError("mkdir failed", errno);
-}
-
-void mkpath(const Path &path, int mode)
-{
- Path p;
- for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
- {
- p /= *i;
-#ifdef WIN32
- if(p.size()==1 && p.is_absolute())
- continue;
-#endif
- struct stat st;
- int err = stat(p, st);
- if(err==0)
- {
- if(!S_ISDIR(st.st_mode))
- throw Exception("A component exists and is not a directory");
- continue;
- }
- else if(errno!=ENOENT)
- throw SystemError("stat failed", errno);
- else
- mkdir(p, mode);
- }
-}
-
-void rmdir(const Path &path)
-{
- if(::rmdir(path.str().c_str())==-1)
- throw SystemError("rmdir failed", errno);
-}
-
-void rmdirs(const Path &path)
-{
- list<string> files = list_files(path);
- for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
- {
- Path p = path / *i;
- struct stat st = stat(p.str().c_str());
- if(S_ISDIR(st.st_mode))
- rmdirs(p);
- else
- unlink(p);
- }
-
- rmdir(path);
-}
-
-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)
- {
- while(dirent *de = readdir(dir))
- {
- const char *fn = de->d_name;
- if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0)))
- continue;
- if(r_filter.match(fn))
- result.push_back(fn);
- }
- closedir(dir);
- }
-
- return result;
-}
-
-Path getcwd()
-{
- char buf[1024];
- return ::getcwd(buf, sizeof(buf));
-}
-
-Path get_home_dir()
-{
-#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 ".";
-}
-
-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
-}
-
-Path get_sys_conf_dir(const string &argv0)
-{
- Path dir = get_bin_dir(argv0);
-
- if(dir[-1]=="bin" || dir[-1]=="sbin")
- {
- dir /= "..";
- if(dir[-1]=="usr")
- dir /= "..";
- return dir/"etc";
- }
- else
- return dir;
-}
-
-Path get_sys_data_dir(const string &argv0, const string &appname)
-{
- Path dir = get_bin_dir(argv0);
-
- if(dir[-1]=="bin" || dir[-1]=="sbin")
- return dir/".."/"share"/appname;
- else
- return dir;
-}
-
-Path get_sys_lib_dir(const string &argv0, const string &appname)
-{
- Path dir = get_bin_dir(argv0);
-
- if(dir[-1]=="bin" || dir[-1]=="sbin")
- return dir/".."/"lib"/appname;
- else
- return dir;
-}
-
-void chdir(const Path &path)
-{
- if(::chdir(path.str().c_str())==-1)
- throw SystemError("chdir failed", errno);
-}
-
-} // namespace FS
-} // namespace Msp
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#ifndef MSP_FS_DIR_H_
-#define MSP_FS_DIR_H_
-
-#include <list>
-#include <string>
-#include "path.h"
-
-namespace Msp {
-namespace FS {
-
-/// Creates a directory
-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, which must be empty
-void rmdir(const Path &path);
-
-/// Removes a directory and anything it contains
-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);
-
-/// Returns a directory containing system-wide configuration
-Path get_sys_conf_dir(const std::string &argv0);
-
-/// Returns a directory containing immutable system-wide data
-Path get_sys_data_dir(const std::string &argv0, const std::string &appname);
-
-/// Returns a directory containing system-wide architecture-specific files
-Path get_sys_lib_dir(const std::string &argv0, const std::string &appname);
-
-/// Changes the current working directory
-void chdir(const Path &);
-
-} // namespace FS
-} // namespace Msp
-
-#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
+#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 <msp/strings/utils.h>
+#include "dir.h"
+#include "path.h"
+#include "stat.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace Msp {
+namespace FS {
+
+namespace
+{
+
+/**
+Helper function to determine the location of the program's executable. Caches
+the last result to cut down filesystem access with repeated calls.
+*/
+const Path &get_bin_dir(const string &argv0)
+{
+ static string last_argv0;
+ static Path bin_dir;
+
+ if(!(argv0==last_argv0))
+ {
+ Path exe;
+ if(argv0.find('/')==string::npos)
+ {
+ const char *path = getenv("PATH");
+ vector<string> dirs = split(path, ':');
+ for(vector<string>::const_iterator i=dirs.begin(); i!=dirs.end(); ++i)
+ if(exists(Path(*i)/argv0))
+ {
+ exe = realpath(Path(*i)/argv0);
+ break;
+ }
+ }
+ else
+ exe = realpath(argv0);
+
+ last_argv0 = argv0;
+ bin_dir = dirname(exe);
+ }
+
+ return bin_dir;
+}
+
+}
+
+void mkdir(const Path &path, int mode)
+{
+ int err;
+#ifdef WIN32
+ // The win32 version of this function doesn't take the mode argument. Go figure.
+ (void)mode;
+ err = ::mkdir(path.str().c_str());
+#else
+ err = ::mkdir(path.str().c_str(), mode);
+#endif
+
+ if(err==-1)
+ throw SystemError("mkdir failed", errno);
+}
+
+void mkpath(const Path &path, int mode)
+{
+ Path p;
+ for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
+ {
+ p /= *i;
+#ifdef WIN32
+ if(p.size()==1 && p.is_absolute())
+ continue;
+#endif
+ struct stat st;
+ int err = stat(p, st);
+ if(err==0)
+ {
+ if(!S_ISDIR(st.st_mode))
+ throw Exception("A component exists and is not a directory");
+ continue;
+ }
+ else if(errno!=ENOENT)
+ throw SystemError("stat failed", errno);
+ else
+ mkdir(p, mode);
+ }
+}
+
+void rmdir(const Path &path)
+{
+ if(::rmdir(path.str().c_str())==-1)
+ throw SystemError("rmdir failed", errno);
+}
+
+void rmdirs(const Path &path)
+{
+ list<string> files = list_files(path);
+ for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
+ {
+ Path p = path / *i;
+ struct stat st = stat(p.str().c_str());
+ if(S_ISDIR(st.st_mode))
+ rmdirs(p);
+ else
+ unlink(p);
+ }
+
+ rmdir(path);
+}
+
+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)
+ {
+ while(dirent *de = readdir(dir))
+ {
+ const char *fn = de->d_name;
+ if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0)))
+ continue;
+ if(r_filter.match(fn))
+ result.push_back(fn);
+ }
+ closedir(dir);
+ }
+
+ return result;
+}
+
+Path getcwd()
+{
+ char buf[1024];
+ return ::getcwd(buf, sizeof(buf));
+}
+
+Path get_home_dir()
+{
+#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 ".";
+}
+
+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
+}
+
+Path get_sys_conf_dir(const string &argv0)
+{
+ Path dir = get_bin_dir(argv0);
+
+ if(dir[-1]=="bin" || dir[-1]=="sbin")
+ {
+ dir /= "..";
+ if(dir[-1]=="usr")
+ dir /= "..";
+ return dir/"etc";
+ }
+ else
+ return dir;
+}
+
+Path get_sys_data_dir(const string &argv0, const string &appname)
+{
+ Path dir = get_bin_dir(argv0);
+
+ if(dir[-1]=="bin" || dir[-1]=="sbin")
+ return dir/".."/"share"/appname;
+ else
+ return dir;
+}
+
+Path get_sys_lib_dir(const string &argv0, const string &appname)
+{
+ Path dir = get_bin_dir(argv0);
+
+ if(dir[-1]=="bin" || dir[-1]=="sbin")
+ return dir/".."/"lib"/appname;
+ else
+ return dir;
+}
+
+void chdir(const Path &path)
+{
+ if(::chdir(path.str().c_str())==-1)
+ throw SystemError("chdir failed", errno);
+}
+
+} // namespace FS
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_FS_DIR_H_
+#define MSP_FS_DIR_H_
+
+#include <list>
+#include <string>
+#include "path.h"
+
+namespace Msp {
+namespace FS {
+
+/// Creates a directory
+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, which must be empty
+void rmdir(const Path &path);
+
+/// Removes a directory and anything it contains
+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);
+
+/// Returns a directory containing system-wide configuration
+Path get_sys_conf_dir(const std::string &argv0);
+
+/// Returns a directory containing immutable system-wide data
+Path get_sys_data_dir(const std::string &argv0, const std::string &appname);
+
+/// Returns a directory containing system-wide architecture-specific files
+Path get_sys_lib_dir(const std::string &argv0, const std::string &appname);
+
+/// Changes the current working directory
+void chdir(const Path &);
+
+} // namespace FS
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <msp/core/except.h>
+#include <msp/strings/utils.h>
+#include "path.h"
+#include "utils.h"
+
+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 {
+
+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]==DIRSEP)
+ return 1;
+
+ unsigned count = 1;
+ for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
+ if(*i==DIRSEP) ++count;
+ return count;
+}
+
+bool Path::is_absolute() const
+{
+#ifdef WIN32
+ if(is_windows_drive((*this)[0]))
+ return true;
+#endif
+ if(path[0]==DIRSEP)
+ return true;
+ return false;
+}
+
+Path Path::subpath(unsigned start, unsigned count) const
+{
+ Path result;
+ Iterator i = begin();
+ for(unsigned j=0; (j<start && i!=end()); ++j)
+ ++i;
+ for(unsigned j=0; (j<count && i!=end()); ++j)
+ {
+ result /= *i;
+ ++i;
+ }
+ 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
+are ignored.
+*/
+Path &Path::operator/=(const Path &p)
+{
+ if(p.is_absolute())
+ path = p.path;
+ else
+ {
+ for(Iterator i=p.begin(); i!=p.end(); ++i)
+ add_component(*i);
+ }
+ return *this;
+}
+
+string Path::operator[](int n) const
+{
+ if(n>=0)
+ {
+ for(Iterator i=begin(); i!=end(); ++i, --n)
+ if(!n)
+ return *i;
+ }
+ else
+ {
+ for(Iterator i=end(); i!=begin();)
+ {
+ --i;
+ if(!++n)
+ return *i;
+ }
+ }
+
+ throw InvalidParameterValue("Path component index out of range");
+}
+
+bool Path::operator==(const Path &p) const
+{
+#ifdef WIN32
+ return !strcasecmp(path, p.path);
+#else
+ return path==p.path;
+#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)
+{
+ string::size_type start = 0;
+ if(p[0]=='/' || p[0]=='\\')
+ add_component(string(1, DIRSEP));
+ while(1)
+ {
+ string::size_type slash = p.find_first_of("/\\", start);
+ if(slash>start)
+ add_component(p.substr(start, slash-start));
+ if(slash==string::npos)
+ break;
+ start = slash+1;
+ }
+}
+
+/**
+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]==DIRSEP)
+ {
+ // Replace the path with the root directory
+#ifdef WIN32
+ unsigned slash = path.find(DIRSEP);
+ if(is_windows_drive(path.substr(0, slash)))
+ path = path.substr(0, 2);
+ else
+#endif
+ path = comp;
+ }
+#ifdef WIN32
+ else if(is_windows_drive(comp))
+ path = comp;
+#endif
+ else if(comp=="..")
+ {
+ if(path.empty() || path==".")
+ path = comp;
+ // .. in root directory is a no-op
+ else if(path.size()==1 && path[0]==DIRSEP)
+ ;
+#ifdef WIN32
+ else if(is_windows_drive(path))
+ ;
+#endif
+ else
+ {
+ string::size_type slash = path.rfind(DIRSEP);
+ string::size_type start = (slash==string::npos ? 0 : slash+1);
+ if(!path.compare(start, string::npos, ".."))
+ {
+ // If the last component already is a .., add another
+ path += DIRSEP;
+ path += comp;
+ }
+ else if(slash==string::npos)
+ path = ".";
+ else
+ {
+ if(slash==0)
+ slash = 1;
+ // Otherwise, erase the last component
+ path.erase(slash, string::npos);
+ }
+ }
+ }
+ else if(comp!="." || path.empty())
+ {
+ if(comp!="." && path.empty())
+ path = ".";
+ 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]==DIRSEP)
+ end = 1;
+#ifdef WIN32
+ 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(DIRSEP);
+}
+
+Path::Iterator &Path::Iterator::operator++()
+{
+ start = end;
+ 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>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 string();
+ if(start==end)
+ return string();
+ return path.path.substr(start, end-start);
+}
+
+} // namespace FS
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_FS_PATH_H_
+#define MSP_FS_PATH_H_
+
+#include <ostream>
+#include <string>
+
+namespace Msp {
+namespace FS {
+
+enum
+{
+#ifdef WIN32
+ DIRSEP = '\\'
+#else
+ DIRSEP = '/'
+#endif
+};
+
+/**
+Stores a filesystem path. Paths are always stored in a normalized form; there
+are never any "." or ".." components in the middle of the path, and relative
+paths always begin with a single "." component or a sequence ".." components.
+*/
+class Path
+{
+public:
+ class Iterator
+ {
+ friend class Path;
+
+ private:
+ const Path &path;
+ std::string::size_type 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:
+ std::string path;
+
+public:
+ Path();
+ Path(const std::string &);
+ Path(const char *);
+
+ const std::string &str() const { return path; }
+
+ /// Returns the number of components in the path.
+ unsigned size() const;
+
+ bool empty() const { return path.empty(); }
+
+ /// 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;
+ Iterator end() const;
+private:
+ void init(const std::string &);
+ void add_component(const std::string &);
+};
+
+inline std::ostream &operator<<(std::ostream &o, const Path &p) { o<<p.str(); return o; }
+
+} // namespace FS
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cerrno>
+#ifdef WIN32
+#include <io.h>
+#endif
+#include <msp/core/except.h>
+#include "path.h"
+#include "stat.h"
+
+namespace Msp {
+namespace FS {
+
+int stat(const Path &fn, struct stat &st)
+{
+ return ::stat(fn.str().c_str(), &st);
+}
+
+struct stat stat(const Path &fn)
+{
+ struct stat st;
+ if(stat(fn, st)==-1)
+ throw SystemError("stat failed", errno);
+ return st;
+}
+
+int lstat(const Path &fn, struct stat &st)
+{
+#ifdef WIN32
+ return stat(fn, st);
+#else
+ return ::lstat(fn.str().c_str(), &st);
+#endif
+}
+
+struct stat lstat(const Path &fn)
+{
+ struct stat st;
+ if(lstat(fn, st)==-1)
+ throw SystemError("lstat failed", errno);
+ return st;
+}
+
+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;
+ if(stat(path, st)==0)
+ return S_ISDIR(st.st_mode);
+ return false;
+}
+
+bool is_link(const Path &path)
+{
+#ifdef WIN32
+ (void)path;
+#else
+ struct stat st;
+ if(lstat(path, st)==0)
+ return S_ISLNK(st.st_mode);
+#endif
+ return false;
+}
+
+} // namespace FS
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_FS_STAT_H_
+#define MSP_FS_STAT_H_
+
+#include <sys/stat.h>
+#include "path.h"
+
+namespace Msp {
+namespace FS {
+
+/**
+Gets information about a file. Returns 0 on success or -1 on error. This
+version can be used to check for file existence and get information in one
+call.
+*/
+int stat(const Path &fn, struct stat &st);
+
+/**
+Returns information about a file. This version throws an exception if an error
+occurs.
+*/
+struct stat stat(const Path &fn);
+
+/// Gets information about a file, without following symbolic links
+int lstat(const Path &fn, struct stat &st);
+
+/// Returns information about a file, without following symbolic links
+struct stat lstat(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);
+
+/// Tests whether a path refers to a symbolic link
+bool is_link(const Path &path);
+
+} // namespace FS
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cerrno>
+#include <cstdio>
+#include <msp/core/except.h>
+#ifndef WIN32
+#include <fnmatch.h>
+#else
+#include <msp/strings/glob.h>
+#endif
+#include <msp/strings/utils.h>
+#include "dir.h"
+#include "path.h"
+#include "stat.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace Msp {
+namespace FS {
+
+string basename(const Path &p)
+{
+ return p[-1];
+}
+
+Path dirname(const Path &p)
+{
+ if(p.size()==1)
+ {
+ if(p.is_absolute())
+ return p;
+ return ".";
+ }
+ 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)
+{
+ string::size_type dot = fn.rfind('.');
+ if(dot==string::npos)
+ return string();
+ return fn.substr(dot);
+}
+
+Path fix_case(const Path &path)
+{
+ bool found = true;
+ Path result;
+ for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
+ {
+ if(!found || *i=="/")
+ result /= *i;
+ else
+ {
+ list<string> files;
+ if(result.size())
+ files = list_files(result);
+ else
+ files = list_files(".");
+
+ found = false;
+ for(list<string>::iterator j=files.begin(); (j!=files.end() && !found); ++j)
+ if(!strcasecmp(*j,*i))
+ {
+ result /= *j;
+ found = true;
+ }
+
+ if(!found)
+ result /= *i;
+ }
+ }
+
+ return result;
+}
+
+Path readlink(const Path &link)
+{
+#ifdef WIN32
+ (void)link;
+ throw Exception("No symbolic links on win32");
+#else
+ char buf[4096];
+ int len = ::readlink(link.str().c_str(), buf, sizeof(buf));
+ if(len==-1)
+ throw SystemError("readlink failed", errno);
+ return string(buf, len);
+#endif
+}
+
+Path realpath(const Path &path)
+{
+#ifdef WIN32
+ if(path.is_absolute())
+ return path;
+ else
+ return getcwd()/path;
+#else
+ list<string> queue(path.begin(), path.end());
+ if(!path.is_absolute())
+ {
+ Path cwd = getcwd();
+ queue.insert(queue.begin(), cwd.begin(), cwd.end());
+ }
+
+ Path real;
+ unsigned n_links = 0;
+ while(!queue.empty())
+ {
+ Path next = real/queue.front();
+ queue.pop_front();
+
+ struct stat st = lstat(next);
+ if(S_ISLNK(st.st_mode))
+ {
+ if(++n_links>64)
+ throw Exception("Ludicrous amount of symlinks detected in realpath, giving up");
+ Path link = readlink(next);
+ queue.insert(queue.begin(), link.begin(), link.end());
+ }
+ else
+ real = next;
+ }
+
+ return real;
+#endif
+}
+
+void rename(const Path &from, const Path &to)
+{
+ if(::rename(from.str().c_str(), to.str().c_str())==-1)
+ throw SystemError("rename failed", errno);
+}
+
+void unlink(const Path &path)
+{
+ if(::unlink(path.str().c_str())==-1)
+ throw SystemError("unlink failed", errno);
+}
+
+Path relative(const Path &path, const Path &base)
+{
+ Path::Iterator i = path.begin();
+ Path::Iterator j = base.begin();
+ for(; (i!=path.end() && j!=base.end() && *i==*j); ++i, ++j) ;
+
+ Path result;
+ for(; j!=base.end(); ++j)
+ result /= "..";
+ for(; i!=path.end(); ++i)
+ result /= *i;
+
+ return result;
+}
+
+int descendant_depth(const Path &path, const Path &parent)
+{
+ Path::Iterator i = path.begin();
+ Path::Iterator j = parent.begin();
+ for(; (i!=path.end() && j!=parent.end() && *i==*j); ++i, ++j) ;
+
+ if(j!=parent.end())
+ return -1;
+
+ int result = 0;
+ for(; i!=path.end(); ++i)
+ ++result;
+
+ return result;
+}
+
+} // namespace FS
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspfs
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_FS_UTILS_H_
+#define MSP_FS_UTILS_H_
+
+#include "path.h"
+
+namespace Msp {
+namespace FS {
+
+/// 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);
+
+/// Reads the contents of a symbolic link
+Path readlink(const Path &path);
+
+/// Resolves all symlinks from a path. Will always return an absolute path.
+Path realpath(const Path &path);
+
+/// Removes a file
+void unlink(const Path &path);
+
+/// Renames a file. Existing file, if any, is overwritten.
+void rename(const Path &from, const Path &to);
+
+/// Makes a path relative to some base path. That is, base/result==path.
+Path relative(const Path &path, const Path &base);
+
+/** Determines how many levels a path is below another. Returns -1 if path is
+not a descendant of parent. */
+int descendant_depth(const Path &path, const Path &parent);
+
+} // namespace FS
+} // namespace Msp
+
+#endif
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#include <msp/core/except.h>
-#include <msp/strings/utils.h>
-#include "path.h"
-#include "utils.h"
-
-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 {
-
-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]==DIRSEP)
- return 1;
-
- unsigned count = 1;
- for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
- if(*i==DIRSEP) ++count;
- return count;
-}
-
-bool Path::is_absolute() const
-{
-#ifdef WIN32
- if(is_windows_drive((*this)[0]))
- return true;
-#endif
- if(path[0]==DIRSEP)
- return true;
- return false;
-}
-
-Path Path::subpath(unsigned start, unsigned count) const
-{
- Path result;
- Iterator i = begin();
- for(unsigned j=0; (j<start && i!=end()); ++j)
- ++i;
- for(unsigned j=0; (j<count && i!=end()); ++j)
- {
- result /= *i;
- ++i;
- }
- 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
-are ignored.
-*/
-Path &Path::operator/=(const Path &p)
-{
- if(p.is_absolute())
- path = p.path;
- else
- {
- for(Iterator i=p.begin(); i!=p.end(); ++i)
- add_component(*i);
- }
- return *this;
-}
-
-string Path::operator[](int n) const
-{
- if(n>=0)
- {
- for(Iterator i=begin(); i!=end(); ++i, --n)
- if(!n)
- return *i;
- }
- else
- {
- for(Iterator i=end(); i!=begin();)
- {
- --i;
- if(!++n)
- return *i;
- }
- }
-
- throw InvalidParameterValue("Path component index out of range");
-}
-
-bool Path::operator==(const Path &p) const
-{
-#ifdef WIN32
- return !strcasecmp(path, p.path);
-#else
- return path==p.path;
-#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)
-{
- string::size_type start = 0;
- if(p[0]=='/' || p[0]=='\\')
- add_component(string(1, DIRSEP));
- while(1)
- {
- string::size_type slash = p.find_first_of("/\\", start);
- if(slash>start)
- add_component(p.substr(start, slash-start));
- if(slash==string::npos)
- break;
- start = slash+1;
- }
-}
-
-/**
-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]==DIRSEP)
- {
- // Replace the path with the root directory
-#ifdef WIN32
- unsigned slash = path.find(DIRSEP);
- if(is_windows_drive(path.substr(0, slash)))
- path = path.substr(0, 2);
- else
-#endif
- path = comp;
- }
-#ifdef WIN32
- else if(is_windows_drive(comp))
- path = comp;
-#endif
- else if(comp=="..")
- {
- if(path.empty() || path==".")
- path = comp;
- // .. in root directory is a no-op
- else if(path.size()==1 && path[0]==DIRSEP)
- ;
-#ifdef WIN32
- else if(is_windows_drive(path))
- ;
-#endif
- else
- {
- string::size_type slash = path.rfind(DIRSEP);
- string::size_type start = (slash==string::npos ? 0 : slash+1);
- if(!path.compare(start, string::npos, ".."))
- {
- // If the last component already is a .., add another
- path += DIRSEP;
- path += comp;
- }
- else if(slash==string::npos)
- path = ".";
- else
- {
- if(slash==0)
- slash = 1;
- // Otherwise, erase the last component
- path.erase(slash, string::npos);
- }
- }
- }
- else if(comp!="." || path.empty())
- {
- if(comp!="." && path.empty())
- path = ".";
- 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]==DIRSEP)
- end = 1;
-#ifdef WIN32
- 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(DIRSEP);
-}
-
-Path::Iterator &Path::Iterator::operator++()
-{
- start = end;
- 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>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 string();
- if(start==end)
- return string();
- return path.path.substr(start, end-start);
-}
-
-} // namespace FS
-} // namespace Msp
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#ifndef MSP_FS_PATH_H_
-#define MSP_FS_PATH_H_
-
-#include <ostream>
-#include <string>
-
-namespace Msp {
-namespace FS {
-
-enum
-{
-#ifdef WIN32
- DIRSEP = '\\'
-#else
- DIRSEP = '/'
-#endif
-};
-
-/**
-Stores a filesystem path. Paths are always stored in a normalized form; there
-are never any "." or ".." components in the middle of the path, and relative
-paths always begin with a single "." component or a sequence ".." components.
-*/
-class Path
-{
-public:
- class Iterator
- {
- friend class Path;
-
- private:
- const Path &path;
- std::string::size_type 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:
- std::string path;
-
-public:
- Path();
- Path(const std::string &);
- Path(const char *);
-
- const std::string &str() const { return path; }
-
- /// Returns the number of components in the path.
- unsigned size() const;
-
- bool empty() const { return path.empty(); }
-
- /// 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;
- Iterator end() const;
-private:
- void init(const std::string &);
- void add_component(const std::string &);
-};
-
-inline std::ostream &operator<<(std::ostream &o, const Path &p) { o<<p.str(); return o; }
-
-} // namespace FS
-} // namespace Msp
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#include <cerrno>
-#ifdef WIN32
-#include <io.h>
-#endif
-#include <msp/core/except.h>
-#include "path.h"
-#include "stat.h"
-
-namespace Msp {
-namespace FS {
-
-int stat(const Path &fn, struct stat &st)
-{
- return ::stat(fn.str().c_str(), &st);
-}
-
-struct stat stat(const Path &fn)
-{
- struct stat st;
- if(stat(fn, st)==-1)
- throw SystemError("stat failed", errno);
- return st;
-}
-
-int lstat(const Path &fn, struct stat &st)
-{
-#ifdef WIN32
- return stat(fn, st);
-#else
- return ::lstat(fn.str().c_str(), &st);
-#endif
-}
-
-struct stat lstat(const Path &fn)
-{
- struct stat st;
- if(lstat(fn, st)==-1)
- throw SystemError("lstat failed", errno);
- return st;
-}
-
-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;
- if(stat(path, st)==0)
- return S_ISDIR(st.st_mode);
- return false;
-}
-
-bool is_link(const Path &path)
-{
-#ifdef WIN32
- (void)path;
-#else
- struct stat st;
- if(lstat(path, st)==0)
- return S_ISLNK(st.st_mode);
-#endif
- return false;
-}
-
-} // namespace FS
-} // namespace Msp
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#ifndef MSP_FS_STAT_H_
-#define MSP_FS_STAT_H_
-
-#include <sys/stat.h>
-#include "path.h"
-
-namespace Msp {
-namespace FS {
-
-/**
-Gets information about a file. Returns 0 on success or -1 on error. This
-version can be used to check for file existence and get information in one
-call.
-*/
-int stat(const Path &fn, struct stat &st);
-
-/**
-Returns information about a file. This version throws an exception if an error
-occurs.
-*/
-struct stat stat(const Path &fn);
-
-/// Gets information about a file, without following symbolic links
-int lstat(const Path &fn, struct stat &st);
-
-/// Returns information about a file, without following symbolic links
-struct stat lstat(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);
-
-/// Tests whether a path refers to a symbolic link
-bool is_link(const Path &path);
-
-} // namespace FS
-} // namespace Msp
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#include <cerrno>
-#include <cstdio>
-#include <msp/core/except.h>
-#ifndef WIN32
-#include <fnmatch.h>
-#else
-#include <msp/strings/glob.h>
-#endif
-#include <msp/strings/utils.h>
-#include "dir.h"
-#include "path.h"
-#include "stat.h"
-#include "utils.h"
-
-using namespace std;
-
-namespace Msp {
-namespace FS {
-
-string basename(const Path &p)
-{
- return p[-1];
-}
-
-Path dirname(const Path &p)
-{
- if(p.size()==1)
- {
- if(p.is_absolute())
- return p;
- return ".";
- }
- 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)
-{
- string::size_type dot = fn.rfind('.');
- if(dot==string::npos)
- return string();
- return fn.substr(dot);
-}
-
-Path fix_case(const Path &path)
-{
- bool found = true;
- Path result;
- for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
- {
- if(!found || *i=="/")
- result /= *i;
- else
- {
- list<string> files;
- if(result.size())
- files = list_files(result);
- else
- files = list_files(".");
-
- found = false;
- for(list<string>::iterator j=files.begin(); (j!=files.end() && !found); ++j)
- if(!strcasecmp(*j,*i))
- {
- result /= *j;
- found = true;
- }
-
- if(!found)
- result /= *i;
- }
- }
-
- return result;
-}
-
-Path readlink(const Path &link)
-{
-#ifdef WIN32
- (void)link;
- throw Exception("No symbolic links on win32");
-#else
- char buf[4096];
- int len = ::readlink(link.str().c_str(), buf, sizeof(buf));
- if(len==-1)
- throw SystemError("readlink failed", errno);
- return string(buf, len);
-#endif
-}
-
-Path realpath(const Path &path)
-{
-#ifdef WIN32
- if(path.is_absolute())
- return path;
- else
- return getcwd()/path;
-#else
- list<string> queue(path.begin(), path.end());
- if(!path.is_absolute())
- {
- Path cwd = getcwd();
- queue.insert(queue.begin(), cwd.begin(), cwd.end());
- }
-
- Path real;
- unsigned n_links = 0;
- while(!queue.empty())
- {
- Path next = real/queue.front();
- queue.pop_front();
-
- struct stat st = lstat(next);
- if(S_ISLNK(st.st_mode))
- {
- if(++n_links>64)
- throw Exception("Ludicrous amount of symlinks detected in realpath, giving up");
- Path link = readlink(next);
- queue.insert(queue.begin(), link.begin(), link.end());
- }
- else
- real = next;
- }
-
- return real;
-#endif
-}
-
-void rename(const Path &from, const Path &to)
-{
- if(::rename(from.str().c_str(), to.str().c_str())==-1)
- throw SystemError("rename failed", errno);
-}
-
-void unlink(const Path &path)
-{
- if(::unlink(path.str().c_str())==-1)
- throw SystemError("unlink failed", errno);
-}
-
-Path relative(const Path &path, const Path &base)
-{
- Path::Iterator i = path.begin();
- Path::Iterator j = base.begin();
- for(; (i!=path.end() && j!=base.end() && *i==*j); ++i, ++j) ;
-
- Path result;
- for(; j!=base.end(); ++j)
- result /= "..";
- for(; i!=path.end(); ++i)
- result /= *i;
-
- return result;
-}
-
-int descendant_depth(const Path &path, const Path &parent)
-{
- Path::Iterator i = path.begin();
- Path::Iterator j = parent.begin();
- for(; (i!=path.end() && j!=parent.end() && *i==*j); ++i, ++j) ;
-
- if(j!=parent.end())
- return -1;
-
- int result = 0;
- for(; i!=path.end(); ++i)
- ++result;
-
- return result;
-}
-
-} // namespace FS
-} // namespace Msp
+++ /dev/null
-/* $Id$
-
-This file is part of libmspfs
-Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#ifndef MSP_FS_UTILS_H_
-#define MSP_FS_UTILS_H_
-
-#include "path.h"
-
-namespace Msp {
-namespace FS {
-
-/// 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);
-
-/// Reads the contents of a symbolic link
-Path readlink(const Path &path);
-
-/// Resolves all symlinks from a path. Will always return an absolute path.
-Path realpath(const Path &path);
-
-/// Removes a file
-void unlink(const Path &path);
-
-/// Renames a file. Existing file, if any, is overwritten.
-void rename(const Path &from, const Path &to);
-
-/// Makes a path relative to some base path. That is, base/result==path.
-Path relative(const Path &path, const Path &base);
-
-/** Determines how many levels a path is below another. Returns -1 if path is
-not a descendant of parent. */
-int descendant_depth(const Path &path, const Path &parent);
-
-} // namespace FS
-} // namespace Msp
-
-#endif