#region Disclaimer / License // Copyright (C) 2008, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; namespace Duplicati.Library.Core { public static class Utility { /// /// Copies the content of one stream into another /// /// The stream to read from /// The stream to write to public static void CopyStream(System.IO.Stream source, System.IO.Stream target) { CopyStream(source, target, true); } /// /// Copies the content of one stream into another /// /// The stream to read from /// The stream to write to /// True if an attempt should be made to rewind the source stream, false otherwise public static void CopyStream(System.IO.Stream source, System.IO.Stream target, bool tryRewindSource) { if (tryRewindSource && source.CanSeek) try { source.Position = 0; } catch { } byte[] buf = new byte[4096]; int read; while ((read = source.Read(buf, 0, buf.Length)) != 0) target.Write(buf, 0, read); } /// /// Returns a list of all files found in the given folder. /// The search is recursive. /// /// The folder to look in /// A list of the full filenames public static List EnumerateFiles(string basepath) { return EnumerateFiles(basepath, null); } /// /// Returns a list of folder names found in the given folder. /// The search is recursive. /// /// The folder to look in /// A list of the full paths public static List EnumerateFolders(string basepath) { return EnumerateFolders(basepath, null); } /// /// Returns a list of all files found in the given folder. /// The search is recursive. /// /// The folder to look in /// An optional filter to apply to the filenames /// A list of the full filenames public static List EnumerateFiles(string basepath, FilenameFilter filter) { List files = new List(); if (!System.IO.Directory.Exists(basepath)) return files; Queue lst = new Queue(); lst.Enqueue(basepath); while (lst.Count > 0) { string f = lst.Dequeue(); foreach (string s in System.IO.Directory.GetDirectories(f)) if (filter == null || filter.ShouldInclude(basepath, AppendDirSeperator(s))) lst.Enqueue(s); foreach (string s in System.IO.Directory.GetFiles(f)) if (filter == null || filter.ShouldInclude(basepath, s)) files.Add(s); } return files; } /// /// Returns a list of all files and subfolders found in the given folder. /// The search is recursive. /// /// The folder to look in. /// A list of the full filenames and foldernames. Foldernames ends with the directoryseperator char public static List EnumerateFileSystemEntries(string basepath) { return EnumerateFileSystemEntries(basepath, null); } /// /// Returns a list of all files and subfolders found in the given folder. /// The search is recursive. /// /// The folder to look in. /// An optional filter to apply to the filenames /// A list of the full filenames and foldernames. Foldernames ends with the directoryseperator char public static List EnumerateFileSystemEntries(string basepath, FilenameFilter filter) { List entries = new List(); if (!System.IO.Directory.Exists(basepath)) return entries; Queue lst = new Queue(); lst.Enqueue(basepath); while (lst.Count > 0) { string f = lst.Dequeue(); foreach (string s in System.IO.Directory.GetDirectories(f)) { string folder = AppendDirSeperator(s); if (filter == null || filter.ShouldInclude(basepath, folder)) { lst.Enqueue(folder); entries.Add(folder); } } foreach (string s in System.IO.Directory.GetFiles(f)) if (filter == null || filter.ShouldInclude(basepath, s)) entries.Add(s); } return entries; } /// /// Returns a list of folder names found in the given folder. /// The search is recursive. /// /// The folder to look in /// An optional filter to apply to the folder names /// A list of the full paths public static List EnumerateFolders(string basepath, FilenameFilter filter) { List folders = new List(); if (!System.IO.Directory.Exists(basepath)) return folders; Queue lst = new Queue(); lst.Enqueue(basepath); while (lst.Count > 0) { string f = lst.Dequeue(); foreach (string s in System.IO.Directory.GetDirectories(f)) { if (filter == null || filter.ShouldInclude(basepath, Core.Utility.AppendDirSeperator(s))) { folders.Add(s); lst.Enqueue(s); } } } return folders; } /// /// Appends the appropriate directory seperator to paths, depending on OS. /// Does not append the seperator if the path already ends with it. /// /// The path to append to /// The path with the directory seperator appended public static string AppendDirSeperator(string path) { if (!path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) return path += System.IO.Path.DirectorySeparatorChar; else return path; } /// /// Some streams can return a number that is less than the requested number of bytes. /// This is usually due to fragmentation, and is solved by issuing a new read. /// This function wraps that functionality. /// /// The stream to read /// The buffer to read into /// The amout of bytes to read /// The actual number of bytes read public static int ForceStreamRead(System.IO.Stream stream, byte[] buf, int count) { int a; int index = 0; do { a = stream.Read(buf, index, count); index += a; count -= a; } while (a != 0 && count > 0); return index; } /// /// Compares two streams to see if they are binary equals /// /// One stream /// Another stream /// True if the length of the two streams should be compared /// True if they are equal, false otherwise public static bool CompareStreams(System.IO.Stream stream1, System.IO.Stream stream2, bool checkLength) { if (checkLength) { try { if (stream1.Length != stream2.Length) return false; } catch { //We must read along, trying to determine if they are equals } } int longSize = BitConverter.GetBytes((long)0).Length; byte[] buf1 = new byte[longSize * 512]; byte[] buf2 = new byte[buf1.Length]; int a1, a2; while ((a1 = ForceStreamRead(stream1, buf1, buf1.Length)) == (a2 = ForceStreamRead(stream2, buf2, buf2.Length))) { int ix = 0; for (int i = 0; i < a1 / longSize; i++) if (BitConverter.ToUInt64(buf1, ix) != BitConverter.ToUInt64(buf2, ix)) return false; else ix += longSize; for (int i = 0; i < a1 % longSize; i++) if (buf1[ix] != buf2[ix]) return false; else ix++; if (a1 == 0) break; } return a1 == a2; } /// /// Removes an entire folder, and its contents. /// Equal to System.IO.Directory.Delete /// /// The folder to remove public static void DeleteFolder(string path) { if (!System.IO.Directory.Exists(path)) return; foreach (string s in EnumerateFiles(path)) { System.IO.File.SetAttributes(s, System.IO.FileAttributes.Normal); System.IO.File.Delete(s); } List folders = Utility.EnumerateFolders(path); folders.Sort(); folders.Reverse(); foreach (string s in folders) System.IO.Directory.Delete(s); System.IO.Directory.Delete(path); } /// /// Calculates the SHA256 hash of a given file, and returns the results as an base64 encoded string /// /// The path to the file to calculate the hash for /// The base64 encoded SHA256 hash public static string CalculateHash(string path) { System.Security.Cryptography.HashAlgorithm sha = System.Security.Cryptography.HashAlgorithm.Create("SHA256"); using (System.IO.FileStream fs = System.IO.File.Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) return Convert.ToBase64String(sha.ComputeHash(fs)); } /// /// Formats a size into a human readable format, eg. 2048 becomes "2 KB". /// /// The size to format /// A human readable string representing the size public static string FormatSizeString(long size) { if (size > 1024 * 1024 * 1024) return string.Format("{0:N} GB", (double)size / (1024 * 1024 * 1024)); else if (size > 1024 * 1024) return string.Format("{0:N} MB", (double)size / (1024 * 1024)); else if (size > 1024) return string.Format("{0:N} KB", (double)size / 1024); else return string.Format("{0} bytes", size); } } }