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 ? _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;
161 int regexStringBufLen = _tcslen(str.c_str()) * sizeof(TCHAR) * 3 + 1;
162 char *regexString = (char *)malloc(regexStringBufLen);
167 // For unicode builds, use UTF-8.
168 // Convert pattern to UTF-8 and set option for PCRE to specify UTF-8.
169 regexLen = TransformUcs2ToUtf8(str.c_str(), str.length(),
170 regexString, regexStringBufLen);
171 regexString[regexLen] = '\0';
172 pcre_opts |= PCRE_UTF8;
174 strcpy(regexString, (LPCTSTR)str.c_str());
175 regexLen = strlen(regexString);
177 pcre_opts |= PCRE_CASELESS;
179 pcre *regexp = pcre_compile(regexString, pcre_opts, &errormsg,
183 FileFilterElement *elem = new FileFilterElement();
186 pcre_extra *pe = pcre_study(regexp, 0, &errormsg);
187 elem->pRegExp = regexp;
189 if (pe != NULL && errormsg != NULL)
190 elem->pRegExpExtra = pe;
192 filterList->push_back(elem);
199 * @brief Parse a filter file, and add it to array if valid.
201 * @param [in] szFilePath Path (w/ filename) to file to load.
202 * @param [out] error Error-code if loading failed (returned NULL).
203 * @return Pointer to new filter, or NULL if error (check error code too).
205 FileFilter * FileFilterMgr::LoadFilterFile(LPCTSTR szFilepath, int & error)
208 if (!file.OpenReadOnly(szFilepath))
210 error = FILTER_ERROR_FILEACCESS;
214 file.ReadBom(); // in case it is a Unicode file, let UniMemFile handle BOM
217 SplitFilename(szFilepath, NULL, &fileName, NULL);
218 FileFilter *pfilter = new FileFilter;
219 pfilter->fullpath = szFilepath;
220 pfilter->name = fileName.c_str(); // Filename is the default name
224 bool bLinesLeft = true;
227 // Returns false when last line is read
229 bLinesLeft = file.ReadString(tmpLine, &lossy);
231 sLine = string_trim_ws(sLine);
233 if (0 == _tcsncmp(sLine.c_str(), _T("name:"), 5))
235 // specifies display name
236 String str = sLine.substr(5);
237 str = string_trim_ws_begin(str);
241 else if (0 == _tcsncmp(sLine.c_str(), _T("desc:"), 5))
243 // specifies display name
244 String str = sLine.substr(5);
245 str = string_trim_ws_begin(str);
247 pfilter->description = str;
249 else if (0 == _tcsncmp(sLine.c_str(), _T("def:"), 4))
252 String str = sLine.substr(4);
253 str = string_trim_ws_begin(str);
254 if (str == _T("0") || str == _T("no") || str == _T("exclude"))
255 pfilter->default_include = false;
256 else if (str == _T("1") || str == _T("yes") || str == _T("include"))
257 pfilter->default_include = true;
259 else if (0 == _tcsncmp(sLine.c_str(), _T("f:"), 2))
262 String str = sLine.substr(2);
263 AddFilterPattern(&pfilter->filefilters, str);
265 else if (0 == _tcsncmp(sLine.c_str(), _T("d:"), 2))
268 String str = sLine.substr(2);
269 AddFilterPattern(&pfilter->dirfilters, str);
271 } while (bLinesLeft);
277 * @brief Give client back a pointer to the actual filter.
279 * @param [in] szFilterPath Full path to filterfile.
280 * @return Pointer to found filefilter or NULL;
281 * @note We just do a linear search, because this is seldom called
283 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
285 vector<FileFilter*>::const_iterator iter = m_filters.begin();
286 while (iter != m_filters.end())
288 if (string_compare_nocase((*iter)->fullpath, szFilterPath) == 0)
296 * @brief Test given string against given regexp list.
298 * @param [in] filterList List of regexps to test against.
299 * @param [in] szTest String to test against regexps.
300 * @return TRUE if string passes
301 * @note Matching stops when first match is found.
303 BOOL TestAgainstRegList(const vector<FileFilterElement*> *filterList, LPCTSTR szTest)
305 vector<FileFilterElement*>::const_iterator iter = filterList->begin();
306 while (iter != filterList->end())
308 //const FileFilterElement & elem = filterList.GetNext(pos);
310 int compStringBufLen = _tcslen(szTest) * sizeof(TCHAR) * 3 + 1;
311 char *compString = (char *)malloc(compStringBufLen);
313 TCHAR * tempName = _tcsdup(szTest); // Create temp copy for conversions
314 TCHAR * cmpStr = _tcsupr(tempName);
317 stringLen = TransformUcs2ToUtf8(cmpStr, _tcslen(cmpStr),
318 compString, compStringBufLen);
320 strcpy(compString, cmpStr);
321 stringLen = strlen(compString);
324 pcre * regexp = (*iter)->pRegExp;
325 pcre_extra * extra = (*iter)->pRegExpExtra;
326 int result = pcre_exec(regexp, extra, compString, stringLen,
341 * @brief Test given filename against filefilter.
343 * Test filename against active filefilter. If matching rule is found
344 * we must first determine type of rule that matched. If we return FALSE
345 * from this function directory scan marks file as skipped.
347 * @param [in] pFilter Pointer to filefilter
348 * @param [in] szFileName Filename to test
349 * @return TRUE if file passes the filter
351 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
352 LPCTSTR szFileName) const
356 if (TestAgainstRegList(&pFilter->filefilters, szFileName))
357 return !pFilter->default_include;
358 return pFilter->default_include;
362 * @brief Test given directory name against filefilter.
364 * Test directory name against active filefilter. If matching rule is found
365 * we must first determine type of rule that matched. If we return FALSE
366 * from this function directory scan marks file as skipped.
368 * @param [in] pFilter Pointer to filefilter
369 * @param [in] szDirName Directory name to test
370 * @return TRUE if directory name passes the filter
372 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
373 LPCTSTR szDirName) const
377 if (TestAgainstRegList(&pFilter->dirfilters, szDirName))
378 return !pFilter->default_include;
379 return pFilter->default_include;
383 * @brief Return name of filter.
385 * @param [in] i Index of filter.
386 * @return Name of filter in given index.
388 String FileFilterMgr::GetFilterName(int i) const
390 return m_filters[i]->name;
394 * @brief Return name of filter.
395 * @param [in] pFilter Filter to get name for.
396 * @return Given filter's name.
398 String FileFilterMgr::GetFilterName(const FileFilter *pFilter) const
400 return pFilter->name;
404 * @brief Return description of filter.
406 * @param [in] i Index of filter.
407 * @return Description of filter in given index.
409 String FileFilterMgr::GetFilterDesc(int i) const
411 return m_filters[i]->description;
415 * @brief Return description of filter.
416 * @param [in] pFilter Filter to get description for.
417 * @return Given filter's description.
419 String FileFilterMgr::GetFilterDesc(const FileFilter *pFilter) const
421 return pFilter->description;
425 * @brief Return full path to filter.
427 * @param [in] i Index of filter.
428 * @return Full path of filter in given index.
430 String FileFilterMgr::GetFilterPath(int i) const
432 return m_filters[i]->fullpath;
436 * @brief Return full path to filter.
438 * @param [in] pFilter Pointer to filter.
439 * @return Full path of filter.
441 String FileFilterMgr::GetFullpath(FileFilter * pfilter) const
443 return pfilter->fullpath;
447 * @brief Reload filter from disk
449 * Reloads filter from disk. This is done by creating a new one
450 * to substitute for old one.
451 * @param [in] pFilter Pointer to filter to reload.
452 * @return FILTER_OK when succeeds, one of FILTER_RETVALUE values on error.
453 * @note Given filter (pfilter) is freed and must not be used anymore.
454 * @todo Should return new filter.
456 int FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
458 int errorcode = FILTER_OK;
459 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath.c_str(), errorcode);
461 if (newfilter == NULL)
466 vector<FileFilter*>::iterator iter = m_filters.begin();
467 while (iter != m_filters.end())
469 if (pfilter == (*iter))
472 m_filters.erase(iter);
476 m_filters.push_back(newfilter);
481 * @brief Reload filter from disk.
483 * Reloads filter from disk. This is done by creating a new one
484 * to substitute for old one.
485 * @param [in] szFullPath Full path to filter file to reload.
486 * @return FILTER_OK when succeeds or one of FILTER_RETVALUE values when fails.
488 int FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
490 int errorcode = FILTER_OK;
491 FileFilter * filter = GetFilterByPath(szFullPath);
493 errorcode = ReloadFilterFromDisk(filter);
495 errorcode = FILTER_NOTFOUND;