1 // **************************************************************** 2 // Copyright 2002-2003, Charlie Poole 3 // This is free software licensed under the NUnit license. You may 4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4 5 // **************************************************************** 6 7 using System; 8 using System.IO; 9 using System.Text; 10 using System.Reflection; 11 using System.Collections; 12 using System.Runtime.InteropServices; 13 14 namespace NUnit.Util 15 { 16 /// <summary> 17 /// Static methods for manipulating project paths, including both directories 18 /// and files. Some synonyms for System.Path methods are included as well. 19 /// </summary> 20 public class PathUtils 21 { 22 public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; 23 public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; 24 public const int MAX_PATH = 256; 25 26 protected static char DirectorySeparatorChar = Path.DirectorySeparatorChar; 27 protected static char AltDirectorySeparatorChar = Path.AltDirectorySeparatorChar; 28 29 #region Public methods 30 IsAssemblyFileType( string path )31 public static bool IsAssemblyFileType( string path ) 32 { 33 string extension = Path.GetExtension( path ).ToLower(); 34 return extension == ".dll" || extension == ".exe"; 35 } 36 37 /// <summary> 38 /// Returns the relative path from a base directory to another 39 /// directory or file. 40 /// </summary> RelativePath( string from, string to )41 public static string RelativePath( string from, string to ) 42 { 43 if (from == null) 44 throw new ArgumentNullException (from); 45 if (to == null) 46 throw new ArgumentNullException (to); 47 if (!Path.IsPathRooted (to)) 48 return to; 49 if (Path.GetPathRoot (from) != Path.GetPathRoot (to)) 50 return null; 51 52 string[] _from = from.Split (PathUtils.DirectorySeparatorChar, 53 PathUtils.AltDirectorySeparatorChar); 54 string[] _to = to.Split (PathUtils.DirectorySeparatorChar, 55 PathUtils.AltDirectorySeparatorChar); 56 57 StringBuilder sb = new StringBuilder (Math.Max (from.Length, to.Length)); 58 59 int last_common, min = Math.Min (_from.Length, _to.Length); 60 for (last_common = 0; last_common < min; ++last_common) 61 { 62 if (!_from [last_common].Equals (_to [last_common])) 63 break; 64 } 65 66 if (last_common < _from.Length) 67 sb.Append (".."); 68 for (int i = last_common + 1; i < _from.Length; ++i) 69 { 70 sb.Append (PathUtils.DirectorySeparatorChar).Append (".."); 71 } 72 73 if (sb.Length > 0) 74 sb.Append (PathUtils.DirectorySeparatorChar); 75 if (last_common < _to.Length) 76 sb.Append (_to [last_common]); 77 for (int i = last_common + 1; i < _to.Length; ++i) 78 { 79 sb.Append (PathUtils.DirectorySeparatorChar).Append (_to [i]); 80 } 81 82 return sb.ToString (); 83 } 84 85 /// <summary> 86 /// Return the canonical form of a path. 87 /// </summary> Canonicalize( string path )88 public static string Canonicalize( string path ) 89 { 90 ArrayList parts = new ArrayList( 91 path.Split( DirectorySeparatorChar, AltDirectorySeparatorChar ) ); 92 93 for( int index = 0; index < parts.Count; ) 94 { 95 string part = (string)parts[index]; 96 97 switch( part ) 98 { 99 case ".": 100 parts.RemoveAt( index ); 101 break; 102 103 case "..": 104 parts.RemoveAt( index ); 105 if ( index > 0 ) 106 parts.RemoveAt( --index ); 107 break; 108 default: 109 index++; 110 break; 111 } 112 } 113 114 return String.Join( DirectorySeparatorChar.ToString(), (string[])parts.ToArray( typeof( string ) ) ); 115 } 116 117 /// <summary> 118 /// True if the two paths are the same. However, two paths 119 /// to the same file or directory using different network 120 /// shares or drive letters are not treated as equal. 121 /// </summary> SamePath( string path1, string path2 )122 public static bool SamePath( string path1, string path2 ) 123 { 124 return string.Compare( Canonicalize(path1), Canonicalize(path2), PathUtils.IsWindows() ) == 0; 125 } 126 127 /// <summary> 128 /// True if the two paths are the same or if the second is 129 /// directly or indirectly under the first. Note that paths 130 /// using different network shares or drive letters are 131 /// considered unrelated, even if they end up referencing 132 /// the same subtrees in the file system. 133 /// </summary> SamePathOrUnder( string path1, string path2 )134 public static bool SamePathOrUnder( string path1, string path2 ) 135 { 136 path1 = Canonicalize( path1 ); 137 path2 = Canonicalize( path2 ); 138 139 int length1 = path1.Length; 140 int length2 = path2.Length; 141 142 // if path1 is longer, then path2 can't be under it 143 if ( length1 > length2 ) 144 return false; 145 146 // if lengths are the same, check for equality 147 if ( length1 == length2 ) 148 //return path1.ToLower() == path2.ToLower(); 149 return string.Compare( path1, path2, IsWindows() ) == 0; 150 151 // path 2 is longer than path 1: see if initial parts match 152 //if ( path1.ToLower() != path2.Substring( 0, length1 ).ToLower() ) 153 if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 ) 154 return false; 155 156 // must match through or up to a directory separator boundary 157 return path2[length1-1] == DirectorySeparatorChar || 158 path2[length1] == DirectorySeparatorChar; 159 } 160 Combine( string path1, params string[] morePaths )161 public static string Combine( string path1, params string[] morePaths ) 162 { 163 string result = path1; 164 foreach( string path in morePaths ) 165 result = Path.Combine( result, path ); 166 return result; 167 } 168 169 // TODO: This logic should be in shared source GetAssemblyPath( Assembly assembly )170 public static string GetAssemblyPath( Assembly assembly ) 171 { 172 string uri = assembly.CodeBase; 173 174 // If it wasn't loaded locally, use the Location 175 if ( !uri.StartsWith( Uri.UriSchemeFile ) ) 176 return assembly.Location; 177 178 return GetAssemblyPathFromFileUri( uri ); 179 } 180 181 // Separate method for testability GetAssemblyPathFromFileUri( string uri )182 public static string GetAssemblyPathFromFileUri( string uri ) 183 { 184 // Skip over the file:// 185 int start = Uri.UriSchemeFile.Length + Uri.SchemeDelimiter.Length; 186 187 if ( PathUtils.DirectorySeparatorChar == '\\' ) 188 { 189 if ( uri[start] == '/' && uri[start+2] == ':' ) 190 ++start; 191 } 192 else 193 { 194 if ( uri[start] != '/' ) 195 --start; 196 } 197 198 return uri.Substring( start ); 199 } 200 #endregion 201 202 #region Helper Methods IsWindows()203 private static bool IsWindows() 204 { 205 return PathUtils.DirectorySeparatorChar == '\\'; 206 } 207 #endregion 208 } 209 } 210