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 BOOL bLinesLeft = TRUE;
224 // Returns false when last line is read
225 bLinesLeft = file.ReadString(sLine, &lossy);
229 if (0 == _tcsncmp(sLine, _T("name:"), 5))
231 // specifies display name
232 CString str = sLine.Mid(5);
237 else if (0 == _tcsncmp(sLine, _T("desc:"), 5))
239 // specifies display name
240 CString str = sLine.Mid(5);
243 pfilter->description = str;
245 else if (0 == _tcsncmp(sLine, _T("def:"), 4))
248 CString str = sLine.Mid(4);
250 if (str == _T("0") || str == _T("no") || str == _T("exclude"))
251 pfilter->default_include = false;
252 else if (str == _T("1") || str == _T("yes") || str == _T("include"))
253 pfilter->default_include = true;
255 else if (0 == _tcsncmp(sLine, _T("f:"), 2))
258 CString str = sLine.Mid(2);
259 AddFilterPattern(pfilter->filefilters, str);
261 else if (0 == _tcsncmp(sLine, _T("d:"), 2))
264 CString str = sLine.Mid(2);
265 AddFilterPattern(pfilter->dirfilters, str);
267 } while (bLinesLeft == TRUE);
273 * @brief Give client back a pointer to the actual filter.
275 * @param [in] szFilterPath Full path to filterfile.
276 * @return Pointer to found filefilter or NULL;
277 * @note We just do a linear search, because this is seldom called
279 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
281 for (int i=0; i<m_filters.GetSize(); ++i)
283 if (m_filters[i]->fullpath.CompareNoCase(szFilterPath) == 0)
290 * @brief Test given string against given regexp list.
292 * @param [in] filterList List of regexps to test against.
293 * @param [in] szTest String to test against regexps.
294 * @return TRUE if string passes
295 * @note Matching stops when first match is found.
297 BOOL TestAgainstRegList(const FileFilterList & filterList, LPCTSTR szTest)
299 CString str = szTest;
301 for (POSITION pos = filterList.GetHeadPosition(); pos; )
303 const FileFilterElement & elem = filterList.GetNext(pos);
304 CRegExp * regexp = elem.pRegExp;
305 if (regexp->RegFind(str) != -1)
312 * @brief Test given filename against filefilter.
314 * Test filename against active filefilter. If matching rule is found
315 * we must first determine type of rule that matched. If we return FALSE
316 * from this function directory scan marks file as skipped.
318 * @param [in] pFilter Pointer to filefilter
319 * @param [in] szFileName Filename to test
320 * @return TRUE if file passes the filter
322 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
323 LPCTSTR szFileName) const
327 if (TestAgainstRegList(pFilter->filefilters, szFileName))
328 return !pFilter->default_include;
329 return pFilter->default_include;
333 * @brief Test given directory name against filefilter.
335 * Test directory name against active filefilter. If matching rule is found
336 * we must first determine type of rule that matched. If we return FALSE
337 * from this function directory scan marks file as skipped.
339 * @param [in] pFilter Pointer to filefilter
340 * @param [in] szDirName Directory name to test
341 * @return TRUE if directory name passes the filter
343 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
344 LPCTSTR szDirName) const
348 if (TestAgainstRegList(pFilter->dirfilters, szDirName))
349 return !pFilter->default_include;
350 return pFilter->default_include;
354 * @brief Return name of filter.
356 * @param [in] i Index of filter.
357 * @return Name of filter in given index.
359 CString FileFilterMgr::GetFilterName(int i) const
361 return m_filters[i]->name;
364 /** @brief Return name of filter. */
365 CString FileFilterMgr::GetFilterName(const FileFilter *pFilter) const
367 return pFilter->name;
371 * @brief Return description of filter.
373 * @param [in] i Index of filter.
374 * @return Description of filter in given index.
376 CString FileFilterMgr::GetFilterDesc(int i) const
378 return m_filters[i]->description;
381 /** @brief Return description of filter. */
382 CString FileFilterMgr::GetFilterDesc(const FileFilter *pFilter) const
384 return pFilter->description;
388 * @brief Return full path to filter.
390 * @param [in] i Index of filter.
391 * @return Full path of filter in given index.
393 CString FileFilterMgr::GetFilterPath(int i) const
395 return m_filters[i]->fullpath;
399 * @brief Return full path to filter.
401 * @param [in] pFilter Pointer to filter.
402 * @return Full path of filter.
404 CString FileFilterMgr::GetFullpath(FileFilter * pfilter) const
406 return pfilter->fullpath;
410 * @brief Reload filter from disk
412 * Reloads filter from disk. This is done by creating a new one
413 * to substitute for old one.
414 * @param [in] pFilter Pointer to filter to reload.
415 * @return FILTER_OK when succeeds, one of FILTER_RETVALUE values on error.
416 * @note Given filter (pfilter) is freed and must not be used anymore.
417 * @todo Should return new filter.
419 int FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
421 int errorcode = FILTER_OK;
422 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath, errorcode);
424 if (newfilter == NULL)
429 for (int i = 0; i < m_filters.GetSize(); ++i)
431 if (pfilter == m_filters[i])
433 m_filters.RemoveAt(i);
438 m_filters.Add(newfilter);
443 * @brief Reload filter from disk.
445 * Reloads filter from disk. This is done by creating a new one
446 * to substitute for old one.
447 * @param [in] szFullPath Full path to filter file to reload.
448 * @return FILTER_OK when succeeds or one of FILTER_RETVALUE values when fails.
450 int FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
452 int errorcode = FILTER_OK;
453 FileFilter * filter = GetFilterByPath(szFullPath);
455 errorcode = ReloadFilterFromDisk(filter);
457 errorcode = FILTER_NOTFOUND;