// **************************************************************** // Copyright 2002-2003, Charlie Poole // This is free software licensed under the NUnit license. You may // obtain a copy of the license at http://nunit.org/?p=license&r=2.4 // **************************************************************** using System; using System.IO; using System.Text; using System.Reflection; using System.Collections; using System.Runtime.InteropServices; namespace NUnit.Util { /// /// Static methods for manipulating project paths, including both directories /// and files. Some synonyms for System.Path methods are included as well. /// public class PathUtils { public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; public const int MAX_PATH = 256; protected static char DirectorySeparatorChar = Path.DirectorySeparatorChar; protected static char AltDirectorySeparatorChar = Path.AltDirectorySeparatorChar; #region Public methods public static bool IsAssemblyFileType( string path ) { string extension = Path.GetExtension( path ).ToLower(); return extension == ".dll" || extension == ".exe"; } /// /// Returns the relative path from a base directory to another /// directory or file. /// public static string RelativePath( string from, string to ) { if (from == null) throw new ArgumentNullException (from); if (to == null) throw new ArgumentNullException (to); if (!Path.IsPathRooted (to)) return to; if (Path.GetPathRoot (from) != Path.GetPathRoot (to)) return null; string[] _from = from.Split (PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar); string[] _to = to.Split (PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar); StringBuilder sb = new StringBuilder (Math.Max (from.Length, to.Length)); int last_common, min = Math.Min (_from.Length, _to.Length); for (last_common = 0; last_common < min; ++last_common) { if (!_from [last_common].Equals (_to [last_common])) break; } if (last_common < _from.Length) sb.Append (".."); for (int i = last_common + 1; i < _from.Length; ++i) { sb.Append (PathUtils.DirectorySeparatorChar).Append (".."); } if (sb.Length > 0) sb.Append (PathUtils.DirectorySeparatorChar); if (last_common < _to.Length) sb.Append (_to [last_common]); for (int i = last_common + 1; i < _to.Length; ++i) { sb.Append (PathUtils.DirectorySeparatorChar).Append (_to [i]); } return sb.ToString (); } /// /// Return the canonical form of a path. /// public static string Canonicalize( string path ) { ArrayList parts = new ArrayList( path.Split( DirectorySeparatorChar, AltDirectorySeparatorChar ) ); for( int index = 0; index < parts.Count; ) { string part = (string)parts[index]; switch( part ) { case ".": parts.RemoveAt( index ); break; case "..": parts.RemoveAt( index ); if ( index > 0 ) parts.RemoveAt( --index ); break; default: index++; break; } } return String.Join( DirectorySeparatorChar.ToString(), (string[])parts.ToArray( typeof( string ) ) ); } /// /// True if the two paths are the same. However, two paths /// to the same file or directory using different network /// shares or drive letters are not treated as equal. /// public static bool SamePath( string path1, string path2 ) { return string.Compare( Canonicalize(path1), Canonicalize(path2), PathUtils.IsWindows() ) == 0; } /// /// True if the two paths are the same or if the second is /// directly or indirectly under the first. Note that paths /// using different network shares or drive letters are /// considered unrelated, even if they end up referencing /// the same subtrees in the file system. /// public static bool SamePathOrUnder( string path1, string path2 ) { path1 = Canonicalize( path1 ); path2 = Canonicalize( path2 ); int length1 = path1.Length; int length2 = path2.Length; // if path1 is longer, then path2 can't be under it if ( length1 > length2 ) return false; // if lengths are the same, check for equality if ( length1 == length2 ) //return path1.ToLower() == path2.ToLower(); return string.Compare( path1, path2, IsWindows() ) == 0; // path 2 is longer than path 1: see if initial parts match //if ( path1.ToLower() != path2.Substring( 0, length1 ).ToLower() ) if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 ) return false; // must match through or up to a directory separator boundary return path2[length1-1] == DirectorySeparatorChar || path2[length1] == DirectorySeparatorChar; } public static string Combine( string path1, params string[] morePaths ) { string result = path1; foreach( string path in morePaths ) result = Path.Combine( result, path ); return result; } // TODO: This logic should be in shared source public static string GetAssemblyPath( Assembly assembly ) { string uri = assembly.CodeBase; // If it wasn't loaded locally, use the Location if ( !uri.StartsWith( Uri.UriSchemeFile ) ) return assembly.Location; return GetAssemblyPathFromFileUri( uri ); } // Separate method for testability public static string GetAssemblyPathFromFileUri( string uri ) { // Skip over the file:// int start = Uri.UriSchemeFile.Length + Uri.SchemeDelimiter.Length; if ( PathUtils.DirectorySeparatorChar == '\\' ) { if ( uri[start] == '/' && uri[start+2] == ':' ) ++start; } else { if ( uri[start] != '/' ) --start; } return uri.Substring( start ); } #endregion #region Helper Methods private static bool IsWindows() { return PathUtils.DirectorySeparatorChar == '\\'; } #endregion } }