From 1834fcf2465c77c387bc92df5529d8631abaffa5 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 10 Aug 2008 12:07:57 +0000 Subject: [PATCH] New functions in utils.h: readlink realpath New functions in dir.h: get_sys_conf_dir get_sys_data_dir New functions in stat.h: lstat is_link Include path.h in other headers to make implicit conversions work --- source/dir.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ source/dir.h | 9 +++++-- source/stat.cpp | 25 +++++++++++++++++++ source/stat.h | 12 +++++++-- source/utils.cpp | 52 +++++++++++++++++++++++++++++++++++++++ source/utils.h | 10 ++++++-- 6 files changed, 166 insertions(+), 6 deletions(-) diff --git a/source/dir.cpp b/source/dir.cpp index 212200d..09a89f1 100644 --- a/source/dir.cpp +++ b/source/dir.cpp @@ -14,6 +14,7 @@ Distributed under the LGPL #endif #include #include +#include #include "dir.h" #include "path.h" #include "stat.h" @@ -24,6 +25,44 @@ 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 Path &argv0) +{ + static Path last_argv0; + static Path bin_dir; + + if(!(argv0==last_argv0)) + { + Path exe; + if(argv0.size()==1) + { + 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; @@ -145,6 +184,31 @@ Path get_user_data_dir(const string &appname) #endif } +Path get_sys_conf_dir(const Path &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 Path &argv0, const string &appname) +{ + Path dir=get_bin_dir(argv0); + + if(dir[-1]=="bin" || dir[-1]=="sbin") + return dir/".."/"share"/appname; + else + return dir; +} + void chdir(const Path &path) { if(::chdir(path.str().c_str())==-1) diff --git a/source/dir.h b/source/dir.h index 3b68463..30ef33d 100644 --- a/source/dir.h +++ b/source/dir.h @@ -10,12 +10,11 @@ Distributed under the LGPL #include #include +#include "path.h" namespace Msp { namespace FS { -class Path; - /// Creates a directory void mkdir(const Path &path, int mode); @@ -43,6 +42,12 @@ 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 Path &argv0); + +/// Returns a directory containing immutable system-wide data +Path get_sys_data_dir(const Path &argv0, const std::string &appname); + /// Changes the current working directory void chdir(const Path &); diff --git a/source/stat.cpp b/source/stat.cpp index 3bfae74..c5bfb64 100644 --- a/source/stat.cpp +++ b/source/stat.cpp @@ -29,6 +29,23 @@ struct stat stat(const Path &fn) 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; @@ -50,5 +67,13 @@ bool is_dir(const Path &path) return false; } +bool is_link(const Path &path) +{ + struct stat st; + if(lstat(path, st)==0) + return S_ISLNK(st.st_mode); + return false; +} + } // namespace FS } // namespace Msp diff --git a/source/stat.h b/source/stat.h index 4970ff8..55a1fec 100644 --- a/source/stat.h +++ b/source/stat.h @@ -9,12 +9,11 @@ Distributed under the LGPL #define MSP_FS_STAT_H_ #include +#include "path.h" namespace Msp { namespace FS { -class Path; - /** 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 @@ -28,6 +27,12 @@ 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); @@ -37,6 +42,9 @@ 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 diff --git a/source/utils.cpp b/source/utils.cpp index 0ca1d60..a0c2486 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -15,6 +15,7 @@ Distributed under the LGPL #include #include "dir.h" #include "path.h" +#include "stat.h" #include "utils.h" using namespace std; @@ -78,6 +79,57 @@ Path fix_case(const Path &path) return result; } +Path readlink(const Path &link) +{ +#ifdef WIN32 + 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 unlink(const Path &path) { if(::unlink(path.str().c_str())==-1) diff --git a/source/utils.h b/source/utils.h index a6d9568..9b4a1e0 100644 --- a/source/utils.h +++ b/source/utils.h @@ -8,11 +8,11 @@ Distributed under the LGPL #ifndef MSP_FS_UTILS_H_ #define MSP_FS_UTILS_H_ +#include "path.h" + namespace Msp { namespace FS { -class Path; - /// Extracts the last component of the path. std::string basename(const Path &); @@ -37,6 +37,12 @@ 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); -- 2.45.2