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; }
}
}
}