/// <summary> /// Moves a file between two filesystems. /// </summary> /// <param name="fs">The source filesystem</param> /// <param name="srcPath">The source path of the file to move from the source filesystem</param> /// <param name="destFileSystem">The destination filesystem</param> /// <param name="destPath">The destination path of the file in the destination filesystem</param> public static void MoveFileCross(this IFileSystem fs, UPath srcPath, IFileSystem destFileSystem, UPath destPath) { if (destFileSystem is null) { throw new ArgumentNullException(nameof(destFileSystem)); } // If this is the same filesystem, use the file system directly to perform the action if (fs == destFileSystem) { fs.MoveFile(srcPath, destPath); return; } // Check source srcPath.AssertAbsolute(nameof(srcPath)); if (!fs.FileExists(srcPath)) { throw NewFileNotFoundException(srcPath); } // Check destination destPath.AssertAbsolute(nameof(destPath)); var destDirectory = destPath.GetDirectory(); if (!destFileSystem.DirectoryExists(destDirectory)) { throw NewDirectoryNotFoundException(destPath); } if (destFileSystem.DirectoryExists(destPath)) { throw NewDestinationDirectoryExistException(destPath); } if (destFileSystem.FileExists(destPath)) { throw NewDestinationFileExistException(destPath); } using (var sourceStream = fs.OpenFile(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var copied = false; try { using (var destStream = destFileSystem.OpenFile(destPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { sourceStream.CopyTo(destStream); } // Preserve all attributes and times destFileSystem.SetAttributes(destPath, fs.GetAttributes(srcPath)); destFileSystem.SetCreationTime(destPath, fs.GetCreationTime(srcPath)); destFileSystem.SetLastAccessTime(destPath, fs.GetLastAccessTime(srcPath)); destFileSystem.SetLastWriteTime(destPath, fs.GetLastWriteTime(srcPath)); copied = true; } finally { if (!copied) { try { destFileSystem.DeleteFile(destPath); } catch { // ignored } } } } var deleted = false; try { fs.DeleteFile(srcPath); deleted = true; } finally { if (!deleted) { try { destFileSystem.DeleteFile(destPath); } catch { // ignored } } } }
private SearchPattern(ref UPath path, ref string searchPattern) { path.AssertAbsolute(); if (searchPattern == null) { throw new ArgumentNullException(nameof(searchPattern)); } _exactMatch = null; _regexMatch = null; // Optimized path, most common case if (searchPattern == "*") { return; } if (searchPattern.StartsWith("/")) { throw new ArgumentException($"The search pattern `{searchPattern}` cannot start by an absolute path `/`"); } searchPattern = searchPattern.Replace('\\', '/'); // If the path contains any directory, we need to concatenate the directory part with the input path if (searchPattern.IndexOf('/') > 0) { var pathPattern = new UPath(searchPattern); var directory = pathPattern.GetDirectory(); if (!directory.IsNull && !directory.IsEmpty) { path = path / directory; } searchPattern = pathPattern.GetName(); // If the search pattern is again a plain any, optimized path if (searchPattern == "*") { return; } } var startIndex = 0; int nextIndex; StringBuilder builder = null; try { while ((nextIndex = searchPattern.IndexOfAny(SpecialChars, startIndex)) >= 0) { if (builder == null) { builder = UPath.GetSharedStringBuilder(); builder.Append("^"); } var lengthToEscape = nextIndex - startIndex; if (lengthToEscape > 0) { var toEscape = Regex.Escape(searchPattern.Substring(startIndex, lengthToEscape)); builder.Append(toEscape); } var c = searchPattern[nextIndex]; var regexPatternPart = c == '*' ? "[^/]*" : "[^/]"; builder.Append(regexPatternPart); startIndex = nextIndex + 1; } if (builder == null) { _exactMatch = searchPattern; } else { var length = searchPattern.Length - startIndex; if (length > 0) { var toEscape = Regex.Escape(searchPattern.Substring(startIndex, length)); builder.Append(toEscape); } builder.Append("$"); var regexPattern = builder.ToString(); _regexMatch = new Regex(regexPattern); } } finally { if (builder != null) { builder.Length = 0; } } }
/// <summary> /// Copies a file between two filesystems. /// </summary> /// <param name="fs">The source filesystem</param> /// <param name="srcPath">The source path of the file to copy from the source filesystem</param> /// <param name="destFileSystem">The destination filesystem</param> /// <param name="destPath">The destination path of the file in the destination filesystem</param> /// <param name="overwrite"><c>true</c> to overwrite an existing destination file</param> public static void CopyFileCross(this IFileSystem fs, UPath srcPath, IFileSystem destFileSystem, UPath destPath, bool overwrite) { if (destFileSystem is null) { throw new ArgumentNullException(nameof(destFileSystem)); } // If this is the same filesystem, use the file system directly to perform the action if (fs == destFileSystem) { fs.CopyFile(srcPath, destPath, overwrite); return; } srcPath.AssertAbsolute(nameof(srcPath)); if (!fs.FileExists(srcPath)) { throw NewFileNotFoundException(srcPath); } destPath.AssertAbsolute(nameof(destPath)); var destDirectory = destPath.GetDirectory(); if (!destFileSystem.DirectoryExists(destDirectory)) { throw NewDirectoryNotFoundException(destDirectory); } if (destFileSystem.FileExists(destPath) && !overwrite) { throw new IOException($"The destination file path `{destPath}` already exist and overwrite is false"); } using (var sourceStream = fs.OpenFile(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var copied = false; try { using (var destStream = destFileSystem.OpenFile(destPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { sourceStream.CopyTo(destStream); } // NOTE: For some reasons, we can sometimes get an Unauthorized access if we try to set the LastWriteTime after the SetAttributes // So we setup it here. destFileSystem.SetLastWriteTime(destPath, fs.GetLastWriteTime(srcPath)); // Preserve attributes and LastWriteTime as a regular File.Copy destFileSystem.SetAttributes(destPath, fs.GetAttributes(srcPath)); copied = true; } finally { if (!copied) { try { destFileSystem.DeleteFile(destPath); } catch { // ignored } } } } }
private SearchPattern(ref UPath path, ref string searchPattern) { path.AssertAbsolute(); if (searchPattern == null) { throw new ArgumentNullException(nameof(searchPattern)); } _exactMatch = null; _regexMatch = null; // Optimized path, most common case if (searchPattern == "*") { return; } if (searchPattern.StartsWith("/")) { throw new ArgumentException($"The search pattern `{searchPattern}` cannot start by an absolute path `/`"); } searchPattern = searchPattern.Replace('\\', '/'); // If the path contains any directory, we need to concatenate the directory part with the input path if (searchPattern.IndexOf('/') > 0) { var pathPattern = new UPath(searchPattern); var directory = pathPattern.GetDirectory(); if (!directory.IsNull && !directory.IsEmpty) { path = path / directory; } searchPattern = pathPattern.GetName(); // If the search pattern is again a plain any, optimized path if (searchPattern == "*") { return; } } bool appendSpecialCaseForExt3Chars = false; var startIndex = 0; int nextIndex; StringBuilder builder = null; try { while ((nextIndex = searchPattern.IndexOfAny(SpecialChars, startIndex)) >= 0) { if (builder == null) { builder = UPath.GetSharedStringBuilder(); builder.Append("^"); } var lengthToEscape = nextIndex - startIndex; if (lengthToEscape > 0) { var toEscape = Regex.Escape(searchPattern.Substring(startIndex, lengthToEscape)); builder.Append(toEscape); } var c = searchPattern[nextIndex]; var regexPatternPart = c == '*' ? "[^/]*" : "[^/]"; builder.Append(regexPatternPart); // If the specified extension is exactly three characters long, // the method returns files with extensions that begin with the specified extension. // For example, "*.xls" returns both "book.xls" and "book.xlsx". // 012345 // *.txt if (c == '*' && nextIndex + 5 == searchPattern.Length && searchPattern[nextIndex + 1] == '.' && searchPattern.IndexOf('.', nextIndex + 2) < 0) { appendSpecialCaseForExt3Chars = true; } startIndex = nextIndex + 1; } if (builder == null) { _exactMatch = searchPattern; } else { var length = searchPattern.Length - startIndex; if (length > 0) { var toEscape = Regex.Escape(searchPattern.Substring(startIndex, length)); builder.Append(toEscape); } if (appendSpecialCaseForExt3Chars) { builder.Append("[^/]*"); } builder.Append("$"); var regexPattern = builder.ToString(); _regexMatch = new Regex(regexPattern); } } finally { if (builder != null) { builder.Length = 0; } } }