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
26 #include "UnicodeString.h"
27 #include "FileFilter.h"
29 #include "FileFilterMgr.h"
31 #include "coretools.h"
37 static char THIS_FILE[] = __FILE__;
42 static void AddFilterPattern(vector<FileFilterElement*> *filterList, String & str);
45 * @brief Destructor, frees all filters.
47 FileFilterMgr::~FileFilterMgr()
53 * @brief Loads filterfile from disk and adds it to filters.
54 * @param [in] szFilterFile Filter file to load.
55 * @return FILTER_OK if succeeded or one of FILTER_RETVALUE values on error.
57 int FileFilterMgr::AddFilter(LPCTSTR szFilterFile)
59 int errorcode = FILTER_OK;
60 FileFilter * pFilter = LoadFilterFile(szFilterFile, errorcode);
62 m_filters.push_back(pFilter);
67 * @brief Load all filter files matching pattern from disk into internal filter set.
69 * @param [in] szPattern Pattern from where to load filters, for example "\\Filters\\*.flt"
70 * @param [in] szExt File-extension of filter files
72 void FileFilterMgr::LoadFromDirectory(LPCTSTR szPattern, LPCTSTR szExt)
75 BOOL bWorking = finder.FindFile(szPattern);
76 int extlen = szExt ? _tcslen(szExt) : 0;
79 bWorking = finder.FindNextFile();
80 if (finder.IsDots() || finder.IsDirectory())
82 CString sFilename = finder.GetFileName();
85 // caller specified a specific extension
86 // (This is really a workaround for brokenness in windows, which
87 // doesn't screen correctly on extension in pattern)
88 if (sFilename.Right(extlen).CompareNoCase(szExt))
91 AddFilter(finder.GetFilePath());
96 * @brief Removes filter from filterlist.
98 * @param [in] szFilterFile Filename of filter to remove.
100 void FileFilterMgr::RemoveFilter(LPCTSTR szFilterFile)
102 // Note that m_filters.GetSize can change during loop
103 vector<FileFilter*>::iterator iter = m_filters.begin();
104 while (iter != m_filters.end())
106 if (string_compare_nocase((*iter)->fullpath, szFilterFile) == 0)
109 m_filters.erase(iter);
117 * @brief Removes all filters from current list.
119 void FileFilterMgr::DeleteAllFilters()
121 while (!m_filters.empty())
123 FileFilter* filter = m_filters.back();
125 m_filters.pop_back();
130 * @brief Add a single pattern (if nonempty & valid) to a pattern list.
132 * @param [in] filterList List where pattern is added.
133 * @param [in] str Temporary variable (ie, it may be altered)
135 static void AddFilterPattern(vector<FileFilterElement*> *filterList, String & str)
137 LPCTSTR commentLeader = _T("##"); // Starts comment
138 str = string_trim_ws_begin(str);
140 // Ignore lines beginning with '##'
141 size_t pos = str.find(commentLeader);
145 // Find possible comment-separator '<whitespace>##'
146 while (pos != std::string::npos && !_istspace(str[pos - 1]))
147 pos = str.find(commentLeader, pos + 1);
149 // Remove comment and whitespaces before it
150 if (pos != std::string::npos)
151 str = str.substr(0, pos);
152 str = string_trim_ws_end(str);
156 const char * errormsg = NULL;
158 char regexString[200] = {0};
163 // For unicode builds, use UTF-8.
164 // Convert pattern to UTF-8 and set option for PCRE to specify UTF-8.
165 regexLen = TransformUcs2ToUtf8(str.c_str(), str.length(),
166 regexString, sizeof(regexString));
167 pcre_opts |= PCRE_UTF8;
169 strcpy(regexString, (LPCTSTR)str);
170 regexLen = strlen(regexString);
172 pcre_opts |= PCRE_CASELESS;
174 pcre *regexp = pcre_compile(regexString, pcre_opts, &errormsg,
178 FileFilterElement *elem = new FileFilterElement();
181 pcre_extra *pe = pcre_study(regexp, 0, &errormsg);
182 elem->pRegExp = regexp;
184 if (pe != NULL && errormsg != NULL)
185 elem->pRegExpExtra = pe;
187 filterList->push_back(elem);
192 * @brief Parse a filter file, and add it to array if valid.
194 * @param [in] szFilePath Path (w/ filename) to file to load.
195 * @param [out] error Error-code if loading failed (returned NULL).
196 * @return Pointer to new filter, or NULL if error (check error code too).
198 FileFilter * FileFilterMgr::LoadFilterFile(LPCTSTR szFilepath, int & error)
201 if (!file.OpenReadOnly(szFilepath))
203 error = FILTER_ERROR_FILEACCESS;
207 file.ReadBom(); // in case it is a Unicode file, let UniMemFile handle BOM
210 SplitFilename(szFilepath, NULL, &fileName, NULL);
211 FileFilter *pfilter = new FileFilter;
212 pfilter->fullpath = szFilepath;
213 pfilter->name = fileName.c_str(); // Filename is the default name
217 bool bLinesLeft = true;
220 // Returns false when last line is read
222 bLinesLeft = file.ReadString(tmpLine, &lossy);
224 sLine = string_trim_ws(sLine);
226 if (0 == _tcsncmp(sLine.c_str(), _T("name:"), 5))
228 // specifies display name
229 String str = sLine.substr(5);
230 str = string_trim_ws_begin(str);
234 else if (0 == _tcsncmp(sLine.c_str(), _T("desc:"), 5))
236 // specifies display name
237 String str = sLine.substr(5);
238 str = string_trim_ws_begin(str);
240 pfilter->description = str;
242 else if (0 == _tcsncmp(sLine.c_str(), _T("def:"), 4))
245 String str = sLine.substr(4);
246 str = string_trim_ws_begin(str);
247 if (str == _T("0") || str == _T("no") || str == _T("exclude"))
248 pfilter->default_include = false;
249 else if (str == _T("1") || str == _T("yes") || str == _T("include"))
250 pfilter->default_include = true;
252 else if (0 == _tcsncmp(sLine.c_str(), _T("f:"), 2))
255 String str = sLine.substr(2);
256 AddFilterPattern(&pfilter->filefilters, str);
258 else if (0 == _tcsncmp(sLine.c_str(), _T("d:"), 2))
261 String str = sLine.substr(2);
262 AddFilterPattern(&pfilter->dirfilters, str);
264 } while (bLinesLeft);
270 * @brief Give client back a pointer to the actual filter.
272 * @param [in] szFilterPath Full path to filterfile.
273 * @return Pointer to found filefilter or NULL;
274 * @note We just do a linear search, because this is seldom called
276 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
278 vector<FileFilter*>::const_iterator iter = m_filters.begin();
279 while (iter != m_filters.end())
281 if (string_compare_nocase((*iter)->fullpath, szFilterPath) == 0)
289 * @brief Test given string against given regexp list.
291 * @param [in] filterList List of regexps to test against.
292 * @param [in] szTest String to test against regexps.
293 * @return TRUE if string passes
294 * @note Matching stops when first match is found.
296 BOOL TestAgainstRegList(const vector<FileFilterElement*> *filterList, LPCTSTR szTest)
298 vector<FileFilterElement*>::const_iterator iter = filterList->begin();
299 while (iter != filterList->end())
301 //const FileFilterElement & elem = filterList.GetNext(pos);
303 char compString[200] = {0};
305 TCHAR * tempName = _tcsdup(szTest); // Create temp copy for conversions
306 TCHAR * cmpStr = _tcsupr(tempName);
309 stringLen = TransformUcs2ToUtf8(cmpStr, _tcslen(cmpStr),
310 compString, sizeof(compString));
312 strcpy(compString, cmpStr);
313 stringLen = strlen(compString);
316 pcre * regexp = (*iter)->pRegExp;
317 pcre_extra * extra = (*iter)->pRegExpExtra;
318 int result = pcre_exec(regexp, extra, compString, stringLen,
332 * @brief Test given filename against filefilter.
334 * Test filename against active filefilter. If matching rule is found
335 * we must first determine type of rule that matched. If we return FALSE
336 * from this function directory scan marks file as skipped.
338 * @param [in] pFilter Pointer to filefilter
339 * @param [in] szFileName Filename to test
340 * @return TRUE if file passes the filter
342 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
343 LPCTSTR szFileName) const
347 if (TestAgainstRegList(&pFilter->filefilters, szFileName))
348 return !pFilter->default_include;
349 return pFilter->default_include;
353 * @brief Test given directory name against filefilter.
355 * Test directory name against active filefilter. If matching rule is found
356 * we must first determine type of rule that matched. If we return FALSE
357 * from this function directory scan marks file as skipped.
359 * @param [in] pFilter Pointer to filefilter
360 * @param [in] szDirName Directory name to test
361 * @return TRUE if directory name passes the filter
363 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
364 LPCTSTR szDirName) const
368 if (TestAgainstRegList(&pFilter->dirfilters, szDirName))
369 return !pFilter->default_include;
370 return pFilter->default_include;
374 * @brief Return name of filter.
376 * @param [in] i Index of filter.
377 * @return Name of filter in given index.
379 String FileFilterMgr::GetFilterName(int i) const
381 return m_filters[i]->name;
385 * @brief Return name of filter.
386 * @param [in] pFilter Filter to get name for.
387 * @return Given filter's name.
389 String FileFilterMgr::GetFilterName(const FileFilter *pFilter) const
391 return pFilter->name;
395 * @brief Return description of filter.
397 * @param [in] i Index of filter.
398 * @return Description of filter in given index.
400 String FileFilterMgr::GetFilterDesc(int i) const
402 return m_filters[i]->description;
406 * @brief Return description of filter.
407 * @param [in] pFilter Filter to get description for.
408 * @return Given filter's description.
410 String FileFilterMgr::GetFilterDesc(const FileFilter *pFilter) const
412 return pFilter->description;
416 * @brief Return full path to filter.
418 * @param [in] i Index of filter.
419 * @return Full path of filter in given index.
421 String FileFilterMgr::GetFilterPath(int i) const
423 return m_filters[i]->fullpath;
427 * @brief Return full path to filter.
429 * @param [in] pFilter Pointer to filter.
430 * @return Full path of filter.
432 String FileFilterMgr::GetFullpath(FileFilter * pfilter) const
434 return pfilter->fullpath;
438 * @brief Reload filter from disk
440 * Reloads filter from disk. This is done by creating a new one
441 * to substitute for old one.
442 * @param [in] pFilter Pointer to filter to reload.
443 * @return FILTER_OK when succeeds, one of FILTER_RETVALUE values on error.
444 * @note Given filter (pfilter) is freed and must not be used anymore.
445 * @todo Should return new filter.
447 int FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
449 int errorcode = FILTER_OK;
450 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath.c_str(), errorcode);
452 if (newfilter == NULL)
457 vector<FileFilter*>::iterator iter = m_filters.begin();
458 while (iter != m_filters.end())
460 if (pfilter == (*iter))
463 m_filters.erase(iter);
467 m_filters.push_back(newfilter);
472 * @brief Reload filter from disk.
474 * Reloads filter from disk. This is done by creating a new one
475 * to substitute for old one.
476 * @param [in] szFullPath Full path to filter file to reload.
477 * @return FILTER_OK when succeeds or one of FILTER_RETVALUE values when fails.
479 int FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
481 int errorcode = FILTER_OK;
482 FileFilter * filter = GetFilterByPath(szFullPath);
484 errorcode = ReloadFilterFromDisk(filter);
486 errorcode = FILTER_NOTFOUND;