2 // Mono.Unix/UnixPath.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004-2006 Jonathan Pryor
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
36 public sealed class UnixPath
38 private UnixPath () {}
40 public static readonly char DirectorySeparatorChar = '/';
41 public static readonly char AltDirectorySeparatorChar = '/';
42 [Obsolete ("Use GetInvalidPathChars()")]
43 public static readonly char[] InvalidPathChars = new char[]{};
44 public static readonly char PathSeparator = ':';
45 public static readonly char VolumeSeparatorChar = '/';
47 private static readonly char[] _InvalidPathChars = new char[]{};
49 public static char[] GetInvalidPathChars ()
51 return (char[]) _InvalidPathChars.Clone ();
54 public static string Combine (string path1, params string[] paths)
57 throw new ArgumentNullException ("path1");
59 throw new ArgumentNullException ("paths");
60 if (path1.IndexOfAny (_InvalidPathChars) != -1)
61 throw new ArgumentException ("Illegal characters in path", "path1");
63 int len = path1.Length + 1;
64 for (int i = 0; i < paths.Length; ++i) {
65 if (paths [i] == null)
66 throw new ArgumentNullException ("paths");
67 len += paths [i].Length + 1;
70 StringBuilder sb = new StringBuilder (len);
72 for (int i = 0; i < paths.Length; ++i)
73 Combine (sb, paths [i]);
74 return sb.ToString ();
77 private static void Combine (StringBuilder path, string part)
79 if (part.IndexOfAny (_InvalidPathChars) != -1)
80 throw new ArgumentException ("Illegal characters in path", "path1");
81 char end = path [path.Length-1];
82 if (end != DirectorySeparatorChar &&
83 end != AltDirectorySeparatorChar &&
84 end != VolumeSeparatorChar)
85 path.Append (DirectorySeparatorChar);
89 public static string GetDirectoryName (string path)
93 int lastDir = path.LastIndexOf (DirectorySeparatorChar);
95 return path.Substring (0, lastDir);
99 public static string GetFileName (string path)
101 if (path == null || path.Length == 0)
104 int lastDir = path.LastIndexOf (DirectorySeparatorChar);
106 return path.Substring (lastDir+1);
111 public static string GetFullPath (string path)
113 path = _GetFullPath (path);
114 return GetCanonicalPath (path);
117 private static string _GetFullPath (string path)
120 throw new ArgumentNullException ("path");
121 if (!IsPathRooted (path))
122 path = UnixDirectoryInfo.GetCurrentDirectory() + DirectorySeparatorChar + path;
127 public static string GetCanonicalPath (string path)
131 GetPathComponents (path, out dirs, out lastIndex);
132 string end = string.Join ("/", dirs, 0, lastIndex);
133 return IsPathRooted (path) ? "/" + end : end;
136 private static void GetPathComponents (string path,
137 out string[] components, out int lastIndex)
139 string [] dirs = path.Split (DirectorySeparatorChar);
141 for (int i = 0; i < dirs.Length; ++i) {
142 if (dirs [i] == "." || dirs [i] == string.Empty) continue;
143 else if (dirs [i] == "..") {
144 if (target != 0) --target;
148 dirs [target++] = dirs [i];
154 public static string GetPathRoot (string path)
158 if (!IsPathRooted (path))
163 public static string GetCompleteRealPath (string path)
166 throw new ArgumentNullException ("path");
169 GetPathComponents (path, out dirs, out lastIndex);
170 StringBuilder realPath = new StringBuilder ();
171 if (dirs.Length > 0) {
172 string dir = IsPathRooted (path) ? "/" : "";
174 realPath.Append (GetRealPath (dir));
176 for (int i = 1; i < lastIndex; ++i) {
177 realPath.Append ("/").Append (dirs [i]);
178 string p = GetRealPath (realPath.ToString());
179 realPath.Remove (0, realPath.Length);
182 return realPath.ToString ();
185 public static string GetRealPath (string path)
188 string name = ReadSymbolicLink (path);
191 if (IsPathRooted (name))
194 path = GetDirectoryName (path) + DirectorySeparatorChar + name;
195 path = GetCanonicalPath (path);
200 // Read the specified symbolic link. If the file isn't a symbolic link,
201 // return null; otherwise, return the contents of the symbolic link.
203 // readlink(2) is horribly evil, as there is no way to query how big the
204 // symlink contents are. Consequently, it's trial and error...
205 internal static string ReadSymbolicLink (string path)
207 StringBuilder buf = new StringBuilder (256);
209 int r = Native.Syscall.readlink (path, buf);
212 switch (e = Native.Stdlib.GetLastError()) {
213 case Native.Errno.EINVAL:
214 // path isn't a symbolic link
217 UnixMarshal.ThrowExceptionForError (e);
221 else if (r == buf.Capacity) {
225 return buf.ToString (0, r);
229 public static bool IsPathRooted (string path)
231 if (path == null || path.Length == 0)
233 return path [0] == DirectorySeparatorChar;
236 internal static void CheckPath (string path)
239 throw new ArgumentNullException ();
240 if (path.Length == 0)
241 throw new ArgumentException ("Path cannot contain a zero-length string", "path");
242 if (path.IndexOfAny (_InvalidPathChars) != -1)
243 throw new ArgumentException ("Invalid characters in path.", "path");