#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"
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;
#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)
#include <list>
#include <string>
+#include "path.h"
namespace Msp {
namespace FS {
-class Path;
-
/// Creates a directory
void mkdir(const Path &path, int mode);
/// 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 &);
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;
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
#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
*/
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 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
#include <msp/strings/utils.h>
#include "dir.h"
#include "path.h"
+#include "stat.h"
#include "utils.h"
using namespace std;
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)
#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 &);
*/
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);