/********************************************************
* *
* Written by Stanic Igor 2012/2013 *
* Supporting functions and helpers for *
* FileSystemController class *
* *
*******************************************************/
#ifndef FSC_HELPERS_GLOBAL_CPP
#define FSC_HELPERS_GLOBAL_CPP
#include "FileSystemController.h"
using namespace std;
using namespace boost;
namespace bfs = boost::filesystem;
#ifdef WINDOWS
#define getCurrentDir _getcwd
#elif defined(LINUX)
#define getCurrentDir getcwd
#endif
namespace
{
/* FileOperations BEGIN*/
class sortHelpers
{
private:
typedef vector<vector<F_INFO> > internalVect;
typedef vector<F_INFO> fInfosStack;
static inline void divideDirsAndFiles(fInfosPtr ptr, internalVect& dirsAndFiles)
{
dirsAndFiles.push_back(vector<F_INFO>());
dirsAndFiles.push_back(vector<F_INFO>());
for_each(ptr->begin(), ptr->end(), [ &dirsAndFiles](F_INFO& info)
{
if(info.isDirectory)
dirsAndFiles[0].push_back(std::move(info));
else
dirsAndFiles[1].push_back(std::move(info));
});
}
static void mergeInfos(fInfosPtr original, internalVect& ptrs, bool dirsFirst = true)
{
int pos = 0;
vector<F_INFO>& dirs = ptrs[0];
vector<F_INFO>& files = ptrs[1];
if(dirsFirst)
{
for(vector<F_INFO>::iterator current = dirs.begin(), last = dirs.end(); current != last; ++current)
original->operator[](pos++) = std::move(*current);
for(vector<F_INFO>::iterator current = files.begin(), last = files.end(); current != last; ++current)
original->operator[](pos++) = std::move(*current);
}
else
{
for(vector<F_INFO>::iterator current = files.begin(), last = files.end(); current != last; ++current)
original->operator[](pos++) = std::move(*current);
for(vector<F_INFO>::iterator current = dirs.begin(), last = dirs.end(); current != last; ++current)
original->operator[](pos++) = std::move(*current);
}
}
static inline void sortASC(fInfosPtr vector)
{
sort(vector->begin(), vector->end(), [](const F_INFO& f1, const F_INFO& f2)
{
return Helpers::toLower<STR_SET>(f1.name) < Helpers::toLower<STR_SET>(f2.name);
});
}
static inline void sortASC(fInfosStack& vector)
{
sort(vector.begin(), vector.end(), [](const F_INFO& f1, const F_INFO& f2)
{
return Helpers::toLower<STR_SET>(f1.name) < Helpers::toLower<STR_SET>(f2.name);
});
}
static inline void sortDESC(fInfosPtr vector)
{
sort(vector->begin(), vector->end(), [](const F_INFO& f1, const F_INFO& f2)
{
return Helpers::toLower<STR_SET>(f1.name) > Helpers::toLower<STR_SET>(f2.name);
});
}
static inline void sortDESC(fInfosStack& vector)
{
sort(vector.begin(), vector.end(), [](const F_INFO& f1, const F_INFO& f2)
{
return Helpers::toLower<STR_SET>(f1.name) > Helpers::toLower<STR_SET>(f2.name);
});
}
static inline void sortSize(fInfosPtr vector,bool ascsize = true)
{
sort(vector->begin(), vector->end(), [ascsize](const F_INFO& f1, const F_INFO& f2)
{
return ascsize ? (f1.length < f2.length) : (f1.length > f2.length);
});
}
static inline void sortSize(fInfosStack& vector,bool ascsize = true)
{
sort(vector.begin(), vector.end(), [ascsize](const F_INFO& f1, const F_INFO& f2)
{
return ascsize ? (f1.length < f2.length) : (f1.length > f2.length);
});
}
static void sortDirFirstAsc(fInfosPtr infos, bool isAsc = true, bool dirsFirst = true)
{
internalVect vectors;
divideDirsAndFiles(infos, vectors);
if(isAsc)
{
if(dirsFirst)
{
sortASC(vectors[0]);
sortASC(vectors[1]);
}
else
{
sortASC(vectors[1]);
sortASC(vectors[0]);
}
}
else
{
if(dirsFirst)
{
sortDESC(vectors[0]);
sortDESC(vectors[1]);
}
else
{
sortDESC(vectors[1]);
sortDESC(vectors[0]);
}
}
mergeInfos(infos, vectors, dirsFirst);
}
static void sortDirFirst_ASC_SIZE(fInfosPtr infos, bool isAsc = true, bool dirsFirst = true)
{
internalVect vectors;
divideDirsAndFiles(infos, vectors);
if(dirsFirst)
{
sortSize(vectors[0], isAsc);
sortSize(vectors[1], isAsc);
}
else
{
sortSize(vectors[1],isAsc);
sortSize(vectors[0],isAsc);
}
mergeInfos(infos, vectors, dirsFirst);
}
public:
static void sortInfos(fInfosPtr finfos, _SortInfo sorting)
{
switch(sorting)
{
case _SortInfo::none:
break;
case _SortInfo::asc:
sortASC(finfos);
break;
case _SortInfo::asc_size:
sortSize(finfos);
break;
case _SortInfo::desc:
sortDESC(finfos);
break;
case _SortInfo::desc_size:
sortSize(finfos, false);
break;
case _SortInfo::dirFirst_asc:
sortDirFirstAsc(finfos);
break;
case _SortInfo::dirFirst_asc_size:
sortDirFirst_ASC_SIZE(finfos);
break;
case _SortInfo::dirFirst_desc:
sortDirFirstAsc(finfos, false);
break;
case _SortInfo::dirFirst_desc_size:
sortDirFirst_ASC_SIZE(finfos, false);
break;
}
}
};
//get current working directory
STRING_TYPE CurrentDir()
{
std::unique_ptr<char[]> currPath(new char[FILENAME_MAX]);
getCurrentDir(currPath.get(), FILENAME_MAX);
STRING_TYPE path(TO_STRING_TYPE(currPath.get()));
return path;
}
//in case of relative path or filename
STRING_TYPE correctPath(const STRING_TYPE& pathToCorrect)
{
bfs::path p(pathToCorrect);
bool needsCorrection = false;
bfs::path parent;
if(!p.is_complete())
{
parent = CurrentDir();
parent /= p;
needsCorrection = true;
}
if(needsCorrection)
return TO_STRING_TYPE(parent.string());
return pathToCorrect;
}
//Get file or folder size if possible. Else, return 0 as size.
ULLONG getFileOrFolderSize(const STRING_TYPE& path)
{
ULLONG result = 0;
if(bfs::exists(path))
{
if(boost::filesystem::is_directory(path))
{
boost::system::error_code ec;
try //the whole recursion will fail if permission denied on one file/folder
{
for(boost::filesystem::recursive_directory_iterator end, current(path, ec); current != end; ++current)
{
if (!bfs::is_directory(current->status()))
result += file_size(current->path());
}
}catch(...){}
}
else
{
try
{
boost::system::error_code ec;
result = bfs::file_size(path, ec);
}catch(...){}
}
}
return result;
}
//another implementation to get file size...
ULLONG internal_FileSize(const char* sFileName)
{
std::ifstream f;
f.open(sFileName, std::ios_base::binary | std::ios_base::in);
if (!f.good() || f.eof() || !f.is_open()) { return 0; }
f.seekg(0, std::ios_base::beg);
std::ifstream::pos_type begin_pos = f.tellg();
f.seekg(0, std::ios_base::end);
return static_cast<ULLONG>(f.tellg() - begin_pos);
}
typedef std::pair<STRING_TYPE::size_type, F_INFO> SEARCH_PAIR;
inline bool increment_dir_iter(boost::filesystem::directory_iterator& it)
{
boost::system::error_code ec;
it.increment(ec);
if(ec.value() != 0)
return false;
return true;
}
#ifdef LINUX
//if underlying link cannot be obtained, return empty string for path
inline std::string getUnderlyingSymlinkLocation(const std::string& linkPath)
{
if(bfs::symbolic_link_exists(linkPath))
{
char buff[1024];
ssize_t len = readlink(linkPath.c_str(), buff, sizeof(buff)-1);
if (len != -1)
{
buff[len] = '\0';
std::string linked(buff);
if(!bfs::path(linked).is_complete())
{
bfs::path temp(bfs::path(linkPath).parent_path());
temp /= linked;
linked = temp.string();
}
return linked;
}
}
return EMPTYSTR;
}
#endif
//Basic getEntries() - get ALL REGULAR available directory entries
//getAllEntries - in case it is true, get ALL PRESENT entries
//If SearchCriteria isn't nullptr performs search for results
fInfosPtr getEntries(const STRING_TYPE& path,
bool getAllEntries = false,
bool firstLevelOnly = false,
const SearchWrapperBase* wrapper = nullptr,
vector<SEARCH_PAIR>* searchResults = nullptr,
bool isRecursion = false,
vector<SEARCH_PAIR>::size_type filesResults = 0)
{
vector<F_INFO>* files = nullptr;
if(!bfs::exists(path) || !bfs::is_directory(path))
return files;
if(!isRecursion)
files = new vector<F_INFO>();
const SearchCriteria* criteria = dynamic_cast<const SearchCriteria*>(wrapper);
const SearchFunction* funcSearchPredicate = dynamic_cast<const SearchFunction*>(wrapper);
vector<SEARCH_PAIR> prioritySearch;
bfs::directory_iterator iter_end;
bfs::directory_iterator it;
try
{
it = bfs::directory_iterator(path);
}
catch(...)
{
return files;
}
while(it != iter_end)
{
STRING_TYPE::size_type priority;
STRING_TYPE name;
STRING_TYPE path;
STRING_TYPE sym_Link_Path;
#ifdef WINDOWS
name = it->path().filename().wstring();
path = it->path().wstring();
#elif defined(LINUX)
name = it->path().filename().string();
path = it->path().string();
#endif
ULLONG len = 0;
bool isSymLink = false;
boost::posix_time::ptime time;
try
{
if(bfs::is_symlink(path))
{
isSymLink = true;
//if(!symbolic_link_exists(path))
// continue;
#ifdef LINUX
try
{
sym_Link_Path = getUnderlyingSymlinkLocation(path);
}
catch(...){}
#endif
}
}
catch(...)
{
if(increment_dir_iter(it))
continue;
else
break;
}
bool is_dir;
try
{
//It happens that status() throws in case of some symlinks
is_dir = bfs::is_directory(it->status());
}
catch(...)
{
//continue;
if(increment_dir_iter(it))
continue;
else
break;
}
//search by standard criteria
if(criteria != nullptr)
{
//limit number of results. Value of 0 means all results
if(criteria->NumberOfResults != 0 && (isRecursion ? (criteria->NumberOfResults <= searchResults->size() + filesResults) :
(criteria->NumberOfResults <= prioritySearch.size() + files->size())))
{
break;
}
if(is_dir && !isSymLink && !firstLevelOnly)
{
//in this case firstLevel only is always false but I decided to
//keep it for the sake of keeping code more clear and understandable
if(!isRecursion)
getEntries(path, getAllEntries, firstLevelOnly, wrapper, &prioritySearch, true);
else
getEntries(path, getAllEntries, firstLevelOnly, wrapper, searchResults, true);
}
if(criteria->InspectNameOnly ?
!criteria->operator()(name, priority) :
!criteria->operator ()(path, priority))
{
if(increment_dir_iter(it))
continue;
else
break;
}
}
if(!getAllEntries)
{
if(!is_dir && !bfs::is_regular_file(path) && !isSymLink)
{
if(increment_dir_iter(it))
continue;
else
break;
}
}
try
{
len = is_dir ? 0 : (ULLONG)bfs::file_size(path);
}
catch(...)
{
#ifdef LINUX
len = is_dir ? 0 : internal_FileSize(path.c_str());
#endif
}
try
{
//if symlink which cannot access the location pointed, time will be invalid..
time_t _time = bfs::last_write_time(path);
time = Helpers::LocalFromUTC(_time);
}
catch(...)
{}
//from this point we musn't use name and path !!!
#ifdef WINDOWS
F_INFO currentEntry(std::move(name), std::move(path), len, is_dir, std::move(time), isSymLink);
#elif defined(LINUX)
F_INFO currentEntry(std::move(name), std::move(path), len, is_dir, std::move(time), isSymLink, std::move(sym_Link_Path));
#endif
if(!getAllEntries)
{
if(
currentEntry.isHidden()
#ifdef WINDOWS
||currentEntry.isSystem()||
currentEntry.permission_denied()
#endif
)
{
if(increment_dir_iter(it))
continue;
else
break;
}
}
//Standard folder listing
if(funcSearchPredicate == nullptr && criteria == nullptr)
{
files->push_back(std::move(currentEntry));
}
else if(funcSearchPredicate != nullptr) //search by user defined Predicate
{
if(is_dir && !isSymLink && !firstLevelOnly)
{
//in this case firstLevel only is always false but I decided to
//keep it for the sake of keeping code more clear and understandable
if(!isRecursion)
getEntries(currentEntry.path, getAllEntries, firstLevelOnly, wrapper, &prioritySearch, true, files->size());
else
getEntries(currentEntry.path, getAllEntries, firstLevelOnly, wrapper, searchResults, true, filesResults);
}
if( funcSearchPredicate->operator ()(currentEntry))
{
if(funcSearchPredicate->NumberOfResults != 0)
{
if((isRecursion ? (funcSearchPredicate->NumberOfResults <= (searchResults->size() + filesResults)) :
(funcSearchPredicate->NumberOfResults <= (prioritySearch.size()) + files->size())))
{
break;
}
}
if(!isRecursion)
files->push_back(std::move(currentEntry));
else
searchResults->push_back(pair<string::size_type, F_INFO>(priority, std::move(currentEntry)));
}
}
else if(criteria != nullptr) //search by criteria
{
if(!isRecursion)
prioritySearch.push_back(pair<string::size_type, F_INFO>(priority, std::move(currentEntry)));
else
searchResults->push_back(pair<string::size_type, F_INFO>(priority, std::move(currentEntry)));
}
if(increment_dir_iter(it))
continue;
else
break;
}
if(criteria != nullptr && !isRecursion)
{
sort(prioritySearch.begin(), prioritySearch.end(), [](const SEARCH_PAIR& p1, const SEARCH_PAIR& p2)
{
return p1.first < p2.first;
});
}
if(!isRecursion && (prioritySearch.size() > 0))
{
if(files->capacity() < prioritySearch.size())
files->reserve(prioritySearch.size());
for(SEARCH_PAIR& p: prioritySearch)
{
files->push_back(std::move(p.second));
}
if(funcSearchPredicate != nullptr)
{
sort(files->begin(), files->end(), [](const F_INFO& fi1, const F_INFO& fi2)
{
return fi1.name < fi2.name;
});
}
}
return files;
}
//getFiles(), Apply sorting criteria with enum _SortInfo values
fInfosPtr inline getEntries(const STRING_TYPE& path, _SortInfo sorting, bool allEntries = false)
{
fInfosPtr fInfos = getEntries(path, allEntries);
if(fInfos != nullptr && sorting != _SortInfo::none)
sortHelpers::sortInfos(fInfos, sorting);
return fInfos;
}
inline OperationResult createOKOpResult(const STRING_TYPE& source)
{
return OperationResult(true, source, CHRARR_CON("") );
}
inline OperationResult createOpResult(const STRING_TYPE& source, const STRING_TYPE& target, system::error_code& ec)
{
if(ec.value() != 0)
{
STRING_TYPE msg = CHRARR_CON("ErrorVal:") + Helpers::ToString<int,STR_SET>(ec.value())
+ CHRARR_CON("\nMsg:") + TO_STRING_TYPE(ec.message()) + CHRARR_CON("\nTargetPath:") + target;
return OperationResult(false, source, std::move(msg));
}
return createOKOpResult(source);
}
inline bool isRoot(OpResults& results, const STRING_TYPE& source)
{
bfs::path root(bfs::path(source).root_path());
if(root == bfs::path(source))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Source destination is root - not allowed.")));
return true;
}
return false;
}
inline bool sourceCheck(OpResults& results, const STRING_TYPE& source)
{
/*if(bfs::is_symlink(source))
{
if(!bfs::symbolic_link_exists(source))
{
results.AddResult(OperationResult(false, source, "Symbolic Link does not exist!"));
return false;
}
}
else
{*/
if(bfs::is_symlink(source))
{
if(!bfs::symbolic_link_exists(source))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("SymLink does not exist!")));
return false;
}
}
else if(!bfs::exists(source))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Source does not exist!")));
return false;
}
//}
return true;
}
bool checkIfPathIsChild(const bfs::path& p1, const bfs::path& p2)
{
bool res = false;
for(bfs::path::iterator it1(p1.begin()), it2(p2.begin()); it1 != p1.end() && it2 != p2.end(); ++it1, ++it2)
{
if(*it1 == *it2)
res = true;
else
{
res = false;
break;
}
}
return res;
}
void checkForBasicCopyMoveErrors(OpResults& results, const STRING_TYPE & source, const STRING_TYPE & target)
{
if(sourceCheck(results, source) && !isRoot(results, source))
{
/*if(!overwrite && bfs::exists(target))
{
results.AddResult(OperationResult(false, source,
STRING_TYPE(CHRARR_CON("Target exists and is not allowed to be overwritten!\nTarget") + target)));
}*/
if(Helpers::toLower<STR_SET>(source) == Helpers::toLower<STR_SET>(target))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Source and Target destination are the same!")));
}
else if(checkIfPathIsChild(target, source))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Copying source to containing target not allowed!")));
}
}
}
inline std::string getFileDeletionTime()
{
time_t _time;
time(&_time);
std::string locFromUtc =
boost::posix_time::to_iso_extended_string(Helpers::LocalFromUTC(_time));
return locFromUtc;
}
//handle x*.y, *x.y, *.y, *x*.y paths with wildcard substitution char
#ifdef WINDOWS
#define TOLOWERSTR(x) Helpers::toLower<STR_SET>(x)
#elif defined(LINUX)
//We shouldn't generalize lowering chars across different platforms
//since Linux is case sensitive by default
//so this version of macro won't do anything to underlying string
#define TOLOWERSTR(x) x
#endif
vector<STRING_TYPE> handleSubstChars(const STRING_TYPE& orgPath)
{
vector<STRING_TYPE> results;
boost::filesystem::path p(orgPath);
STRING_TYPE filename = TO_STRING_TYPE(p.filename().string());
std::string extension_str = p.extension().string();
STRING_TYPE extension = TO_STRING_TYPE(extension_str);
STRING_TYPE parent_dir = TO_STRING_TYPE(p.parent_path().string());
//doesn't have '*' wildchard char
static REGEX noe(CHRARR_CON("[^\\*]*"));
//match *x.y
static const REGEX e(CHRARR_CON("\\*[^\\*]*\\.[^\\*]*"));
//match x*.y
static const REGEX e2(CHRARR_CON("[^\\*]*\\*\\.[^\\*]*"));
//match *.y
static const REGEX eext(CHRARR_CON("\\*\\.[^\\*]*"));
//match *x*.y
static const REGEX e3(CHRARR_CON("\\*[^\\*]*\\*\\.[^\\*]*"));
//match x*.*
static const REGEX e4(CHRARR_CON("[^\\*]*\\*\\.\\*"));
unique_ptr<SearchFunction> sf = nullptr;
STRING_TYPE fname;
if(!boost::regex_match(filename, noe))
{
if(p.filename().string() == "*.*")
{
unique_ptr<vector<F_INFO> > f_infos(FileSystemController::GetEntries(parent_dir,_SortInfo::none, true));
//results.reserve(f_infos->size());
transform(f_infos->begin(), f_infos->end(), back_inserter(results), [](const F_INFO& item)
{
return item.path;
});
}
else if(boost::regex_match(filename, eext))//match *.y
{
sf = unique_ptr<SearchFunction>(new SearchFunction([&p, &parent_dir, &extension](const F_INFO& fi)
{
boost::filesystem::path p1(fi.path);
//check if this is in the same directory as substitute path expression
if(p1.parent_path() == boost::filesystem::path(parent_dir))
return boost::filesystem::path(fi.path).extension() == p.extension() && !extension.empty();
else return false;
}));
}
else if(boost::regex_match(filename,e))//match *x.y
{
fname = filename.substr(1,filename.find(extension) - 1);//discard first '*' and last '.'
sf = unique_ptr<SearchFunction>(new SearchFunction([&fname, &parent_dir, &extension](const F_INFO& fi)
{
boost::filesystem::path p1(fi.path);
//check if this is in the same directory as substitute path expression
if(p1.parent_path() == boost::filesystem::path(parent_dir) && !extension.empty())//*x.y
{
REGEX criteria(CHRARR_CON(".*\\Q") + TOLOWERSTR(fname) + CHRARR_CON("\\E\\") +
TOLOWERSTR(extension)); //extensions begins with '.' which is why we need \\ previously
return boost::regex_match(TOLOWERSTR(fi.name), criteria);
}
else
return false;
}));
}
else if(boost::regex_match(filename, e2))//match x*.y
{
STRING_TYPE::size_type posEnd;
posEnd = filename.find_last_of(CHRARR_CON(".")) - 1;//discard last "*."
if(posEnd != STRING_TYPE::npos)
{
fname = filename.substr(0, posEnd);
sf = unique_ptr<SearchFunction>(new SearchFunction([&fname, &parent_dir, &extension](const F_INFO& fi)
{
boost::filesystem::path p1(fi.path);
//check if this is in the same directory as substitute path expression
if(p1.parent_path() == boost::filesystem::path(parent_dir) && !extension.empty())//x*.y
{
REGEX criteria(CHRARR_CON("\\Q") + TOLOWERSTR(fname) + CHRARR_CON("\\E.*\\")
+ TOLOWERSTR(extension));
return boost::regex_match(TOLOWERSTR(fi.name), criteria);
}
else
return false;
}));
}
}
else if(boost::regex_match(filename, e3))//match *x*.y
{
fname = filename.substr(1, filename.find_last_of(CHRARR_CON("*")) - 1);//discard first and last "*"
sf = unique_ptr<SearchFunction>(new SearchFunction([&fname, &parent_dir, &extension](const F_INFO& fi)
{
boost::filesystem::path p1(fi.path);
//check if this is in the same directory as substitute path expression
if(p1.parent_path() == boost::filesystem::path(parent_dir) && !extension.empty())
{
REGEX criteria(CHRARR_CON(".*\\Q") + TOLOWERSTR(fname) + CHRARR_CON("\\E.*\\")
+ TOLOWERSTR(extension));
return boost::regex_match(TOLOWERSTR(fi.name), criteria);
}
else
return false;
}));
}
else if(boost::regex_match(filename, e4))//match x*.*
{
STRING_TYPE::size_type posEnd;
posEnd = filename.find(CHRARR_CON("*.*"));//discard last "*."
if(posEnd != STRING_TYPE::npos)
{
fname = filename.substr(0, posEnd);
sf = unique_ptr<SearchFunction>(new SearchFunction([&fname, &parent_dir, &extension](const F_INFO& fi)
{
boost::filesystem::path p1(fi.path);
//check if this is in the same directory as substitute path expression
if(p1.parent_path() == boost::filesystem::path(parent_dir))
{
REGEX criteria(CHRARR_CON("\\Q") + TOLOWERSTR(fname) + CHRARR_CON("\\E.*"));
return boost::regex_match(TOLOWERSTR(fi.name), criteria);
}
else
return false;
}));
}
}
if(sf != nullptr)
{
unique_ptr<vector<F_INFO> > ptrRes(getEntries(parent_dir, true, true, (SearchWrapperBase*)sf.get()));//FileSystemController::FindFile(parent_dir, (SearchWrapperBase*)sf.get(), true));
if(ptrRes != nullptr)
{
for(F_INFO& f: *ptrRes)
{
results.push_back(f.path);
}
}
}
}
else
results.push_back(orgPath);
return results;
}
#undef TOLOWERSTR
//real delete operation
OpResults deleteItem(const STRING_TYPE &source)
{
OpResults results;
system::error_code ec;
if(sourceCheck(results, source))
{
if(bfs::is_directory(source))
{
std::shared_ptr<vector<F_INFO> > entries(getEntries(source, true));
if(entries->size() > 0)
{
for_each(entries->begin(), entries->end(), [=,&results, &source](const F_INFO& info)
{
if(!info.permission_denied())
{
bfs::path tempTarget(source);
bfs::path currentPath(info.path);
tempTarget /= currentPath.leaf();
results += deleteItem(TO_STRING_TYPE(tempTarget.string()));
}
else
results.AddResult(OperationResult(false, info.path, CHRARR_CON("access_denied")));
});
#ifdef LINUX
if(bfs::is_symlink(source))
{
if(unlink(source.c_str()) != 0)
results.AddResult(OperationResult(false, source, "Unsuccessfull removal of symbolic link"));
}
else
#endif
{
bfs::remove(bfs::path(source),ec);
results.AddResult(createOpResult(source, CHRARR_CON("1"), ec));
}
}
else
{
#ifdef LINUX
if(bfs::is_symlink(source))
{
if(unlink(source.c_str()) != 0)
results.AddResult(OperationResult(false, source, "Unsuccessfull removal of symbolic link"));
}
else
#endif
{
bfs::remove(bfs::path(source),ec);
results.AddResult(createOpResult(source, CHRARR_CON("2"), ec));
}
}
}
else
{
#ifdef WINDOWS
//WIN32_FILE_ATTRIBUTE_DATA fdata;
try
{
//GetFileAttributesWindows(source, fdata);
/*if((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != FALSE)
{
results.AddResult(OperationResult(false, source,
CHRARR_CON("File has system file attribute. No deletion")));
return results;
}
else
{*/
if(SetFileAttributesW(source.c_str(), FILE_ATTRIBUTE_NORMAL) != TRUE)
throw;
//}
}
catch(...)
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Cannot set file attributes to NORMAL so it can be deleted")));
return results;
}
#endif
bfs::remove(bfs::path(source),ec);
results.AddResult(createOpResult(source, CHRARR_CON(""), ec));
}
}
return results;
}
OpResults copyMerge(const STRING_TYPE &source, const STRING_TYPE &target,
bool overwrite = false,
bool allowMerge = false,
bool isRecursion = false,
vector<bfs::path>* tempCopiedDirs = nullptr,
vector<bfs::path>* tempCopiedOther = nullptr,
vector<bfs::path>* tempOverwritten = nullptr)
{
OpResults results;
checkForBasicCopyMoveErrors(results, source, target);
if(results.hasErrors())
return results;
bfs::path temp = bfs::path(source);
bfs::path newSourcePath = bfs::path(target); //target destination
bool newSourcePathExists = bfs::exists(newSourcePath);
if(newSourcePathExists)
{
if(!bfs::is_directory(newSourcePath) && bfs::is_directory(temp))
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Invalid attempt to copy directory into existing file!")));
return results;
}
}
if(newSourcePathExists)
{
if(bfs::is_directory(newSourcePath) && !bfs::is_directory(temp))// && !overwrite)
newSourcePath /= temp.leaf();
}
vector<bfs::path> copiedDirs;
vector<bfs::path> copiedOther;
vector<bfs::path> overwritten;
static STRING_TYPE tempNameAddition(CHRARR_CON("_FS_TEMP_OP_"));
if(!results.hasErrors())
{
system::error_code ec;
if(bfs::is_directory(source))
{
if(!bfs::exists(newSourcePath))
{
bfs::copy_directory(source, newSourcePath, ec);
if(ec.value() != 0)
{
results.AddResult(createOpResult(source, TO_STRING_TYPE(newSourcePath.string()), ec));
if(isRecursion)
return results;
}
if(!isRecursion)
copiedDirs.push_back(newSourcePath);
else
tempCopiedDirs->push_back(newSourcePath);
}
else
{
if(overwrite)
{
STRING_TYPE tmpDirName = TO_STRING_TYPE(newSourcePath.filename().string());
tmpDirName += tempNameAddition;
tmpDirName += Helpers::createGUID<STR_SET>();
bfs::path newTmpPath(newSourcePath.parent_path());
newTmpPath /= tmpDirName;
bfs::rename(newSourcePath, newTmpPath, ec);
if(ec.value() != 0)
{
results.AddResult(createOpResult(source, TO_STRING_TYPE(newTmpPath.string()), ec));
if(isRecursion)
return results;
}
bfs::copy_directory(source, newSourcePath, ec);
if(ec.value() != 0)
{
results.AddResult(createOpResult(source, TO_STRING_TYPE(newSourcePath.string()), ec));
if(isRecursion)
return results;
}
if(!isRecursion)
{
copiedDirs.push_back(newSourcePath);
overwritten.push_back(newTmpPath);
}
else
{
tempCopiedDirs->push_back(newSourcePath);
tempOverwritten->push_back(tmpDirName);
}
}
else if(!allowMerge)
{
results.AddResult(OperationResult(false, source, CHRARR_CON("Destination exists and is not supposed to be overwritten")));
return results;
}
}
//in this case, if we are copying All contents we should use
unique_ptr<vector<F_INFO> > entries(getEntries(source, true));
if(entries->size() > 0)
{
for_each(entries->begin(), entries->end(), [=,&results,
&newSourcePath,
&copiedDirs,
&copiedOther,
&overwritten,
&tempCopiedDirs,
&tempCopiedOther,
&tempOverwritten
](const F_INFO& info)
{
if(!info.permission_denied())
{
bfs::path tempTarget(newSourcePath);
bfs::path currentPath(info.path);
tempTarget /= currentPath.leaf();
if(!isRecursion)
results += copyMerge(info.path,
TO_STRING_TYPE(tempTarget.string()),
overwrite,
allowMerge,
true,
&copiedDirs,
&copiedOther,
&overwritten);
else
results += copyMerge(info.path,
TO_STRING_TYPE(tempTarget.string()),
overwrite,
allowMerge,
true,
tempCopiedDirs,
tempCopiedOther,
tempOverwritten);
}
else
{
results.AddResult(OperationResult(false, info.path, CHRARR_CON("access_denied")));
}
});
}
}
else
{
if(bfs::is_symlink(source))
{
if(!bfs::symbolic_link_exists(newSourcePath) && !bfs::exists(newSourcePath))
{
bfs::copy_symlink(bfs::path(source), newSourcePath, ec);
if(!isRecursion)
copiedOther.push_back(newSourcePath);
else
tempCopiedOther->push_back(newSourcePath);
}
else
{
if(overwrite)
{
bfs::path tempOld = newSourcePath;
#ifdef WINDOWS
STRING_TYPE tempName(newSourcePath.filename().wstring() + tempNameAddition + Helpers::createGUID<STR_SET>());
#elif defined(LINUX)
STRING_TYPE tempName(newSourcePath.filename().string() + tempNameAddition+ Helpers::createGUID<STR_SET>());
#endif
tempOld = newSourcePath.parent_path();
tempOld /= tempName;
bfs::rename(newSourcePath, tempOld, ec);
if(ec.value() == 0)//!= 0)
{
bfs::copy_symlink(bfs::path(source), newSourcePath, ec);
if(!isRecursion)
{
copiedOther.push_back(newSourcePath);
overwritten.push_back(tempOld);
}
else
{
tempCopiedOther->push_back(newSourcePath);
tempOverwritten->push_back(tempOld);
}
}
}
else //Destination exists && overwrite ISN'T DEFINED!!!
{
if(!allowMerge)
{
results.AddResult(OperationResult(false, source,
TO_STRING_TYPE("Destination already exists and isn't supposed to be overwritten!")));
if(isRecursion)
return results;;
}
}
}
}
else if(bfs::is_regular_file(source))
{
if(!bfs::exists(newSourcePath))
{
bfs::copy_file(bfs::path(source), newSourcePath, ec);
if(!isRecursion)
copiedOther.push_back(newSourcePath);
else
tempCopiedOther->push_back(newSourcePath);
}
else
{
if(overwrite)
{
bfs::path tempOld = newSourcePath;
#ifdef WINDOWS
STRING_TYPE tempName(newSourcePath.filename().wstring() + tempNameAddition + Helpers::createGUID<STR_SET>());
#elif defined(LINUX)
STRING_TYPE tempName(newSourcePath.filename().string() + tempNameAddition + Helpers::createGUID<STR_SET>());
#endif
tempOld = newSourcePath.parent_path();
tempOld /= tempName;
bfs::rename(newSourcePath, tempOld, ec);
if(ec.value() == 0)
{
bfs::copy_file(bfs::path(source), newSourcePath, ec);
if(!isRecursion)
{
copiedOther.push_back(newSourcePath);
overwritten.push_back(tempOld);
}
else
{
tempCopiedOther->push_back(newSourcePath);
tempOverwritten->push_back(tempOld);
}
}
}
else
{
if(!allowMerge)
{
results.AddResult(OperationResult(false, source,
TO_STRING_TYPE("Destination already exists and isn't supposed to be overwritten!")));
if(isRecursion)
return results;;
}
}
}
}
if(ec.value() != 0)
{
results.AddResult(createOpResult(source,
TO_STRING_TYPE(newSourcePath.string()),
ec));
if(isRecursion)
return results;
}
else
results.AddResult(createOKOpResult(source));
}
if(!isRecursion)//overwrite &&
{
if(!results.hasErrors())
{
for(const bfs::path& p : overwritten)
{
if(bfs::exists(p))
{
results += deleteItem(TO_STRING_TYPE(p.string()));
}
}
}
else
{
if(!allowMerge)
{
for_each(copiedOther.cbegin(), copiedOther.cend(), [&results](const bfs::path& p)
{
results += deleteItem(TO_STRING_TYPE(p.string()));
});
}
for_each(overwritten.cbegin(), overwritten.cend(), [&results](const bfs::path& p)
{
STRING_TYPE tmpPath = TO_STRING_TYPE(p.string());
tmpPath = tmpPath.substr(0, tmpPath.find_last_of(tempNameAddition)
- tempNameAddition.length());
boost::system::error_code ec;
bfs::rename(p, tmpPath, ec);
if(ec.value() != 0)
results.AddResult(createOpResult(TO_STRING_TYPE(p.string()), TO_STRING_TYPE("temp renaming operation"), ec));
});
if(!allowMerge)
{
for_each(copiedDirs.cbegin(), copiedDirs.cend(), [&results](const bfs::path& p)
{
results += deleteItem(TO_STRING_TYPE(p.string()));
});
}
}
}
}
return results;
}
//If Move() operation experiences errors, it will be identical to Copy operation - it won't delete the source
OpResults moveItem(const STRING_TYPE &source, const STRING_TYPE &target, bool overwrite)
{
OpResults results;
if(!overwrite)
{
system::error_code ec;
if(!bfs::exists(target))
{
bfs::rename(bfs::path(source), target, ec); // try quick way if possible...
if(ec.value() == 0) //18 - cross-device error
{
results.AddResult(createOpResult(source, target, ec));
return results;
}
}
}
//Don't allow merging for moved items
results = copyMerge(source, target, overwrite, false);
if(!results.hasErrors())
{
results += deleteItem(source);
}
return results;
}
} //Global part of NS END
/* FileOperations END*/
#endif