2 // Mono.Unix/UnixPath.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004 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 public static readonly char[] InvalidPathChars = new char[]{'\0'};
43 public static readonly char PathSeparator = ':';
44 public static readonly char VolumeSeparatorChar = '/';
46 public static string Combine (string path1, params string[] paths)
49 throw new ArgumentNullException ("path1");
51 throw new ArgumentNullException ("paths");
52 if (path1.IndexOfAny (InvalidPathChars) != -1)
53 throw new ArgumentException ("Illegal characters in path", "path1");
55 int len = path1.Length + 1;
56 for (int i = 0; i < paths.Length; ++i) {
57 if (paths [i] == null)
58 throw new ArgumentNullException ("paths");
59 len += paths [i].Length + 1;
62 StringBuilder sb = new StringBuilder (len);
64 for (int i = 0; i < paths.Length; ++i)
65 Combine (sb, paths [i]);
66 return sb.ToString ();
69 private static void Combine (StringBuilder path, string part)
71 if (part.IndexOfAny (InvalidPathChars) != -1)
72 throw new ArgumentException ("Illegal characters in path", "path1");
73 char end = path [path.Length-1];
74 if (end != DirectorySeparatorChar &&
75 end != AltDirectorySeparatorChar &&
76 end != VolumeSeparatorChar)
77 path.Append (DirectorySeparatorChar);
81 public static string GetDirectoryName (string path)
85 int lastDir = path.LastIndexOf (DirectorySeparatorChar);
87 return path.Substring (0, lastDir);
91 public static string GetFileName (string path)
93 if (path == null || path.Length == 0)
96 int lastDir = path.LastIndexOf (DirectorySeparatorChar);
98 return path.Substring (lastDir+1);
103 public static string GetFullPath (string path)
105 path = _GetFullPath (path);
106 return GetCanonicalPath (path);
109 private static string _GetFullPath (string path)
112 throw new ArgumentNullException ("path");
113 if (!IsPathRooted (path))
114 path = UnixDirectory.GetCurrentDirectory() + DirectorySeparatorChar + path;
119 public static string GetCanonicalPath (string path)
123 GetPathComponents (path, out dirs, out lastIndex);
124 string end = string.Join ("/", dirs, 0, lastIndex);
125 return IsPathRooted (path) ? "/" + end : end;
128 private static void GetPathComponents (string path,
129 out string[] components, out int lastIndex)
131 string [] dirs = path.Split (DirectorySeparatorChar);
133 for (int i = 0; i < dirs.Length; ++i) {
134 if (dirs [i] == "." || dirs [i] == string.Empty) continue;
135 else if (dirs [i] == "..") {
136 if (target != 0) --target;
140 dirs [target++] = dirs [i];
146 public static string GetPathRoot (string path)
150 if (!IsPathRooted (path))
155 public static string GetCompleteRealPath (string path)
158 throw new ArgumentNullException ("path");
161 GetPathComponents (path, out dirs, out lastIndex);
162 StringBuilder realPath = new StringBuilder ();
163 if (dirs.Length > 0) {
164 string dir = IsPathRooted (path) ? "/" : "";
166 realPath.Append (GetRealPath (dir));
168 for (int i = 1; i < lastIndex; ++i) {
169 realPath.Append ("/").Append (dirs [i]);
170 string p = GetRealPath (realPath.ToString());
171 realPath.Remove (0, realPath.Length);
174 return realPath.ToString ();
177 public static string GetRealPath (string path)
180 string name = ReadSymbolicLink (path);
183 if (IsPathRooted (name))
186 path = GetDirectoryName (path) + DirectorySeparatorChar + name;
187 path = GetCanonicalPath (path);
192 // Read the specified symbolic link. If the file isn't a symbolic link,
193 // return null; otherwise, return the contents of the symbolic link.
195 // readlink(2) is horribly evil, as there is no way to query how big the
196 // symlink contents are. Consequently, it's trial and error...
197 internal static string ReadSymbolicLink (string path)
199 StringBuilder buf = new StringBuilder (256);
201 int r = Syscall.readlink (path, buf);
204 switch (e = Syscall.GetLastError()) {
206 // path isn't a symbolic link
209 UnixMarshal.ThrowExceptionForError (e);
213 else if (r == buf.Capacity) {
217 return buf.ToString (0, r);
221 public static bool IsPathRooted (string path)
223 if (path == null || path.Length == 0)
225 return path [0] == DirectorySeparatorChar;
228 internal static void CheckPath (string path)
231 throw new ArgumentNullException ();
232 if (path.IndexOfAny (UnixPath.InvalidPathChars) != -1)
233 throw new ArgumentException ("Invalid characters in path.");