1 /////////////////////////////////////////////////////////////////////////////
2 // FileFilterMgr.cpp : implementation file
3 // see FileFilterMgr.h for description
4 /////////////////////////////////////////////////////////////////////////////
6 // This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
7 // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8 // You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
9 /////////////////////////////////////////////////////////////////////////////
11 * @file FileFilterMgr.cpp
13 * @brief Implementation of FileFilterMgr and supporting routines
15 // RCS ID line follows -- this is updated by CVS
19 #include "FileFilterMgr.h"
22 #include "coretools.h"
27 static char THIS_FILE[] = __FILE__;
31 * @brief Deletes items from filter list.
33 * @param [in] filterList List to empty.
35 void EmptyFilterList(FileFilterList & filterList)
37 while (!filterList.IsEmpty())
39 FileFilterElement &elem = filterList.GetHead();
41 filterList.RemoveHead();
46 * @brief One actual filter.
48 * For example, this might be a GNU C filter, excluding *.o files and CVS
49 * directories. That is to say, a filter is a set of file masks and
50 * directory masks. Usually FileFilter contains rules from one filter
51 * definition file. So it can be thought as filter file contents.
56 bool default_include; /**< If true, filter rules are inclusive by default */
57 CString name; /**< Filter name (shown in UI) */
58 CString description; /**< Filter description text */
59 CString fullpath; /**< Full path to filter file */
60 FileFilterList filefilters; /**< List of rules for files */
61 FileFilterList dirfilters; /**< List of rules for directories */
62 FileFilter() : default_include(true) { }
66 FileFilter::~FileFilter()
68 EmptyFilterList(filefilters);
69 EmptyFilterList(dirfilters);
72 FileFilterMgr::~FileFilterMgr()
78 * @brief Loads filterfile from disk and adds it to filters.
79 * @param [in] szFilterFile to load.
80 * @return FILTER_OK if succeeded or one of FILTER_RETVALUE values on error.
82 int FileFilterMgr::AddFilter(LPCTSTR szFilterFile)
84 int errorcode = FILTER_OK;
85 FileFilter * pFilter = LoadFilterFile(szFilterFile, errorcode);
87 m_filters.Add(pFilter);
92 * @brief Load all filter files matching pattern from disk into internal filter set.
94 * @param [in] szPattern Pattern from where to load filters, for example "\\Filters\\*.flt"
95 * @param [in] szExt File-extension of filter files
97 void FileFilterMgr::LoadFromDirectory(LPCTSTR szPattern, LPCTSTR szExt)
100 BOOL bWorking = finder.FindFile(szPattern);
101 int extlen = szExt ? _tcslen(szExt) : 0;
104 bWorking = finder.FindNextFile();
105 if (finder.IsDots() || finder.IsDirectory())
107 CString sFilename = finder.GetFileName();
110 // caller specified a specific extension
111 // (This is really a workaround for brokenness in windows, which
112 // doesn't screen correctly on extension in pattern)
113 if (sFilename.Right(extlen).CompareNoCase(szExt))
116 AddFilter(finder.GetFilePath());
121 * @brief Removes filter from filterlist.
123 * @param [in] szFilterFile Filename of filter to remove.
125 void FileFilterMgr::RemoveFilter(LPCTSTR szFilterFile)
127 // Note that m_filters.GetSize can change during loop
128 for (int i = 0; i < m_filters.GetSize(); i++)
130 FileFilter * pFilter = m_filters.GetAt(i);
131 if (pFilter->fullpath.CompareNoCase(szFilterFile) == 0)
133 m_filters.RemoveAt(i);
140 * @brief Removes all filters from current list.
142 void FileFilterMgr::DeleteAllFilters()
144 for (int i=0; i<m_filters.GetSize(); ++i)
149 m_filters.RemoveAll();
153 * @brief Add a single pattern (if nonempty & valid) to a pattern list.
155 * @param [in] filterList List where pattern is added.
156 * @param [in] str Temporary variable (ie, it may be altered)
158 static void AddFilterPattern(FileFilterList & filterList, CString & str)
160 LPCTSTR commentLeader = _T("##"); // Starts comment
164 // Ignore lines beginning with '##'
165 int pos = str.Find(commentLeader);
169 // Find possible comment-separator '<whitespace>##'
170 while (pos > 0 && !_istspace(str[pos - 1]))
171 pos = str.Find(commentLeader, pos);
173 // Remove comment and whitespaces before it
180 CRegExp * regexp = new CRegExp;
183 if (regexp->RegComp(str))
185 FileFilterElement elem;
186 elem.pRegExp = regexp;
188 filterList.AddTail(elem);
196 * @brief Parse a filter file, and add it to array if valid.
198 * @param [in] szFilePath Path (w/ filename) to file to load.
199 * @param [out] error Error-code if loading failed (returned NULL).
200 * @return Pointer to new filter, or NULL if error (check error code too).
202 FileFilter * FileFilterMgr::LoadFilterFile(LPCTSTR szFilepath, int & error)
205 if (!file.OpenReadOnly(szFilepath))
207 error = FILTER_ERROR_FILEACCESS;
211 file.ReadBom(); // in case it is a Unicode file, let UniMemFile handle BOM
214 SplitFilename(szFilepath, NULL, &fileName, NULL);
215 FileFilter *pfilter = new FileFilter;
216 pfilter->fullpath = szFilepath;
217 pfilter->name = fileName; // Filename is the default name
221 while (file.ReadString(sLine, &lossy))
226 if (0 == _tcsncmp(sLine, _T("name:"), 5))
228 // specifies display name
229 CString str = sLine.Mid(5);
234 else if (0 == _tcsncmp(sLine, _T("desc:"), 5))
236 // specifies display name
237 CString str = sLine.Mid(5);
240 pfilter->description = str;
242 else if (0 == _tcsncmp(sLine, _T("def:"), 4))
245 CString str = sLine.Mid(4);
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, _T("f:"), 2))
255 CString str = sLine.Mid(2);
256 AddFilterPattern(pfilter->filefilters, str);
258 else if (0 == _tcsncmp(sLine, _T("d:"), 2))
261 CString str = sLine.Mid(2);
262 AddFilterPattern(pfilter->dirfilters, str);
269 * @brief Give client back a pointer to the actual filter.
271 * @param [in] szFilterPath Full path to filterfile.
272 * @return Pointer to found filefilter or NULL;
273 * @note We just do a linear search, because this is seldom called
275 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
277 for (int i=0; i<m_filters.GetSize(); ++i)
279 if (m_filters[i]->fullpath.CompareNoCase(szFilterPath) == 0)
286 * @brief Test given string against given regexp list.
288 * @param [in] filterList List of regexps to test against.
289 * @param [in] szTest String to test against regexps.
290 * @return TRUE if string passes
291 * @note Matching stops when first match is found.
293 BOOL TestAgainstRegList(const FileFilterList & filterList, LPCTSTR szTest)
295 CString str = szTest;
297 for (POSITION pos = filterList.GetHeadPosition(); pos; )
299 const FileFilterElement & elem = filterList.GetNext(pos);
300 CRegExp * regexp = elem.pRegExp;
301 if (regexp->RegFind(str) != -1)
308 * @brief Test given filename against filefilter.
310 * Test filename against active filefilter. If matching rule is found
311 * we must first determine type of rule that matched. If we return FALSE
312 * from this function directory scan marks file as skipped.
314 * @param [in] pFilter Pointer to filefilter
315 * @param [in] szFileName Filename to test
316 * @return TRUE if file passes the filter
318 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
319 LPCTSTR szFileName) const
323 if (TestAgainstRegList(pFilter->filefilters, szFileName))
324 return !pFilter->default_include;
325 return pFilter->default_include;
329 * @brief Test given directory name against filefilter.
331 * Test directory name against active filefilter. If matching rule is found
332 * we must first determine type of rule that matched. If we return FALSE
333 * from this function directory scan marks file as skipped.
335 * @param [in] pFilter Pointer to filefilter
336 * @param [in] szDirName Directory name to test
337 * @return TRUE if directory name passes the filter
339 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
340 LPCTSTR szDirName) const
344 if (TestAgainstRegList(pFilter->dirfilters, szDirName))
345 return !pFilter->default_include;
346 return pFilter->default_include;
350 * @brief Return name of filter.
352 * @param [in] i Index of filter.
353 * @return Name of filter in given index.
355 CString FileFilterMgr::GetFilterName(int i) const
357 return m_filters[i]->name;
360 /** @brief Return name of filter. */
361 CString FileFilterMgr::GetFilterName(const FileFilter *pFilter) const
363 return pFilter->name;
367 * @brief Return description of filter.
369 * @param [in] i Index of filter.
370 * @return Description of filter in given index.
372 CString FileFilterMgr::GetFilterDesc(int i) const
374 return m_filters[i]->description;
377 /** @brief Return description of filter. */
378 CString FileFilterMgr::GetFilterDesc(const FileFilter *pFilter) const
380 return pFilter->description;
384 * @brief Return full path to filter.
386 * @param [in] i Index of filter.
387 * @return Full path of filter in given index.
389 CString FileFilterMgr::GetFilterPath(int i) const
391 return m_filters[i]->fullpath;
395 * @brief Return full path to filter.
397 * @param [in] pFilter Pointer to filter.
398 * @return Full path of filter.
400 CString FileFilterMgr::GetFullpath(FileFilter * pfilter) const
402 return pfilter->fullpath;
406 * @brief Reload filter from disk
408 * Reloads filter from disk. This is done by creating a new one
409 * to substitute for old one.
410 * @param [in] pFilter Pointer to filter to reload.
411 * @return FILTER_OK when succeeds, one of FILTER_RETVALUE values on error.
412 * @note Given filter (pfilter) is freed and must not be used anymore.
413 * @todo Should return new filter.
415 int FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
417 int errorcode = FILTER_OK;
418 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath, errorcode);
420 if (newfilter == NULL)
425 for (int i = 0; i < m_filters.GetSize(); ++i)
427 if (pfilter == m_filters[i])
429 m_filters.RemoveAt(i);
434 m_filters.Add(newfilter);
439 * @brief Reload filter from disk.
441 * Reloads filter from disk. This is done by creating a new one
442 * to substitute for old one.
443 * @param [in] szFullPath Full path to filter file to reload.
444 * @return FILTER_OK when succeeds or one of FILTER_RETVALUE values when fails.
446 int FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
448 int errorcode = FILTER_OK;
449 FileFilter * filter = GetFilterByPath(szFullPath);
451 errorcode = ReloadFilterFromDisk(filter);
453 errorcode = FILTER_NOTFOUND;