New functions in utils.h: readlink realpath
authorMikko Rasa <tdb@tdb.fi>
Sun, 10 Aug 2008 12:07:57 +0000 (12:07 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sun, 10 Aug 2008 12:07:57 +0000 (12:07 +0000)
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
source/dir.h
source/stat.cpp
source/stat.h
source/utils.cpp
source/utils.h

index 212200d04918a368b11a0f64050c3ee49cef938e..09a89f1f1f29def46e0de231deb6a309a0669140 100644 (file)
@@ -14,6 +14,7 @@ Distributed under the LGPL
 #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"
@@ -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<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;
@@ -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)
index 3b68463ec36f37f5944ff63a65d54191b18d915f..30ef33dbeed4120747759f23cc71882e47bf5459 100644 (file)
@@ -10,12 +10,11 @@ Distributed under the LGPL
 
 #include <list>
 #include <string>
+#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 &);
 
index 3bfae74c348dc2429b135efc189878514c56d518..c5bfb642ab92b65376dba5f7f13e4a10eb7a0d94 100644 (file)
@@ -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
index 4970ff8634b9d1a1cf99061c1dc73f27e4054bc6..55a1fec51b575f50b0208878a4e066a20383c98f 100644 (file)
@@ -9,12 +9,11 @@ Distributed under the LGPL
 #define MSP_FS_STAT_H_
 
 #include <sys/stat.h>
+#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
 
index 0ca1d6064e1bd2cfcfa48bd8a3b649efc7333ca0..a0c2486f17402593b2e48199fc9d689923098809 100644 (file)
@@ -15,6 +15,7 @@ Distributed under the LGPL
 #include <msp/strings/utils.h>
 #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<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 unlink(const Path &path)
 {
        if(::unlink(path.str().c_str())==-1)
index a6d956869bf633d531d35e24636b8b37519e35f4..9b4a1e0f706d972f3f3cfbc3472a047cbd35129e 100644 (file)
@@ -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);