Files
textmate/Frameworks/io/src/move_path.cc
Allan Odgaard 9894969e67 Initial commit
2012-08-09 16:25:56 +02:00

246 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "io.h"
#include "entries.h"
#include <oak/debug.h>
OAK_DEBUG_VAR(IO_Info);
OAK_DEBUG_VAR(IO_Failure);
// ====================
// = Helper Functions =
// ====================
static bool send_move_request_to_auth_server (std::string const& src, std::string const& dst, bool overwrite)
{
D(DBF_IO_Failure, bug("request requires root (%s → %s)\n", src.c_str(), dst.c_str()););
ASSERT(false);
return false;
}
static bool is_copyable_dir (std::string const& path)
{
if(access(path.c_str(), X_OK) != 0)
{
D(DBF_IO_Failure, bug("unable to read dir %s: %s\n", path.c_str(), strerror(errno)););
return false;
}
bool res = true;
citerate(it, path::entries(path))
{
std::string const& newSrc = path::join(path, (*it)->d_name);
if((*it)->d_type == DT_REG)
{
if(access(newSrc.c_str(), R_OK) != 0)
{
D(DBF_IO_Failure, bug("unable to read file %s: %s\n", newSrc.c_str(), strerror(errno)););
res = false;
}
}
else if((*it)->d_type == DT_DIR)
{
res = is_copyable_dir(newSrc);
}
else if((*it)->d_type == DT_LNK)
{
return true;
}
else
{
D(DBF_IO_Failure, bug("unknown file type: %s (%d)\n", newSrc.c_str(), (*it)->d_type););
res = false;
}
if(!res)
break;
}
return res;
}
static bool is_copyable (std::string const& path)
{
struct stat buf;
if(lstat(path.c_str(), &buf) != 0)
{
D(DBF_IO_Failure, bug("stat(\"%s\"): %s\n", path.c_str(), strerror(errno)););
}
else if(S_ISDIR(buf.st_mode))
{
return is_copyable_dir(path);
}
else if(S_ISLNK(buf.st_mode) || access(path.c_str(), R_OK) == 0)
{
return true;
}
else
{
D(DBF_IO_Failure, bug("unable to read file %s: %s\n", path.c_str(), strerror(errno)););
}
return false;
}
// ====================
// = Public Functions =
// ====================
namespace path
{
bool copy (std::string const& src, std::string const& dst)
{
D(DBF_IO_Info, bug("%s → %s\n", src.c_str(), dst.c_str()););
ASSERTF(path::exists(src), "%s\n", src.c_str());
ASSERTF(!path::exists(dst), "%s\n", dst.c_str());
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
if(copyfile(src.c_str(), dst.c_str(), NULL, COPYFILE_ALL | COPYFILE_NOFOLLOW_SRC) != 0)
{
D(DBF_IO_Failure, bug("copyfile(\"%s\", \"%s\"): %s\n", src.c_str(), dst.c_str(), strerror(errno)););
return false;
}
bool res = true;
citerate(it, path::entries(src))
{
std::string const& newSrc = path::join(src, (*it)->d_name);
std::string const& newDst = path::join(dst, (*it)->d_name);
if((*it)->d_type == DT_DIR)
{
res = path::copy(newSrc, newDst) && res;
}
else if((*it)->d_type == DT_REG || (*it)->d_type == DT_LNK)
{
if(copyfile(newSrc.c_str(), newDst.c_str(), NULL, COPYFILE_ALL | COPYFILE_NOFOLLOW_SRC) != 0)
{
D(DBF_IO_Failure, bug("copyfile(\"%s\", \"%s\"): %s\n", newSrc.c_str(), newDst.c_str(), strerror(errno)););
res = false;
}
}
else
{
D(DBF_IO_Failure, bug("skip unknown type: %s (%d)\n", newSrc.c_str(), (*it)->d_type););
res = false;
}
}
return res;
#else
return false;
#endif
}
bool move (std::string const& src, std::string const& dst, bool overwrite)
{
D(DBF_IO_Info, bug("%s → %s\n", src.c_str(), dst.c_str()););
std::string const& dst_parent = path::parent(dst);
std::string const& src_parent = path::parent(src);
bool src_exists DB_VAR = path::exists(src);
bool dst_exists = path::exists(dst);
ASSERT(src_exists);
if(dst_exists && !overwrite)
{
D(DBF_IO_Failure, bug("destination exists: %s (and overwrite was disabled)\n", dst.c_str()););
errno = EEXIST;
return false;
}
if(!path::make_dir(dst_parent))
{
D(DBF_IO_Failure, bug("unable to create destination directory: %s\n", dst_parent.c_str()););
return false;
}
dev_t src_device = path::device(src);
dev_t dst_device = path::device(dst_parent);
if(dst_exists)
{
if(!path::remove(dst))
{
D(DBF_IO_Failure, bug("unable to remove existing destionation %s: %s\n", dst.c_str(), strerror(errno)););
return false; // if errno == EACCES then send_move_request_to_auth_server()
}
}
// when src and dst are on the same device we dont need to ensure that is_copyable(src)
if(!is_copyable(src) || !path::is_writable(src_parent) || !path::is_writable(dst_parent))
return send_move_request_to_auth_server(src, dst, overwrite);
if(src_device == dst_device)
{
if(::rename(src.c_str(), dst.c_str()) == 0)
return true;
D(DBF_IO_Failure, bug("rename(\"%s\", \"%s\"): %s\n", src.c_str(), dst.c_str(), strerror(errno)););
}
else
{
return path::copy(src, dst) && path::remove(src);
}
return false;
}
static bool remove_dir (std::string const& path)
{
bool res = true;
citerate(it, path::entries(path))
{
std::string const& newSrc = path::join(path, (*it)->d_name);
if((*it)->d_type == DT_DIR)
{
res = path::remove_dir(newSrc) && res;
}
else
{
if(unlink(newSrc.c_str()) != 0)
{
D(DBF_IO_Failure, bug("failed to unlink %s: %s\n", newSrc.c_str(), strerror(errno)););
res = false;
}
else
{
D(DBF_IO_Info, bug("unlinked %s\n", newSrc.c_str()););
}
}
}
if(res && rmdir(path.c_str()) != 0)
{
D(DBF_IO_Failure, bug("failed to rmdir %s: %s\n", path.c_str(), strerror(errno)););
res = false;
}
else
{
D(DBF_IO_Info, bug("removed directory %s\n", path.c_str()););
}
return res;
}
bool remove (std::string const& path)
{
D(DBF_IO_Info, bug("%s\n", path == NULL_STR ? "(null)" : path.c_str()););
if(path == NULL_STR)
return false;
struct stat buf;
if(lstat(path.c_str(), &buf) != 0)
{
D(DBF_IO_Failure, bug("stat(\"%s\"): %s\n", path.c_str(), strerror(errno)););
}
else if(S_ISDIR(buf.st_mode))
{
return path::remove_dir(path);
}
else if(unlink(path.c_str()) == 0)
{
D(DBF_IO_Info, bug("unlinked %s\n", path.c_str()););
return true;
}
else
{
D(DBF_IO_Failure, bug("failed to unlink %s: %s\n", path.c_str(), strerror(errno)););
}
return false;
}
} /* path */