1 //------------------------------------------------------------------------------
5 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
6 // Copyright (C) 2002 Ximian, Inc. (http://www.ximian.com)
7 // Copyright (C) 2003 Ben Maurer
9 // Author: Jim Richardson, develop@wtfo-guru.com
10 // Dan Lewis (dihlewis@yahoo.co.uk)
11 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
12 // Ben Maurer (bmaurer@users.sourceforge.net)
13 // Created: Saturday, August 11, 2001
15 //------------------------------------------------------------------------------
18 using System.Runtime.CompilerServices;
22 public sealed class Path
24 public static readonly char AltDirectorySeparatorChar;
25 public static readonly char DirectorySeparatorChar;
26 public static readonly char[] InvalidPathChars;
27 public static readonly char PathSeparator;
28 internal static readonly string DirectorySeparatorStr;
29 public static readonly char VolumeSeparatorChar;
31 private static readonly char[] PathSeparatorChars;
32 private static bool dirEqualsVolume;
37 public static string ChangeExtension (string path, string extension)
44 if (path.IndexOfAny (InvalidPathChars) != -1)
45 throw new ArgumentException ("Illegal characters in path", "path");
47 int iExt = findExtension (path);
49 if (extension != null && path.Length != 0) {
50 if (extension [0] != '.')
51 extension = "." + extension;
53 extension = String.Empty;
56 return path + extension;
57 } else if (iExt > 0) {
58 string temp = path.Substring (0, iExt);
59 return temp + extension;
65 public static string Combine (string path1, string path2)
68 throw new ArgumentNullException ("path1");
71 throw new ArgumentNullException ("path2");
73 if (path1 == String.Empty)
76 if (path2 == String.Empty)
79 if (path1.IndexOfAny (InvalidPathChars) != -1)
80 throw new ArgumentException ("Illegal characters in path", "path1");
82 if (path2.IndexOfAny (InvalidPathChars) != -1)
83 throw new ArgumentException ("Illegal characters in path", "path2");
86 // LAMESPEC: MS says that if path1 is not empty and path2 is a full path
87 // it should throw ArgumentException
88 if (IsPathRooted (path2))
91 if (Array.IndexOf (PathSeparatorChars, path1 [path1.Length - 1]) == -1)
92 return path1 + DirectorySeparatorChar + path2;
97 public static string GetDirectoryName (string path)
99 // LAMESPEC: For empty string MS docs say both
100 // return null AND throw exception. Seems .NET throws.
101 if (path == String.Empty)
102 throw new ArgumentException();
104 if (path == null || GetPathRoot (path) == path)
107 CheckArgument.WhitespaceOnly (path);
108 CheckArgument.PathChars (path);
110 int nLast = path.LastIndexOfAny (PathSeparatorChars);
115 return path.Substring (0, nLast);
120 public static string GetExtension (string path)
125 if (path.IndexOfAny (InvalidPathChars) != -1)
126 throw new ArgumentException ("Illegal characters in path", "path");
128 int iExt = findExtension (path);
131 { // okay it has an extension
132 return path.Substring (iExt);
137 public static string GetFileName (string path)
139 if (path == null || path == String.Empty)
142 if (path.IndexOfAny (InvalidPathChars) != -1)
143 throw new ArgumentException ("Illegal characters in path", "path");
145 int nLast = path.LastIndexOfAny (PathSeparatorChars);
147 return path.Substring (nLast + 1);
152 public static string GetFileNameWithoutExtension (string path)
154 return ChangeExtension (GetFileName (path), null);
157 public static string GetFullPath (string path)
160 throw (new ArgumentNullException (
162 "You must specify a path when calling System.IO.Path.GetFullPath"));
164 if (path.Trim () == String.Empty)
165 throw new ArgumentException ("The path is not of a legal form", "path");
167 if (!IsPathRooted (path))
168 path = Directory.GetCurrentDirectory () + DirectorySeparatorStr + path;
170 return CanonicalizePath (path);
173 static bool IsDsc (char c) {
174 return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
177 public static string GetPathRoot (string path)
179 if (path == null) return null;
180 if (!IsPathRooted (path)) return String.Empty;
182 if (DirectorySeparatorChar == '/') {
184 return IsDsc (path [0]) ? DirectorySeparatorChar.ToString () : String.Empty;
189 if (path.Length <= 2) return String.Empty;
191 if (IsDsc (path [0]) && IsDsc (path[1])) {
192 // UNC: \\server or \\server\share
194 while (len < path.Length && !IsDsc (path [len])) len++;
196 while (len < path.Length && !IsDsc (path [len])) len++;
197 } else if (path[1] == VolumeSeparatorChar) {
199 if (path.Length >= 3 && (IsDsc (path [2]))) len++;
202 return path.Substring (0, len);
206 public static string GetTempFileName ()
217 path = GetTempPath() + DirectorySeparatorChar + "tmp" + num.ToString("x");
220 f = new FileStream (path, FileMode.CreateNew);
230 /// Returns the path of the current systems temp directory
232 public static string GetTempPath ()
234 return get_temp_path ();
237 [MethodImplAttribute(MethodImplOptions.InternalCall)]
238 private static extern string get_temp_path ();
240 public static bool HasExtension (string path)
242 CheckArgument.Null (path);
243 CheckArgument.Empty (path);
244 CheckArgument.WhitespaceOnly (path);
246 return findExtension (path) > -1;
249 public static bool IsPathRooted (string path)
251 if (path == null || path.Length == 0)
254 if (path.IndexOfAny (InvalidPathChars) != -1)
255 throw new ArgumentException ("Illegal characters in path", "path");
258 return (c == DirectorySeparatorChar ||
259 c == AltDirectorySeparatorChar ||
260 (!dirEqualsVolume && path.Length > 1 && path [1] == VolumeSeparatorChar));
263 // private class methods
265 private static int findExtension (string path)
267 // method should return the index of the path extension
268 // start or -1 if no valid extension
270 int iLastDot = path.LastIndexOf (".");
271 int iLastSep = path.LastIndexOfAny ( PathSeparatorChars );
273 if (iLastDot > iLastSep)
280 VolumeSeparatorChar = MonoIO.VolumeSeparatorChar;
281 DirectorySeparatorChar = MonoIO.DirectorySeparatorChar;
282 AltDirectorySeparatorChar = MonoIO.AltDirectorySeparatorChar;
284 PathSeparator = MonoIO.PathSeparator;
285 InvalidPathChars = MonoIO.InvalidPathChars;
289 DirectorySeparatorStr = DirectorySeparatorChar.ToString ();
290 PathSeparatorChars = new char [] {
291 DirectorySeparatorChar,
292 AltDirectorySeparatorChar,
296 dirEqualsVolume = (DirectorySeparatorChar == VolumeSeparatorChar);
300 static string CanonicalizePath (string path) {
302 // STEP 1: Check for empty string
303 if (path == null) return path;
305 if (path == String.Empty) return path;
307 // STEP 2: Check to see if this is only a root
308 string root = GetPathRoot (path);
309 if (root == path) return path;
311 // STEP 3: split the directories, this gets rid of consecutative "/"'s
312 string [] dirs = path.Split (DirectorySeparatorChar, AltDirectorySeparatorChar);
313 // STEP 4: Get rid of directories containing . and ..
316 for (int i = 0; i < dirs.Length; i++) {
317 if (dirs [i] == "." || (i != 0 && dirs [i] == String.Empty)) continue;
318 else if (dirs [i] == "..") {
319 if (target != 0) target--;
322 dirs [target++] = dirs [i];
325 // STEP 5: Combine everything.
329 return String.Join (DirectorySeparatorStr, dirs, 0, target);