From: Mikko Rasa Date: Wed, 27 Jul 2011 05:49:01 +0000 (+0300) Subject: Prepare for assimilation into core X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=commitdiff_plain;h=af94bc926e301e9b871dc18662b4fa6e5614fdbf;hp=fa77438b62207466c48620604c8cc34931080936 Prepare for assimilation into core --- diff --git a/Build b/Build index b0e5881..b44b8eb 100644 --- a/Build +++ b/Build @@ -9,13 +9,13 @@ package "mspfs" headers "msp/fs" { - source "source"; + source "source/fs"; install true; }; library "mspfs" { - source "source"; + source "source/fs"; install true; }; diff --git a/source/dir.cpp b/source/dir.cpp deleted file mode 100644 index ad2e013..0000000 --- a/source/dir.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* $Id$ - -This file is part of libmspfs -Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ - -#include -#include -#include -#include -#ifdef WIN32 -#include -#endif -#include -#include -#include -#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 dirs = split(path, ':'); - for(vector::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 files = list_files(path); - for(list::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 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) - { - 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 diff --git a/source/dir.h b/source/dir.h deleted file mode 100644 index 53228cc..0000000 --- a/source/dir.h +++ /dev/null @@ -1,60 +0,0 @@ -/* $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 -#include -#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 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); - -/// 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 diff --git a/source/fs/dir.cpp b/source/fs/dir.cpp new file mode 100644 index 0000000..ad2e013 --- /dev/null +++ b/source/fs/dir.cpp @@ -0,0 +1,229 @@ +/* $Id$ + +This file is part of libmspfs +Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include +#include +#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 dirs = split(path, ':'); + for(vector::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 files = list_files(path); + for(list::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 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) + { + 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 diff --git a/source/fs/dir.h b/source/fs/dir.h new file mode 100644 index 0000000..53228cc --- /dev/null +++ b/source/fs/dir.h @@ -0,0 +1,60 @@ +/* $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 +#include +#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 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); + +/// 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 diff --git a/source/fs/path.cpp b/source/fs/path.cpp new file mode 100644 index 0000000..e514d36 --- /dev/null +++ b/source/fs/path.cpp @@ -0,0 +1,279 @@ +/* $Id$ + +This file is part of libmspfs +Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#include +#include +#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=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()) + return string(); + if(start==end) + return string(); + return path.path.substr(start, end-start); +} + +} // namespace FS +} // namespace Msp diff --git a/source/fs/path.h b/source/fs/path.h new file mode 100644 index 0000000..e560d3f --- /dev/null +++ b/source/fs/path.h @@ -0,0 +1,95 @@ +/* $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 +#include + +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(-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< +#ifdef WIN32 +#include +#endif +#include +#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 diff --git a/source/fs/stat.h b/source/fs/stat.h new file mode 100644 index 0000000..55a1fec --- /dev/null +++ b/source/fs/stat.h @@ -0,0 +1,51 @@ +/* $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 +#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 diff --git a/source/fs/utils.cpp b/source/fs/utils.cpp new file mode 100644 index 0000000..694a8f1 --- /dev/null +++ b/source/fs/utils.cpp @@ -0,0 +1,185 @@ +/* $Id$ + +This file is part of libmspfs +Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#include +#include +#include +#ifndef WIN32 +#include +#else +#include +#endif +#include +#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 files; + if(result.size()) + files = list_files(result); + else + files = list_files("."); + + found = false; + for(list::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 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 diff --git a/source/fs/utils.h b/source/fs/utils.h new file mode 100644 index 0000000..6a4c55a --- /dev/null +++ b/source/fs/utils.h @@ -0,0 +1,62 @@ +/* $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 diff --git a/source/path.cpp b/source/path.cpp deleted file mode 100644 index e514d36..0000000 --- a/source/path.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* $Id$ - -This file is part of libmspfs -Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ - -#include -#include -#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=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()) - return string(); - if(start==end) - return string(); - return path.path.substr(start, end-start); -} - -} // namespace FS -} // namespace Msp diff --git a/source/path.h b/source/path.h deleted file mode 100644 index e560d3f..0000000 --- a/source/path.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $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 -#include - -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(-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< -#ifdef WIN32 -#include -#endif -#include -#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 diff --git a/source/stat.h b/source/stat.h deleted file mode 100644 index 55a1fec..0000000 --- a/source/stat.h +++ /dev/null @@ -1,51 +0,0 @@ -/* $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 -#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 diff --git a/source/utils.cpp b/source/utils.cpp deleted file mode 100644 index 694a8f1..0000000 --- a/source/utils.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* $Id$ - -This file is part of libmspfs -Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ - -#include -#include -#include -#ifndef WIN32 -#include -#else -#include -#endif -#include -#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 files; - if(result.size()) - files = list_files(result); - else - files = list_files("."); - - found = false; - for(list::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 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 diff --git a/source/utils.h b/source/utils.h deleted file mode 100644 index 6a4c55a..0000000 --- a/source/utils.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $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