Click here to Skip to main content
15,896,557 members
Articles / Programming Languages / C++11

Extending boost::filesystem for Windows and Linux: Part 1

Rate me:
Please Sign up or sign in to vote.
4.93/5 (15 votes)
19 Mar 2013CPOL16 min read 31.3K   977   33  
Extending boost::filesystem for Windows and Linux.
/********************************************************
 *                                                      *
 *          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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Serbia Serbia
Software developer with couple of years of experience mostly with .NET programming and MS SQL databases currently interested in expanding knowledge to C++ and other operating systems.

Comments and Discussions