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"
26 static char THIS_FILE[] = __FILE__;
30 * @brief Deletes items from filter list.
32 * @param [in] filterList List to empty.
34 void EmptyFilterList(FileFilterList & filterList)
36 while (!filterList.IsEmpty())
38 FileFilterElement &elem = filterList.GetHead();
40 filterList.RemoveHead();
45 * @brief One actual filter.
47 * For example, this might be a GNU C filter, excluding *.o files and CVS
48 * directories. That is to say, a filter is a set of file masks and
49 * directory masks. Usually FileFilter contains rules from one filter
50 * definition file. So it can be thought as filter file contents.
55 bool default_include; /**< If true, filter rules are inclusive by default */
56 CString name; /**< Filter name (shown in UI) */
57 CString description; /**< Filter description text */
58 CString fullpath; /**< Full path to filter file */
59 FileFilterList filefilters; /**< List of rules for files */
60 FileFilterList dirfilters; /**< List of rules for directories */
61 FileFilter() : default_include(true) { }
65 FileFilter::~FileFilter()
67 EmptyFilterList(filefilters);
68 EmptyFilterList(dirfilters);
71 FileFilterMgr::~FileFilterMgr()
77 * @brief Loads filterfile and adds filters.
79 * @param [in] szFilterFile
80 * @bug Silently fails loading
82 void FileFilterMgr::AddFilter(LPCTSTR szFilterFile)
84 TCHAR dir[_MAX_DRIVE] = {0};
85 TCHAR path[_MAX_PATH] = {0};
86 TCHAR filename[_MAX_PATH] = {0};
87 TCHAR ext[_MAX_EXT] = {0};
89 _tsplitpath(szFilterFile, dir, path, filename, ext);
91 CString filterPath = dir;
93 CString filterFile = filename;
96 FileFilter * pFilter = LoadFilterFile(szFilterFile, filterFile);
98 m_filters.Add(pFilter);
102 * @brief Load all filter files matching pattern from disk into internal filter set.
104 * @param [in] szPattern Pattern from where to load filters, for example "\\Filters\\*.flt"
105 * @param [in] szExt File-extension of filter files
107 void FileFilterMgr::LoadFromDirectory(LPCTSTR szPattern, LPCTSTR szExt)
110 BOOL bWorking = finder.FindFile(szPattern);
111 int extlen = szExt ? _tcslen(szExt) : 0;
114 bWorking = finder.FindNextFile();
115 if (finder.IsDots() || finder.IsDirectory())
117 CString sFilename = finder.GetFileName();
120 // caller specified a specific extension
121 // (This is really a workaround for brokenness in windows, which
122 // doesn't screen correctly on extension in pattern)
123 if (sFilename.Right(extlen).CompareNoCase(szExt))
126 FileFilter * pfilter = LoadFilterFile(finder.GetFilePath(), sFilename);
127 m_filters.Add(pfilter);
132 * @brief Removes filter from filterlist.
134 * @param [in] szFilterFile Filename of filter to remove.
136 void FileFilterMgr::RemoveFilter(LPCTSTR szFilterFile)
138 // Note that m_filters.GetSize can change during loop
139 for (int i = 0; i < m_filters.GetSize(); i++)
141 FileFilter * pFilter = m_filters.GetAt(i);
142 if (pFilter->fullpath.CompareNoCase(szFilterFile) == 0)
144 m_filters.RemoveAt(i);
151 * @brief Removes all filters from current list.
153 void FileFilterMgr::DeleteAllFilters()
155 for (int i=0; i<m_filters.GetSize(); ++i)
160 m_filters.RemoveAll();
164 * @brief Add a single pattern (if nonempty & valid) to a pattern list.
166 * @param [in] filterList List where pattern is added.
167 * @param [in] str Temporary variable (ie, it may be altered)
169 static void AddFilterPattern(FileFilterList & filterList, CString & str)
171 LPCTSTR commentLeader = _T("##"); // Starts comment
175 // Ignore lines beginning with '##'
176 int pos = str.Find(commentLeader);
180 // Find possible comment-separator '<whitespace>##'
181 while (pos > 0 && !_istspace(str[pos - 1]))
182 pos = str.Find(commentLeader, pos);
184 // Remove comment and whitespaces before it
191 CRegExp * regexp = new CRegExp;
194 if (regexp->RegComp(str))
196 FileFilterElement elem;
197 elem.pRegExp = regexp;
199 filterList.AddTail(elem);
207 * @brief Parse a filter file, and add it to array if valid.
209 * @param [in] szFilePath Path (w/ filename) to file to load.
210 * @param [in] szFilename Name of file to load.
211 * @todo Remove redundancy from parameters (both having filename)
213 FileFilter * FileFilterMgr::LoadFilterFile(LPCTSTR szFilepath, LPCTSTR szFilename)
216 if (!file.OpenReadOnly(szFilepath))
219 file.ReadBom(); // in case it is a Unicode file, let UniMemFile handle BOM
221 FileFilter *pfilter = new FileFilter;
222 pfilter->fullpath = szFilepath;
223 pfilter->name = szFilename; // default if no name
226 while (file.ReadString(sLine, &lossy))
231 if (0 == _tcsncmp(sLine, _T("name:"), 5))
233 // specifies display name
234 CString str = sLine.Mid(5);
239 else if (0 == _tcsncmp(sLine, _T("desc:"), 5))
241 // specifies display name
242 CString str = sLine.Mid(5);
245 pfilter->description = str;
247 else if (0 == _tcsncmp(sLine, _T("def:"), 4))
250 CString str = sLine.Mid(4);
252 if (str == _T("0") || str == _T("no") || str == _T("exclude"))
253 pfilter->default_include = false;
254 else if (str == _T("1") || str == _T("yes") || str == _T("include"))
255 pfilter->default_include = true;
257 else if (0 == _tcsncmp(sLine, _T("f:"), 2))
260 CString str = sLine.Mid(2);
261 AddFilterPattern(pfilter->filefilters, str);
263 else if (0 == _tcsncmp(sLine, _T("d:"), 2))
266 CString str = sLine.Mid(2);
267 AddFilterPattern(pfilter->dirfilters, str);
274 * @brief Give client back a pointer to the actual filter.
276 * @param [in] szFilterPath Full path to filterfile.
277 * @return Pointer to found filefilter or NULL;
278 * @note We just do a linear search, because this is seldom called
280 FileFilter * FileFilterMgr::GetFilterByPath(LPCTSTR szFilterPath)
282 for (int i=0; i<m_filters.GetSize(); ++i)
284 if (m_filters[i]->fullpath.CompareNoCase(szFilterPath) == 0)
291 * @brief Test given string against given regexp list.
293 * @param [in] filterList List of regexps to test against.
294 * @param [in] szTest String to test against regexps.
295 * @return TRUE if string passes
296 * @note Matching stops when first match is found.
298 BOOL TestAgainstRegList(const FileFilterList & filterList, LPCTSTR szTest)
300 CString str = szTest;
302 for (POSITION pos = filterList.GetHeadPosition(); pos; )
304 const FileFilterElement & elem = filterList.GetNext(pos);
305 CRegExp * regexp = elem.pRegExp;
306 if (regexp->RegFind(str) != -1)
313 * @brief Test given filename against filefilter.
315 * Test filename against active filefilter. If matching rule is found
316 * we must first determine type of rule that matched. If we return FALSE
317 * from this function directory scan marks file as skipped.
319 * @param [in] pFilter Pointer to filefilter
320 * @param [in] szFileName Filename to test
321 * @return TRUE if file passes the filter
323 BOOL FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter,
324 LPCTSTR szFileName) const
328 if (TestAgainstRegList(pFilter->filefilters, szFileName))
329 return !pFilter->default_include;
330 return pFilter->default_include;
334 * @brief Test given directory name against filefilter.
336 * Test directory name against active filefilter. If matching rule is found
337 * we must first determine type of rule that matched. If we return FALSE
338 * from this function directory scan marks file as skipped.
340 * @param [in] pFilter Pointer to filefilter
341 * @param [in] szDirName Directory name to test
342 * @return TRUE if directory name passes the filter
344 BOOL FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter,
345 LPCTSTR szDirName) const
349 if (TestAgainstRegList(pFilter->dirfilters, szDirName))
350 return !pFilter->default_include;
351 return pFilter->default_include;
355 * @brief Return name of filter.
357 * @param [in] i Index of filter.
358 * @return Name of filter in given index.
360 CString FileFilterMgr::GetFilterName(int i) const
362 return m_filters[i]->name;
366 * @brief Return description of filter.
368 * @param [in] i Index of filter.
369 * @return Description of filter in given index.
371 CString FileFilterMgr::GetFilterDesc(int i) const
373 return m_filters[i]->description;
377 * @brief Return full path to filter.
379 * @param [in] i Index of filter.
380 * @return Full path of filter in given index.
382 CString FileFilterMgr::GetFilterPath(int i) const
384 return m_filters[i]->fullpath;
388 * @brief Return full path to filter.
390 * @param [in] pFilter Pointer to filter.
391 * @return Full path of filter.
393 CString FileFilterMgr::GetFullpath(FileFilter * pfilter) const
395 return pfilter->fullpath;
399 * @brief Reload filter from disk
401 * Reloads filter from disk. This is done by creating a new one
402 * to substitute for old one.
403 * @param [in] pFilter Pointer to filter to reload.
405 void FileFilterMgr::ReloadFilterFromDisk(FileFilter * pfilter)
407 FileFilter * newfilter = LoadFilterFile(pfilter->fullpath, pfilter->name);
408 for (int i=0; i<m_filters.GetSize(); ++i)
410 if (pfilter == m_filters[i])
412 m_filters.RemoveAt(i);
417 m_filters.Add(newfilter);
421 * @brief Reload filter from disk
423 * Reloads filter from disk. This is done by creating a new one
424 * to substitute for old one.
425 * @param [in] szFullPath Full path to filter file to reload.
427 void FileFilterMgr::ReloadFilterFromDisk(LPCTSTR szFullPath)
429 FileFilter * filter = GetFilterByPath(szFullPath);
430 ReloadFilterFromDisk(filter);