Skip to content

File glob.cpp

File List > src > utils > glob.cpp

Go to the documentation of this file

//
// Created by ibois on 12/14/22.
//

#include "glob.h"
#include "glob.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#include <fnmatch.h>
#endif

namespace glob {

#ifdef _WIN32

    class glob_impl
{
public:
    glob_impl():
        find_handle(INVALID_HANDLE_VALUE),
        find_data(),
        has_next(false)
    {}

    HANDLE find_handle;
    WIN32_FIND_DATAA find_data;
    bool has_next;
};

glob::glob(const std::string &pattern):
    impl_(std::make_unique<glob_impl>())
{
    open(pattern);
}

glob::~glob()
{
    close();
}

void glob::open(const std::string &pattern)
{
    impl_->find_handle =
        FindFirstFileA(pattern.c_str(), &impl_->find_data);
    impl_->has_next = impl_->find_handle != INVALID_HANDLE_VALUE;
}

void glob::close()
{
    if (impl_->find_handle != INVALID_HANDLE_VALUE) {
        FindClose(impl_->find_handle);
        impl_->find_handle = INVALID_HANDLE_VALUE;
        impl_->has_next = false;
    }
}

std::string glob::current_match() const
{
    return impl_->find_data.cFileName;
}

bool glob::is_valid() const
{
    return impl_->has_next;
}

bool glob::next()
{
    impl_->has_next =
        FindNextFileA(impl_->find_handle, &impl_->find_data) != 0;
    return impl_->has_next;
}

#else // _WIN32

    namespace {

        std::pair<std::string, std::string> split_path(const std::string &path)
        {
            std::string::size_type last_sep = path.find_last_of("/");
            if (last_sep != std::string::npos) {
                return std::make_pair(
                        std::string(path.begin(), path.begin() + last_sep),
                        std::string(path.begin() + last_sep + 1, path.end()));
            }
            return std::make_pair(".", path);
        }

    } // namespace

    class glob_impl
    {
    public:
        glob_impl():
                dir(nullptr),
                dir_entry(nullptr)
        {}

        std::string file_pattern;
        DIR *dir;
        struct dirent *dir_entry;
    };

    glob::glob(const std::string &pattern):
            impl_(std::make_unique<glob_impl>())
    {
        open(pattern);
    }

    glob::~glob()
    {
        close();
    }

    void glob::open(const std::string &pattern)
    {
        auto dir_and_file = split_path(pattern);
        dir_path = dir_and_file.first;
        impl_->dir = opendir(dir_and_file.first.c_str());
        impl_->file_pattern = dir_and_file.second;

        if (impl_->dir != nullptr) {
            next();
        }
    }

    void glob::close()
    {
        if (impl_->dir != nullptr) {
            closedir(impl_->dir);
            impl_->dir = nullptr;
        }
    }

    std::string glob::current_match() const
    {

        return dir_path + '/' + impl_->dir_entry->d_name;
    }

    bool glob::next()
    {
        while ((impl_->dir_entry = readdir(impl_->dir)) != nullptr) {
            if (!fnmatch(impl_->file_pattern.c_str(),
                         impl_->dir_entry->d_name,
                         FNM_CASEFOLD | FNM_NOESCAPE | FNM_PERIOD)) {
                return true;
            }
        }
        return false;
    }

    bool glob::is_valid() const
    {
        return impl_->dir != nullptr && impl_->dir_entry != nullptr;
    }

#endif // !_WIN32

} // namespace glob