1 /////////////////////////////////////////////////////////////////////////////
3 // This program is free software; you can redistribute it and/or modify it
4 // under the terms of the GNU General Public License as published by the
5 // Free Software Foundation; either version 2 of the License, or (at your
6 // option) any later version.
7 // This program is distributed in the hope that it will be useful, but
8 // WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 // General Public License for more details.
11 // You should have received a copy of the GNU General Public License
12 // along with this program; if not, write to the Free Software
13 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14 /////////////////////////////////////////////////////////////////////////////
16 * @file FileFilterMgr.cpp
18 * @brief Implementation of FileFilterMgr and supporting routines
20 // ID line follows -- this is updated by SVN
21 // $Id: FileFilterMgr.cpp 7024 2009-10-22 18:26:45Z kimmov $
27 #include "UnicodeString.h"
28 #include "FileFilter.h"
30 #include "FileFilterMgr.h"
32 #include "coretools.h"
38 static void AddFilterPattern(vector<FileFilterElement*> *filterList, String & str);
41 * @brief Destructor, frees all filters.
43 FileFilterMgr::~FileFilterMgr()
49 * @brief Loads filterfile from disk and adds it to filters.
50 * @param [in] szFilterFile Filter file to load.
51 * @return FILTER_OK if succeeded or one of FILTER_RETVALUE values on error.
53 int FileFilterMgr::AddFilter(LPCTSTR szFilterFile)
55 int errorcode = FILTER_OK;
56 FileFilter * pFilter = LoadFilterFile(szFilterFile, errorcode);
58 m_filters.push_back(pFilter);
63 * @brief Load all filter files matching pattern from disk into internal filter set.
64 * @param [in] dir Directory from where filters are loaded.
65 * @param [in] szPattern Pattern for filters to load filters, for example "*.flt".
66 * @param [in] szExt File-extension of filter files.
68 void FileFilterMgr::LoadFromDirectory(LPCTSTR dir, LPCTSTR szPattern, LPCTSTR szExt)
70 const int extlen = szExt ? (int)_tcslen(szExt) : 0;
71 const String pattern = paths_ConcatPath(dir, szPattern);
73 HANDLE h = FindFirstFile(pattern.c_str(), &ff);
74 if (h != INVALID_HANDLE_VALUE)
78 if (ff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
80 String filename = ff.cFileName;
83 // caller specified a specific extension
84 // (This is really a workaround for brokenness in windows, which
85 // doesn't screen correctly on extension in pattern)
86 String ext = filename.substr(filename.length() - extlen);
87 if (string_compare_nocase(szExt, ext) != 0)
91 String filterpath = paths_ConcatPath(dir, ff.cFileName);
92 AddFilter(filterpath.c_str());
93 } while (FindNextFile(h, &ff));
99 * @brief Removes filter from filterlist.
101 * @param [in] szFilterFile Filename of filter to remove.
103 void FileFilterMgr::RemoveFilter(LPCTSTR szFilterFile)
105 // Note that m_filters.GetSize can change during loop
106 vector<FileFilter*>::iterator iter = m_filters.begin();
107 while (iter != m_filters.end())
109 if (string_compare_nocase((*iter)->fullpath, szFilterFile) == 0)
112 m_filters.erase(iter);
120 * @brief Removes all filters from current list.
122 void FileFilterMgr::DeleteAllFilters()
124 while (!m_filters.empty())
126 FileFilter* filter = m_filters.back();
128 m_filters.pop_back();
133 * @brief Add a single pattern (if nonempty & valid) to a pattern list.
135 * @param [in] filterList List where pattern is added.
136 * @param [in] str Temporary variable (ie, it may be altered)
138 static void AddFilterPattern(vector<FileFilterElement*> *filterList, String & str)
140 LPCTSTR commentLeader = _T("##"); // Starts comment
141 str = string_trim_ws_begin(str);
143 // Ignore lines beginning with '##'
144 size_t pos = str.find(commentLeader);
148 // Find possible comment-separator '<whitespace>##'
149 while (pos != std::string::npos && !_istspace(str[pos - 1]))
150 pos = str.find(commentLeader, pos + 1);
152 // Remove comment and whitespaces before it
153 if (pos != std::string::npos)
154 str = str.substr(0, pos);
155 str = string_trim_ws_end(str);
159 const char * errormsg = NULL;
162 char *regexString = UCS2UTF8_ConvertToUtf8(str.c_str());
166 pcre_opts |= PCRE_UTF8;
168 pcre_opts |= PCRE_CASELESS;
169 pcre *regexp = pcre_compile(regexString, pcre_opts, &errormsg,
173 FileFilterElement *elem = new FileFilterElement();
176 pcre_extra *pe = pcre_study(regexp, 0, &errormsg);
177 elem->pRegExp = regexp;
179 if (pe != NULL && errormsg != NULL)
180 elem->pRegExpExtra = pe;
182 filterList->push_back(elem);
184 UCS2UTF8_Dealloc(regexString);
188 * @brief Parse a filter file, and add it to array if valid.
190 * @param [in] szFilePath Path (w/ filename) to file to load.
191 * @param [out] error Error-code if loading failed (returned NULL).
192 * @return Pointer to new filter, or NULL if error (check error code too).
194 FileFilter * FileFilterMgr::LoadFilterFile(LPCTSTR szFilepath, int & error)
197 if (!file.OpenReadOnly(szFilepath))
199 error = FILTER_ERROR_FILEACCESS;
203 file.ReadBom(); // in case it is a Unicode file, let UniMemFile handle BOM
206 SplitFilename(szFilepath, NULL, &fileName, NULL);
207 FileFilter *pfilter = new FileFilter;
208 pfilter->fullpath = szFilepath;
209 pfilter->name = fileName.c_str(); // Filename is the default name
213 bool bLinesLeft = true;
216 // Returns false when last line is read
218 bLinesLeft = file.ReadString(tmpLine, &lossy);
220 sLine = string_trim_ws(sLine);
222 if (0 == _tcsncmp(sLine.c_str(), _T("name:"), 5))
224 // specifies display name
225 String str = sLine.substr(5);
226 str = string_trim_ws_begin(str);
230 else if (0 == _tcsncmp(sLine.c_str(), _T("desc:"), 5))
232 // specifies display name
233 String str = sLine.substr(5);
234 str = string_trim_ws_begin(str);
236 pfilter->description = str;
238 else if (0 == _tcsncmp(sLine.c_str(), _T("def:"), 4))
241 String str = sLine.substr(4);
242 str = string_trim_ws_begin(str);
243 if (str == _T("0") || str == _T("no") || str == _T("exclude"))
244 pfilter->default_include = false;
245 else if (str == _T("1") || str == _T("yes") || str == _T("include"))
246 pfilter->default_include = true;
248 else if (0 == _tcsncmp(sLine.c_str(), _T("f:"), 2))
251 String str = sLine.substr(2);
252 AddFilterPattern(&pfilter->filefilters, str);
254 else if (0 == _tcsncmp(sLine.c_str(), _T("d:"), 2))
257 String str = sLine.substr(2);
258 AddFilterPattern(&pfilter->dirfilters, str);
260 } while (bLinesLeft);
266 * @brief Give client back a pointer to the actual filter.
268 * @param [in] szFilterPath Full path to filterfile.
269 * @return Pointer to found filefilter or NULL;
270 * @note We just do a linear search, because this is seldom called
272 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
274 vector<FileFilter*>::const_iterator iter = m_filters.begin();
275 while (iter != m_filters.end())
277 if (string_compare_nocase((*iter)->fullpath, szFilterPath) == 0)
285 * @brief Test given string against given regexp list.
287 * @param [in] filterList List of regexps to test against.
288 * @param [in] szTest String to test against regexps.
289 * @return TRUE if string passes
290 * @note Matching stops when first match is found.
292 BOOL TestAgainstRegList(const vector<FileFilterElement*> *filterList, LPCTSTR szTest)
295 char *compString = UCS2UTF8_ConvertToUtf8(szTest);
296 size_t stringlen = strlen(compString);
298 vector<FileFilterElement*>::const_iterator iter = filterList->begin();
299 while (iter != filterList->end())
301 pcre * regexp = (*iter)->pRegExp;
302 pcre_extra * extra = (*iter)->pRegExpExtra;
303 result = pcre_exec(regexp, extra, compString,(int) stringlen,
309 UCS2UTF8_Dealloc(compString);
316 * @brief Test given filename against filefilter.
318 * Test filename against active filefilter. If matching rule is found
319 * we must first determine type of rule that matched. If we return FALSE
320 * from this function directory scan marks file as skipped.
322 * @param [in] pFilter Pointer to filefilter
323 * @param [in] szFileName Filename to test
324 * @return TRUE if file passes the filter
326 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
327 LPCTSTR szFileName) const
331 if (TestAgainstRegList(&pFilter->filefilters, szFileName))
332 return !pFilter->default_include;
333 return pFilter->default_include;
337 * @brief Test given directory name against filefilter.
339 * Test directory name against active filefilter. If matching rule is found
340 * we must first determine type of rule that matched. If we return FALSE
341 * from this function directory scan marks file as skipped.
343 * @param [in] pFilter Pointer to filefilter
344 * @param [in] szDirName Directory name to test
345 * @return TRUE if directory name passes the filter
347 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
348 LPCTSTR szDirName) const
352 if (TestAgainstRegList(&pFilter->dirfilters, szDirName))
353 return !pFilter->default_include;
354 return pFilter->default_include;
358 * @brief Return name of filter.
360 * @param [in] i Index of filter.
361 * @return Name of filter in given index.
363 String FileFilterMgr::GetFilterName(int i) const
365 return m_filters[i]->name;
369 * @brief Return name of filter.
370 * @param [in] pFilter Filter to get name for.
371 * @return Given filter's name.
373 String FileFilterMgr::GetFilterName(const FileFilter *pFilter) const
375 return pFilter->name;
379 * @brief Return description of filter.
381 * @param [in] i Index of filter.
382 * @return Description of filter in given index.
384 String FileFilterMgr::GetFilterDesc(int i) const
386 return m_filters[i]->description;
390 * @brief Return description of filter.
391 * @param [in] pFilter Filter to get description for.
392 * @return Given filter's description.
394 String FileFilterMgr::GetFilterDesc(const FileFilter *pFilter) const
396 return pFilter->description;
400 * @brief Return full path to filter.
402 * @param [in] i Index of filter.
403 * @return Full path of filter in given index.
405 String FileFilterMgr::GetFilterPath(int i) const
407 return m_filters[i]->fullpath;
411 * @brief Return full path to filter.
413 * @param [in] pFilter Pointer to filter.
414 * @return Full path of filter.
416 String FileFilterMgr::GetFullpath(FileFilter * pfilter) const
418 return pfilter->fullpath;
422 * @brief Reload filter from disk
424 * Reloads filter from disk. This is done by creating a new one
425 * to substitute for old one.
426 * @param [in] pFilter Pointer to filter to reload.
427 * @return FILTER_OK when succeeds, one of FILTER_RETVALUE values on error.
428 * @note Given filter (pfilter) is freed and must not be used anymore.
429 * @todo Should return new filter.
431 int FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
433 int errorcode = FILTER_OK;
434 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath.c_str(), errorcode);
436 if (newfilter == NULL)
441 vector<FileFilter*>::iterator iter = m_filters.begin();
442 while (iter != m_filters.end())
444 if (pfilter == (*iter))
447 m_filters.erase(iter);
451 m_filters.push_back(newfilter);
456 * @brief Reload filter from disk.
458 * Reloads filter from disk. This is done by creating a new one
459 * to substitute for old one.
460 * @param [in] szFullPath Full path to filter file to reload.
461 * @return FILTER_OK when succeeds or one of FILTER_RETVALUE values when fails.
463 int FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
465 int errorcode = FILTER_OK;
466 FileFilter * filter = GetFilterByPath(szFullPath);
468 errorcode = ReloadFilterFromDisk(filter);
470 errorcode = FILTER_NOTFOUND;