root/headers/private/shared/EntryFilter.h
/*
 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
#ifndef _ENTRY_FILTER_H
#define _ENTRY_FILTER_H

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include <new>

#if defined(__BEOS__) && !defined(__HAIKU__)
        // BeOS doesn't have <fnmatch.h>, but libroot.so features fnmatch() anyway
        extern "C" int fnmatch(const char *pattern, const char *string, int flags);
#else
#       include <fnmatch.h>
#endif

namespace BPrivate {


class BasicEntryFilter {
public:
        BasicEntryFilter()
                : fPattern(NULL),
                  fIsFileName(false)
        {
        }

        ~BasicEntryFilter()
        {
                free(fPattern);
        }

        bool SetTo(const char* pattern, bool isFileName)
        {
                free(fPattern);

                fPattern = strdup(pattern);
                if (fPattern == NULL)
                        return false;

                fIsFileName = isFileName;

                return true;
        }

        bool Filter(const char* path, const char* name) const
        {
                if (fPattern != NULL) {
                        if (fnmatch(fPattern, (fIsFileName ? name : path), 0) == 0)
                                return true;
                }

                return false;
        }

        void SetNextFilter(BasicEntryFilter* next)
        {
                fNextFilter = next;
        }

        BasicEntryFilter* NextFilter() const
        {
                 return fNextFilter;
        }

private:
        char*                           fPattern;
        bool                            fIsFileName;
        BasicEntryFilter*       fNextFilter;
};


class EntryFilter {
public:
        EntryFilter()
                : fIncludeFilters(NULL),
                  fExcludeFilters(NULL)
        {
        }

        ~EntryFilter()
        {
                while (BasicEntryFilter* filter = fIncludeFilters) {
                        fIncludeFilters = filter->NextFilter();
                        delete filter;
                }

                while (BasicEntryFilter* filter = fExcludeFilters) {
                        fExcludeFilters = filter->NextFilter();
                        delete filter;
                }
        }

        void AddIncludeFilter(BasicEntryFilter* filter)
        {
                _AddFilter(fIncludeFilters, filter);
        }

        bool AddIncludeFilter(const char* pattern, bool isFilePattern)
        {
                return _AddFilter(fIncludeFilters, pattern, isFilePattern);
        }

        void AddExcludeFilter(BasicEntryFilter* filter)
        {
                _AddFilter(fExcludeFilters, filter);
        }

        bool AddExcludeFilter(const char* pattern, bool isFilePattern)
        {
                return _AddFilter(fExcludeFilters, pattern, isFilePattern);
        }

        bool Filter(const char* path) const
        {
                if (fExcludeFilters == NULL && fIncludeFilters)
                        return true;

                // get leaf name
                const char* name = strrchr(path, '/');
                name = (name != NULL ? name + 1 : path);

                // exclude filters
                if (_Filter(fExcludeFilters, path, name))
                        return false;

                // include filters -- if none are given, everything matches
                return fIncludeFilters == NULL || _Filter(fIncludeFilters, path, name);
        }

private:
        static void _AddFilter(BasicEntryFilter*& filterList,
                BasicEntryFilter* filter)
        {
                filter->SetNextFilter(filterList);
                filterList = filter;
        }

        static bool _AddFilter(BasicEntryFilter*& filterList, const char* pattern,
                bool isFilePattern)
        {
                BasicEntryFilter* filter = new(std::nothrow) BasicEntryFilter;
                if (filter == NULL)
                        return false;

                if (!filter->SetTo(pattern, isFilePattern)) {
                        delete filter;
                        return false;
                }

                _AddFilter(filterList, filter);

                return true;
        }

        static bool _Filter(const BasicEntryFilter* const& filterList,
                const char* path, const char* name)
        {
                const BasicEntryFilter* filter = filterList;
                while (filter) {
                        if (filter->Filter(path, name))
                                return true;
                        filter = filter->NextFilter();
                }

                return false;
        }

private:
        BasicEntryFilter*       fIncludeFilters;
        BasicEntryFilter*       fExcludeFilters;
};

}       // namespace BPrivate


#endif  // _ENTRY_FILTER_H