// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace DiscUtils.Internal { internal static class Utilities { /// /// Converts between two arrays. /// /// The type of the elements of the source array. /// The type of the elements of the destination array. /// The source array. /// The function to map from source type to destination type. /// The resultant array. public static U[] Map(ICollection source, Func func) { U[] result = new U[source.Count]; int i = 0; foreach (T sVal in source) { result[i++] = func(sVal); } return result; } /// /// Converts between two arrays. /// /// The type of the elements of the source array. /// The type of the elements of the destination array. /// The source array. /// The function to map from source type to destination type. /// The resultant array. public static U[] Map(IEnumerable source, Func func) { List result = new List(); foreach (T sVal in source) { result.Add(func(sVal)); } return result.ToArray(); } /// /// Filters a collection into a new collection. /// /// The type of the new collection. /// The type of the collection entries. /// The collection to filter. /// The predicate to select which entries are carried over. /// The new collection, containing all entries where the predicate returns true. public static C Filter(ICollection source, Func predicate) where C : ICollection, new() { C result = new C(); foreach (T val in source) { if (predicate(val)) { result.Add(val); } } return result; } /// /// Indicates if two ranges overlap. /// /// The type of the ordinals. /// The lowest ordinal of the first range (inclusive). /// The highest ordinal of the first range (exclusive). /// The lowest ordinal of the second range (inclusive). /// The highest ordinal of the second range (exclusive). /// true if the ranges overlap, else false. public static bool RangesOverlap(T xFirst, T xLast, T yFirst, T yLast) where T : IComparable { return !((xLast.CompareTo(yFirst) <= 0) || (xFirst.CompareTo(yLast) >= 0)); } #region Bit Twiddling public static bool IsAllZeros(byte[] buffer, int offset, int count) { int end = offset + count; for (int i = offset; i < end; ++i) { if (buffer[i] != 0) { return false; } } return true; } public static bool IsPowerOfTwo(uint val) { if (val == 0) { return false; } while ((val & 1) != 1) { val >>= 1; } return val == 1; } public static bool IsPowerOfTwo(long val) { if (val == 0) { return false; } while ((val & 1) != 1) { val >>= 1; } return val == 1; } public static bool AreEqual(byte[] a, byte[] b) { if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; ++i) { if (a[i] != b[i]) { return false; } } return true; } public static ushort BitSwap(ushort value) { return (ushort)(((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8)); } public static uint BitSwap(uint value) { return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24); } public static ulong BitSwap(ulong value) { return ((ulong)BitSwap((uint)(value & 0xFFFFFFFF)) << 32) | BitSwap((uint)(value >> 32)); } public static short BitSwap(short value) { return (short)BitSwap((ushort)value); } public static int BitSwap(int value) { return (int)BitSwap((uint)value); } public static long BitSwap(long value) { return (long)BitSwap((ulong)value); } #endregion #region Path Manipulation /// /// Extracts the directory part of a path. /// /// The path to process. /// The directory part. public static string GetDirectoryFromPath(string path) { string trimmed = path.TrimEnd('\\'); int index = trimmed.LastIndexOf('\\'); if (index < 0) { return string.Empty; // No directory, just a file name } return trimmed.Substring(0, index); } /// /// Extracts the file part of a path. /// /// The path to process. /// The file part of the path. public static string GetFileFromPath(string path) { string trimmed = path.Trim('\\'); int index = trimmed.LastIndexOf('\\'); if (index < 0) { return trimmed; // No directory, just a file name } return trimmed.Substring(index + 1); } /// /// Combines two paths. /// /// The first part of the path. /// The second part of the path. /// The combined path. public static string CombinePaths(string a, string b) { if (string.IsNullOrEmpty(a) || (b.Length > 0 && b[0] == '\\')) { return b; } if (string.IsNullOrEmpty(b)) { return a; } return a.TrimEnd('\\') + '\\' + b.TrimStart('\\'); } /// /// Resolves a relative path into an absolute one. /// /// The base path to resolve from. /// The relative path. /// The absolute path. If no is specified /// then relativePath is returned as-is. If /// contains more '..' characters than the base path contains levels of /// directory, the resultant string be the root drive followed by the file name. /// If no the basePath starts with '\' (no drive specified) then the returned /// path will also start with '\'. /// For example: (\TEMP\Foo.txt, ..\..\Bar.txt) gives (\Bar.txt). /// public static string ResolveRelativePath(string basePath, string relativePath) { if (string.IsNullOrEmpty(basePath)) { return relativePath; } if (!basePath.EndsWith(@"\")) basePath = Path.GetDirectoryName(basePath); string merged = Path.GetFullPath(Path.Combine(basePath, relativePath)); if (basePath.StartsWith(@"\") && merged.Length > 2 && merged[1].Equals(':')) { return merged.Substring(2); } return merged; } public static string ResolvePath(string basePath, string path) { if (!path.StartsWith("\\", StringComparison.OrdinalIgnoreCase)) { return ResolveRelativePath(basePath, path); } return path; } public static string MakeRelativePath(string path, string basePath) { List pathElements = new List(path.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)); List basePathElements = new List(basePath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)); if (!basePath.EndsWith("\\", StringComparison.Ordinal) && basePathElements.Count > 0) { basePathElements.RemoveAt(basePathElements.Count - 1); } // Find first part of paths that don't match int i = 0; while (i < Math.Min(pathElements.Count - 1, basePathElements.Count)) { if (pathElements[i].ToUpperInvariant() != basePathElements[i].ToUpperInvariant()) { break; } ++i; } // For each remaining part of the base path, insert '..' StringBuilder result = new StringBuilder(); if (i == basePathElements.Count) { result.Append(@".\"); } else if (i < basePathElements.Count) { for (int j = 0; j < basePathElements.Count - i; ++j) { result.Append(@"..\"); } } // For each remaining part of the path, add the path element for (int j = i; j < pathElements.Count - 1; ++j) { result.Append(pathElements[j]); result.Append(@"\"); } result.Append(pathElements[pathElements.Count - 1]); // If the target was a directory, put the terminator back if (path.EndsWith(@"\", StringComparison.Ordinal)) { result.Append(@"\"); } return result.ToString(); } #endregion #region Filesystem Support /// /// Indicates if a file name matches the 8.3 pattern. /// /// The name to test. /// true if the name is 8.3, otherwise false. public static bool Is8Dot3(string name) { if (name.Length > 12) { return false; } string[] split = name.Split('.'); if (split.Length > 2 || split.Length < 1) { return false; } if (split[0].Length > 8) { return false; } foreach (char ch in split[0]) { if (!Is8Dot3Char(ch)) { return false; } } if (split.Length > 1) { if (split[1].Length > 3) { return false; } foreach (char ch in split[1]) { if (!Is8Dot3Char(ch)) { return false; } } } return true; } public static bool Is8Dot3Char(char ch) { return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || "_^$~!#%£-{}()@'`&".IndexOf(ch) != -1; } /// /// Converts a 'standard' wildcard file/path specification into a regular expression. /// /// The wildcard pattern to convert. /// The resultant regular expression. /// /// The wildcard * (star) matches zero or more characters (including '.'), and ? /// (question mark) matches precisely one character (except '.'). /// public static Regex ConvertWildcardsToRegEx(string pattern) { if (!pattern.Contains(".")) { pattern += "."; } string query = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", "[^.]") + "$"; return new Regex(query, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } public static FileAttributes FileAttributesFromUnixFileType(UnixFileType fileType) { switch (fileType) { case UnixFileType.Fifo: return FileAttributes.Device | FileAttributes.System; case UnixFileType.Character: return FileAttributes.Device | FileAttributes.System; case UnixFileType.Directory: return FileAttributes.Directory; case UnixFileType.Block: return FileAttributes.Device | FileAttributes.System; case UnixFileType.Regular: return FileAttributes.Normal; case UnixFileType.Link: return FileAttributes.ReparsePoint; case UnixFileType.Socket: return FileAttributes.Device | FileAttributes.System; default: return 0; } } #endregion } }