/*
* Common.IO.FindFile - Regex enabled file/directory search class
* Copyright (C) 2003 S�bastien Lorion
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace Common.IO
{
public class FindFile
{
/// <summary>
/// Provides attributes for files and directories to look for during a search.
/// </summary>
[Flags]
public enum SearchAttributes
{
Archive = FileAttributes.Archive,
Compressed = FileAttributes.Compressed,
Device = FileAttributes.Device,
Directory = FileAttributes.Directory,
Encrypted = FileAttributes.Encrypted,
Hidden = FileAttributes.Hidden,
Normal = FileAttributes.Normal,
NotContentIndexed = FileAttributes.NotContentIndexed,
Offline = FileAttributes.Offline,
ReadOnly = FileAttributes.ReadOnly,
ReparsePoint = FileAttributes.ReparsePoint,
SparseFile = FileAttributes.SparseFile,
System = FileAttributes.System,
Temporary = FileAttributes.Temporary,
All = 0x7FFF,
AnyFile = All - Directory
}
/// <summary>
/// The delegate that is called each time a file is found during a search by <see cref="Common.IO.FindFile"/>
/// </summary>
/// <remarks>Sets the return value to true to stop the search.</remarks>
public delegate bool FoundAFile(String path, FileAttributes fileAttributes);
public static Int32 Search(String basePath, String searchPattern, SearchAttributes searchAttributes, FoundAFile callback)
{
return Search(basePath, searchPattern, false, RegexOptions.None, searchAttributes, -1, -1, callback);
}
public static Int32 Search(String basePath, String searchPattern, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
return Search(basePath, searchPattern, false, RegexOptions.None, searchAttributes, maxDepth, maxResults, callback);
}
public static Int32 Search(String basePath, String searchPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, FoundAFile callback)
{
return Search(basePath, searchPattern, true, regexOptions, searchAttributes, -1, -1, callback);
}
public static Int32 Search(String basePath, String searchPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
return Search(basePath, searchPattern, true, regexOptions, searchAttributes, maxDepth, maxResults, callback);
}
/// <summary>
/// Calls a delegate each time a file is found that match the specified search pattern.
/// </summary>
/// <param name="basePath">The starting point of the search.</param>
/// <param name="searchPattern">The search string to match against the names of files in path.</param>
/// <param name="isRegexPattern">Indicates if the search pattern is a regular expression.</param>
/// <param name="regexOptions">The options for the regex engine.</param>
/// <param name="searchAttributes">The attributes for files and directories to look for during the search.</param>
/// <param name="maxDepth">The maximum level of recursion.</param>
/// <param name="maxResults">The maximum number of results to return.</param>
/// <param name="callback">The callback function to call each time a file is found.</param>
/// <returns>The number of results.</returns>
private static Int32 Search(String basePath, String searchPattern, bool isRegexPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
if (isRegexPattern)
return Search(basePath, new String[] {searchPattern}, isRegexPattern, regexOptions, searchAttributes, maxDepth, maxResults, callback);
else
{
Int32 resultCount = 0;
if (maxDepth < 0)
maxDepth = Int32.MaxValue;
if (maxResults < 0)
maxResults = Int32.MaxValue;
Search(basePath, searchPattern, searchAttributes, maxDepth, maxResults, callback, 0, ref resultCount);
return resultCount;
}
}
public static Int32 Search(String basePath, String[] searchPattern, SearchAttributes searchAttributes, FoundAFile callback)
{
return Search(basePath, searchPattern, false, RegexOptions.None, searchAttributes, -1, -1, callback);
}
public static Int32 Search(String basePath, String[] searchPattern, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
return Search(basePath, searchPattern, false, RegexOptions.None, searchAttributes, maxDepth, maxResults, callback);
}
public static Int32 Search(String basePath, String[] searchPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, FoundAFile callback)
{
return Search(basePath, searchPattern, true, regexOptions, searchAttributes, -1, -1, callback);
}
public static Int32 Search(String basePath, String[] searchPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
return Search(basePath, searchPattern, true, regexOptions, searchAttributes, maxDepth, maxResults, callback);
}
/// <summary>
/// Calls a delegate each time a file is found that match the specified search pattern.
/// </summary>
/// <param name="basePath">The starting point of the search.</param>
/// <param name="searchPatterns">The search strings to match against the names of files in path.</param>
/// <param name="isRegexPattern">Indicates if the search patterns are regular expressions.</param>
/// <param name="regexOptions">The options for the regex engine.</param>
/// <param name="searchAttributes">The attributes for files and directories to look for during the search.</param>
/// <param name="maxDepth">The maximum level of recursion.</param>
/// <param name="maxResults">The maximum number of results to return.</param>
/// <param name="callback">The callback function to call each time a file is found.</param>
/// <returns>The number of results.</returns>
private static Int32 Search(String basePath, String[] searchPatterns, bool isRegexPattern, RegexOptions regexOptions, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback)
{
StringBuilder totalPattern = new StringBuilder();
totalPattern.Append('(');
foreach (String pattern in searchPatterns)
{
if (isRegexPattern)
totalPattern.Append(pattern);
else
{
String converted = pattern.Replace(".", @"\.");
converted = converted.Replace("*", ".*");
totalPattern.Append(converted);
}
totalPattern.Append('|');
}
totalPattern[totalPattern.Length - 1] = ')';
Int32 resultCount = 0;
if (maxDepth < 0)
maxDepth = Int32.MaxValue;
if (maxResults < 0)
maxResults = Int32.MaxValue;
regexOptions = regexOptions | RegexOptions.Compiled;
Regex regex = new Regex(totalPattern.ToString(), regexOptions);
SearchRegex(basePath, regex, searchAttributes, maxDepth, maxResults, callback, 0, ref resultCount);
return resultCount;
}
/// <summary>
/// Calls a delegate each time a file is found that match the specified search pattern.
/// </summary>
/// <param name="basePath">The starting point of the search.</param>
/// <param name="searchPattern">The search string to match against the names of files in path.</param>
/// <param name="searchAttributes">The attributes for files and directories to look for during the search.</param>
/// <param name="maxDepth">The maximum level of recursion.</param>
/// <param name="maxResults">The maximum number of results to return.</param>
/// <param name="callback">The callback function to call each time a file is found.</param>
/// <param name="nestedLevel">(Internal) Indicates the current level of recursion.</param>
/// <param name="resultCount">(Internal) Indicates the number of results found so far.</param>
private static void Search(String basePath, String searchPattern, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback, Int32 nestedLevel, ref Int32 resultCount)
{
try
{
bool isCanceled = false;
foreach (String path in Directory.GetDirectories(basePath, searchPattern))
{
if (!isCanceled && resultCount < maxResults)
{
if ((searchAttributes & SearchAttributes.Directory) != 0)
{
FileAttributes fileAttributes = File.GetAttributes(path);
if (((Int32)fileAttributes & (Int32)searchAttributes) != 0)
{
isCanceled = callback(path, fileAttributes);
resultCount++;
}
}
}
else
break;
if (nestedLevel < maxDepth)
Search(path, searchPattern, searchAttributes, maxDepth, maxResults, callback, nestedLevel + 1, ref resultCount);
}
// if we only need directories, we might as well skip this section altogether
if (!isCanceled || (Int32)searchAttributes == (Int32)FileAttributes.Directory)
{
foreach (String path in Directory.GetFiles(basePath, searchPattern))
{
if (!isCanceled && resultCount < maxResults)
{
FileAttributes fileAttributes = File.GetAttributes(path);
if (((Int32)fileAttributes & (Int32)searchAttributes) != 0)
{
isCanceled = callback(path, fileAttributes);
resultCount++;
}
}
else
break;
}
}
}
catch (System.UnauthorizedAccessException) {}
}
/// <summary>
/// Calls a delegate each time a file is found that match the specified search pattern.
/// </summary>
/// <param name="basePath">The starting point of the search.</param>
/// <param name="searchPattern">The search pattern to match against the names of files in path.</param>
/// <param name="searchAttributes">The attributes for files and directories to look for during the search.</param>
/// <param name="maxDepth">The maximum level of recursion.</param>
/// <param name="maxResults">The maximum number of results to return.</param>
/// <param name="callback">The callback function to call each time a file is found.</param>
/// <param name="nestedLevel">(Internal) Indicates the current level of recursion.</param>
/// <param name="resultCount">(Internal) Indicates the number of results found so far.</param>
private static void SearchRegex(String basePath, Regex searchPattern, SearchAttributes searchAttributes, Int32 maxDepth, Int32 maxResults, FoundAFile callback, Int32 nestedLevel, ref Int32 resultCount)
{
try
{
bool isCanceled = false;
foreach (String path in Directory.GetDirectories(basePath, "*"))
{
if (!isCanceled && resultCount < maxResults)
{
if ((searchAttributes & SearchAttributes.Directory) != 0)
{
FileAttributes fileAttributes = File.GetAttributes(path);
if (((Int32)fileAttributes & (Int32)searchAttributes) != 0)
{
if (searchPattern.IsMatch(Path.GetFileName(path)))
{
isCanceled = callback(path, fileAttributes);
resultCount++;
}
}
}
}
else
break;
if (nestedLevel < maxDepth)
SearchRegex(path, searchPattern, searchAttributes, maxDepth, maxResults, callback, nestedLevel + 1, ref resultCount);
}
// if we only need directories, we might as well skip this section altogether
if (!isCanceled || (Int32)searchAttributes == (Int32)FileAttributes.Directory)
{
foreach (String path in Directory.GetFiles(basePath, "*"))
{
if (!isCanceled && resultCount < maxResults)
{
FileAttributes fileAttributes = File.GetAttributes(path);
if (((Int32)fileAttributes & (Int32)searchAttributes) != 0)
{
if (searchPattern.IsMatch(Path.GetFileName(path)))
{
isCanceled = callback(path, fileAttributes);
resultCount++;
}
}
}
else
break;
}
}
}
catch (System.UnauthorizedAccessException) {}
}
}
}