using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Threading; using log4net; using NDepend.Helpers.FileDirectoryPath; namespace DelftTools.Utils.IO { /// /// File manipulations /// public static class FileUtils { private static readonly ILog Log = LogManager.GetLogger(typeof(FileUtils)); /// /// Copy all files and folders in a directory to another directory /// /// TODO: make it copy directory or file, change sourceDirectory, targetDirectory to sourcePath, targetPath /// or rename this method to Copy /// /// /// /// public static void CopyDirectory(string sourceDirectory, string targetDirectory, string ignorePath = "") { var diSource = new DirectoryInfo(sourceDirectory); var diTarget = new DirectoryInfo(targetDirectory); CopyAll(diSource, diTarget, ignorePath); } /// /// Copies the source file to the target destination; if the file /// already exists, it will be overwritten /// public static void CopyFile(string sourcePath, string targetPath, bool overwrite = true) { var sourceFullPath = Path.GetFullPath(sourcePath); var targetFullPath = Path.GetFullPath(targetPath); if (sourceFullPath == targetFullPath) { return; } if (File.Exists(targetPath)) { if (!overwrite) { return; } File.Delete(targetPath); } File.Copy(sourcePath, targetPath); } /// /// Copy files in a directory and its subdirectories to another directory. /// /// /// /// public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string ignorePath) { foreach (var diSourceSubDir in source.GetDirectories().Where(diSourceSubDir => diSourceSubDir.Name != ignorePath)) { if (Directory.Exists(target.FullName) == false) { Directory.CreateDirectory(target.FullName); } var nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name); CopyAll(diSourceSubDir, nextTargetSubDir, ignorePath); } foreach (var fi in source.GetFiles()) { Log.DebugFormat(@"Copying {0}\{1}", target.FullName, fi.Name); if (!target.Exists) { target.Create(); } var path = Path.Combine(target.ToString(), fi.Name); fi.CopyTo(path, true); } } /// /// Make all files and sub-directories writable. /// /// public static void MakeWritable(string path) { if (Directory.Exists(path)) { File.SetAttributes(path, FileAttributes.Normal); } var di = new DirectoryInfo(path); foreach (DirectoryInfo di2 in di.GetDirectories()) { File.SetAttributes(path, FileAttributes.Normal); MakeWritable(Path.Combine(path, di2.Name)); } // Copy each file into it's new directory. foreach (FileInfo fi in di.GetFiles()) { String filePath = Path.Combine(path, fi.Name); File.SetAttributes(filePath, FileAttributes.Normal); } } /// /// check if the search item path is a subdirectory of the rootDir /// /// /// /// public static bool IsSubdirectory(string rootDir, string searchItem) { if (rootDir.StartsWith("\\")) //network disk? { return searchItem.StartsWith(rootDir, StringComparison.InvariantCultureIgnoreCase); } else { var root = new DirectoryPathAbsolute(rootDir); var search = new DirectoryPathAbsolute(searchItem); return search.IsChildDirectoryOf(root); } } /// /// Check if the searchItem is part of the rootDir, or equal to. /// /// /// /// public static bool IsSubdirectoryOrEquals(string rootDir, string searchItem) { DirectoryInfo di1 = new DirectoryInfo(rootDir); DirectoryInfo di2 = new DirectoryInfo(searchItem); return di2.FullName == di1.FullName || IsSubdirectory(rootDir, searchItem); } /// /// Returns if the supplied path is a directory /// /// Path to check /// Path is directory public static bool IsDirectory(string path) { return (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory; } /// /// Compares two directory strings /// /// /// /// public static bool CompareDirectories(string rootDir, string searchItem) { var root = new FilePathAbsolute(Path.GetFullPath(rootDir)); var search = new FilePathAbsolute(Path.GetFullPath(searchItem)); return (root == search); } /// /// Returns a relative path string from a full path. /// /// public static string GetRelativePath(string rootDir, string filePath) { if (rootDir == null || filePath == null) { return filePath; } if (rootDir.StartsWith("\\") && IsSubdirectory(rootDir, filePath)) //network disk? { return "." + filePath.Substring(rootDir.Length); } try { var filePathAbsolute = new FilePathAbsolute(filePath); var directoryPathAbsolute = new DirectoryPathAbsolute(rootDir); FilePathRelative filePathRelative = filePathAbsolute.GetPathRelativeFrom(directoryPathAbsolute); return filePathRelative.Path; } catch (Exception) { // gulp: return original filepath } return filePath; } /// /// Create dir if not exists /// /// File path to a directory /// Optional flag to delete the specified directory if it does exist when set to true /// When: /// The directory specified by is read-only /// Furthermore when is true: /// A file with the same name and location specified by exists. -or- /// The directory is the application's current working directory. /// /// When: The caller does not have the required permission. /// /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . -or- /// is prefixed with, or contains only a colon character (:). /// is null. /// /// The specified path, file name, or both exceed the system-defined maximum length. /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must be less than 260 characters. /// The specified path is invalid (for example, it is on an unmapped drive). /// contains a colon character (:) that is not part of a drive label ("C:\"). public static void CreateDirectoryIfNotExists(string path, bool deleteIfExists = false) { if (Directory.Exists(path)) { //replace the directory with a one if (deleteIfExists) { Directory.Delete(path, true); Directory.CreateDirectory(path); } } else { Directory.CreateDirectory(path); } } /// /// Initializes filesystemwatcher /// /// public static FileSystemWatcher CreateWatcher() { // use built-in filesystemwatcher class to monitor creation/modification/deletion of files // example http://www.codeguru.com/csharp/csharp/cs_network/article.php/c6043/ var watcher = new FileSystemWatcher { IncludeSubdirectories = false, NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size }; return watcher; } /// /// Checks wether the path is a valid relative path /// /// /// public static bool PathIsRelative(string path) { return !Path.IsPathRooted(path); } /// /// Replaces text in a file. /// /// Path of the text file. /// Text to search for. /// Text to replace the search text. public static void ReplaceInFile(string filePath, string searchText, string replaceText) { var reader = new StreamReader(filePath); string content = reader.ReadToEnd(); reader.Close(); content = Regex.Replace(content, searchText, replaceText); var writer = new StreamWriter(filePath); writer.Write(content); writer.Close(); } /// /// Creates a temporary directory /// /// path to temporary directory public static string CreateTempDirectory() { string path = Path.GetFileNameWithoutExtension(Path.GetRandomFileName()); Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), path)); return Path.Combine(Path.GetTempPath(), path); } /// /// Check if one or more files can be copied /// /// Todo: Is this function necessary? Probably better design to just copy and handle errors /// /// /// /// public static bool CanCopy(IEnumerable files, string targetDir) { //todo check if targetdir is readonly //check if targetdrive has enough space long spaceRequired = 0; foreach (string file in files) { FileInfo fi = new FileInfo(file); spaceRequired += fi.Length; } string driveName = targetDir.Substring(0, 1); DriveInfo di = new DriveInfo(driveName); if (spaceRequired > di.AvailableFreeSpace) { return false; } //check if files exist with the same name foreach (string file in files) { var info = new FileInfo(Path.Combine(targetDir, Path.GetFileName(file))); if (info.Exists) { return false; } } return true; } /// /// check if file can be copied to another folder /// /// Todo: Is this function necessary? Probably better design to just copy and handle errors /// /// /// /// public static bool CanCopy(string name, string targetDir) { string[] files = new string[] { name }; return CanCopy(files, targetDir); } /// /// Check if the content of two files are equal. /// /// first file path /// second file path /// public static bool FilesAreEqual(string file1Path, string file2Path) { var first = new FileInfo(file1Path); var second = new FileInfo(file2Path); if (first.Length != second.Length) { return false; } const int bytesToRead = sizeof(Int64); var iterations = (int) Math.Ceiling((double) first.Length/bytesToRead); using (FileStream fs1 = first.OpenRead()) { using (FileStream fs2 = second.OpenRead()) { var one = new byte[bytesToRead]; var two = new byte[bytesToRead]; for (int i = 0; i < iterations; i++) { fs1.Read(one, 0, bytesToRead); fs2.Read(two, 0, bytesToRead); if (BitConverter.ToInt64(one, 0) != BitConverter.ToInt64(two, 0)) { return false; } } } } return true; } /// /// Checks if the extension of a file belongs to the filefilter /// /// "My file format1 (*.ext1)|*.ext1|My file format2 (*.ext2)|*.ext2" /// Path to a file or filename including extension /// public static bool FileMatchesFileFilterByExtension(string fileFilter, string path) { /* if (!fileFilterRegex.Match(fileFilter).Success) { throw new ArgumentException(string.Format("Invalid filefilter: {0}", fileFilter)); }*/ if (String.IsNullOrEmpty(Path.GetExtension(path))) { return false; } return Regex.Match(fileFilter, String.Format(@"(\||\;)+\s*\*\{0}", Path.GetExtension(path))).Success; } /// /// Deletes the given file or directory if it exists /// public static void DeleteIfExists(string path) { if (!File.Exists(path) & !Directory.Exists(path)) { return; } var attributes = File.GetAttributes(path); // if file is readonly - make it non-readonly if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { File.SetAttributes(path, attributes ^ FileAttributes.ReadOnly); } // now delete everything if (File.Exists(path)) { File.Delete(path); } else if (Directory.Exists(path)) { foreach (var path2 in Directory.GetDirectories(path).Union(Directory.GetFiles(path))) { DeleteIfExists(path2); } Directory.Delete(path); } } public static string GetUniqueFileNameWithPath(string existingFileName) { var newFileName = GetUniqueFileName(existingFileName); return Path.Combine(Path.GetDirectoryName(existingFileName), newFileName); } public static string MakeDirectoryNameUnique(string directory) { if (!Directory.Exists(directory)) { return directory; } var parentDirectory = Directory.GetParent(directory); var directoryName = Path.GetFileName(directory); var uniqueFolderName = NamingHelper.GenerateUniqueNameFromList(directoryName + "{0}", true, parentDirectory.GetDirectories().Select(d => d.Name)); return Path.Combine(parentDirectory.FullName, uniqueFolderName); } /// /// /// /// The name of the file wich may include the path /// public static string GetUniqueFileName(string existingFileName) { if (existingFileName == null) { throw new ArgumentNullException("existingFileName"); } var directory = Path.GetDirectoryName(existingFileName).Replace(Path.GetFileName(existingFileName), ""); directory = string.IsNullOrEmpty(directory) ? "." : directory; // Declare a function to strip a file name which leaves out the extension Func getFileNameWithoutExtension = Path.GetFileNameWithoutExtension; var searchString = string.Format("{0}*.*", getFileNameWithoutExtension(existingFileName)); // get all items with the same name var items = Directory.GetFiles(directory, searchString); // make a list of INameable items where the Name property will get the name of the file var namedItems = items.Select(f => new FileName { Name = getFileNameWithoutExtension(f) }); var newName = getFileNameWithoutExtension(existingFileName); if (namedItems.Any()) { newName = NamingHelper.GetUniqueName(string.Format("{0} ({{0}})", newName), namedItems, typeof(INameable)); } return newName + Path.GetExtension(existingFileName); } public static IList GetDirectoriesRelative(string path) { var q = from subdir in Directory.GetDirectories(path) select GetRelativePath(Path.GetFullPath(path), Path.GetFullPath(subdir)); return q.ToList(); } public static bool IsValidFileName(string fileName) { return fileName != null && fileName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 && fileName.Trim().Length > 0; } public static IEnumerable GetReducedInvalidFileNameChars() { // reduce by excluding 'exotic' characters with ASCII code < 32 return Path.GetInvalidFileNameChars().Where(c => c > 31); } /// /// Checks if directory is empty /// /// The path to the directory /// true is directory exists and is empty, false otherwise public static bool IsDirectoryEmpty(string path) { if (!Directory.Exists(path)) { return false; } return !Directory.EnumerateFileSystemEntries(path).Any(); } /// /// Blocks until the file is not locked any more. /// /// /// /// public static bool WaitForFile(string fullPath, int maxTries = 5, int waitTimePerTryInMillis = 500) { // from: http://stackoverflow.com/questions/50744/wait-until-file-is-unlocked-in-net var numTries = 0; while (true) { ++numTries; try { // Attempt to open the file exclusively. using (var fs = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None, 100)) { fs.ReadByte(); break; // If we got this far the file is ready } } catch (Exception) { if (numTries > maxTries) { return false; } // Wait for the lock to be released Thread.Sleep(waitTimePerTryInMillis); } } return true; } /// /// Computes the checksum for a given file. /// /// The file path. /// The /// Uses the MD5 checksum algorithm. public static string GetChecksum(string filePath) { using (var md5Algorithm = MD5.Create()) { using (var stream = File.OpenRead(filePath)) { var computedByteHash = md5Algorithm.ComputeHash(stream); return BitConverter.ToString(computedByteHash).Replace("-", "").ToLower(); } } } /// /// Verifies that a given file matches with a checksum. /// /// The file path. /// The checksum to be matched. /// True if the checksum matches; false otherwise. /// public static bool VerifyChecksum(string filePath, string checksumToBeMatched) { var checksum = GetChecksum(filePath); return Equals(checksumToBeMatched, checksum); } /// /// Check if two paths are equal. /// This prevents failure of forward and backward slashes. /// It checks on full path names via . /// public static bool PathsAreEqual(string path1, string path2) { var info1 = new FileInfo(path1); var info2 = new FileInfo(path2); return info1.FullName == info2.FullName; } private class FileName : INameable { public string Name { get; set; } } } }