1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5#ifndef _ENTRY_FILTER_H
6#define _ENTRY_FILTER_H
7
8#include <stddef.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <new>
13
14#if defined(__BEOS__) && !defined(__HAIKU__)
15	// BeOS doesn't have <fnmatch.h>, but libroot.so features fnmatch() anyway
16	extern "C" int fnmatch(const char *pattern, const char *string, int flags);
17#else
18#	include <fnmatch.h>
19#endif
20
21namespace BPrivate {
22
23
24class BasicEntryFilter {
25public:
26	BasicEntryFilter()
27		: fPattern(NULL),
28		  fIsFileName(false)
29	{
30	}
31
32	~BasicEntryFilter()
33	{
34		free(fPattern);
35	}
36
37	bool SetTo(const char* pattern, bool isFileName)
38	{
39		free(fPattern);
40
41		fPattern = strdup(pattern);
42		if (fPattern == NULL)
43			return false;
44
45		fIsFileName = isFileName;
46
47		return true;
48	}
49
50	bool Filter(const char* path, const char* name) const
51	{
52		if (fPattern != NULL) {
53			if (fnmatch(fPattern, (fIsFileName ? name : path), 0) == 0)
54				return true;
55		}
56
57		return false;
58	}
59
60	void SetNextFilter(BasicEntryFilter* next)
61	{
62		fNextFilter = next;
63	}
64
65	BasicEntryFilter* NextFilter() const
66	{
67		 return fNextFilter;
68	}
69
70private:
71	char*				fPattern;
72	bool				fIsFileName;
73	BasicEntryFilter*	fNextFilter;
74};
75
76
77class EntryFilter {
78public:
79	EntryFilter()
80		: fIncludeFilters(NULL),
81		  fExcludeFilters(NULL)
82	{
83	}
84
85	~EntryFilter()
86	{
87		while (BasicEntryFilter* filter = fIncludeFilters) {
88			fIncludeFilters = filter->NextFilter();
89			delete filter;
90		}
91
92		while (BasicEntryFilter* filter = fExcludeFilters) {
93			fExcludeFilters = filter->NextFilter();
94			delete filter;
95		}
96	}
97
98	void AddIncludeFilter(BasicEntryFilter* filter)
99	{
100		_AddFilter(fIncludeFilters, filter);
101	}
102
103	bool AddIncludeFilter(const char* pattern, bool isFilePattern)
104	{
105		return _AddFilter(fIncludeFilters, pattern, isFilePattern);
106	}
107
108	void AddExcludeFilter(BasicEntryFilter* filter)
109	{
110		_AddFilter(fExcludeFilters, filter);
111	}
112
113	bool AddExcludeFilter(const char* pattern, bool isFilePattern)
114	{
115		return _AddFilter(fExcludeFilters, pattern, isFilePattern);
116	}
117
118	bool Filter(const char* path) const
119	{
120		if (fExcludeFilters == NULL && fIncludeFilters)
121			return true;
122
123		// get leaf name
124		const char* name = strrchr(path, '/');
125		name = (name != NULL ? name + 1 : path);
126
127		// exclude filters
128		if (_Filter(fExcludeFilters, path, name))
129			return false;
130
131		// include filters -- if none are given, everything matches
132		return fIncludeFilters == NULL || _Filter(fIncludeFilters, path, name);
133	}
134
135private:
136	static void _AddFilter(BasicEntryFilter*& filterList,
137		BasicEntryFilter* filter)
138	{
139		filter->SetNextFilter(filterList);
140		filterList = filter;
141	}
142
143	static bool _AddFilter(BasicEntryFilter*& filterList, const char* pattern,
144		bool isFilePattern)
145	{
146		BasicEntryFilter* filter = new(std::nothrow) BasicEntryFilter;
147		if (filter == NULL)
148			return false;
149
150		if (!filter->SetTo(pattern, isFilePattern)) {
151			delete filter;
152			return false;
153		}
154
155		_AddFilter(filterList, filter);
156
157		return true;
158	}
159
160	static bool _Filter(const BasicEntryFilter* const& filterList,
161		const char* path, const char* name)
162	{
163		const BasicEntryFilter* filter = filterList;
164		while (filter) {
165			if (filter->Filter(path, name))
166				return true;
167			filter = filter->NextFilter();
168		}
169
170		return false;
171	}
172
173private:
174	BasicEntryFilter*	fIncludeFilters;
175	BasicEntryFilter*	fExcludeFilters;
176};
177
178}	// namespace BPrivate
179
180
181#endif	// _ENTRY_FILTER_H
182