1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.IO
10 {
11 public class InvalidPathException : Exception
12 {
13 public nothrow InvalidPathException(const string& message_) : base(message_)
14 {
15 }
16 }
17
18 public string GetCurrentWorkingDirectory()
19 {
20 int handle = RtGetCurrentWorkingDirectoryHandle();
21 if (handle < 0)
22 {
23 string errorMessage = RtGetError(handle);
24 RtDisposeError(handle);
25 throw FileSystemException(errorMessage);
26 }
27 string currentWorkingDirectory = Path.MakeCanonical(RtGetCurrentWorkingDirectory(handle));
28 RtFreeCurrentWorkingDirectoryHandle(handle);
29 return currentWorkingDirectory;
30 }
31
32 public string GetFullPath(const string& path)
33 {
34 string p = Path.MakeCanonical(path);
35 if (Path.IsRelative(p))
36 {
37 p = GetCurrentWorkingDirectory();
38 p.Append('/');
39 p.Append(Path.MakeCanonical(path));
40 }
41 List<string> components = p.Split('/');
42 long w = 0;
43 long n = components.Count();
44 for (long i = 0; i < n; ++i;)
45 {
46 string c = components[i];
47 if (i == 0 || (!c.IsEmpty() && c != "."))
48 {
49 if (c == "..")
50 {
51 --w;
52 if (w < 0)
53 {
54 throw InvalidPathException("path '" + path + "' is invalid");
55 }
56 }
57 else
58 {
59 if (w != i)
60 {
61 components[w] = components[i];
62 }
63 ++w;
64 }
65 }
66 }
67 if (w == 0)
68 {
69 return "/";
70 }
71 else if (w == 1)
72 {
73 string p = components[0];
74 if (p.Length() == 2 && IsAlpha(p[0]) && p[1] == ':')
75 {
76 return p + "/";
77 }
78 }
79 string result;
80 for (long i = 0; i < w; ++i;)
81 {
82 if (i != 0)
83 {
84 result.Append('/');
85 }
86 result.Append(components[i]);
87 }
88 if (result.IsEmpty())
89 {
90 return "/";
91 }
92 else
93 {
94 return result;
95 }
96 }
97
98 public nothrow string MakeRelativeDirPath(const string& dirPath, const string& referenceDirPath)
99 {
100 string p = GetFullPath(dirPath);
101 string r = GetFullPath(referenceDirPath);
102 if (p == r)
103 {
104 return string();
105 }
106 if (Path.GetDrive(p) != Path.GetDrive(r))
107 {
108 return p;
109 }
110 List<string> pc = p.Split('/');
111 List<string> rc = r.Split('/');
112 int n = cast<int>(Min(pc.Count(), rc.Count()));
113 int m = 0;
114 for (; m < n; ++m;)
115 {
116 if (pc[m] != rc[m])
117 {
118 break;
119 }
120 }
121 string result;
122 int rn = cast<int>(rc.Count());
123 for (int i = m; i < rn; ++i;)
124 {
125 result = Path.Combine(result, "..");
126 }
127 int pn = cast<int>(pc.Count());
128 for (int i = m; i < pn; ++i;)
129 {
130 result = Path.Combine(result, pc[i]);
131 }
132 return result;
133 }
134
135 public static class Path
136 {
137 public static nothrow string MakeCanonical(const string& path)
138 {
139 bool startsWithDriveLetter = false;
140 #if (WINDOWS)
141 if (path.Length() >= 2 && IsAlpha(path[0]) && path[1] == ':')
142 {
143 startsWithDriveLetter = true;
144 }
145 #endif
146 string result;
147 char prev = ' ';
148 bool first = true;
149 for (char c : path)
150 {
151 if (first)
152 {
153 first = false;
154 if (startsWithDriveLetter)
155 {
156 c = AsciiToUpper(c);
157 }
158 }
159 if (c == '\\')
160 {
161 c = '/';
162 }
163 if (c == '/')
164 {
165 if (prev != '/')
166 {
167 result.Append(c);
168 }
169 }
170 else
171 {
172 result.Append(c);
173 }
174 prev = c;
175 }
176 if (result.Length() == 3 && IsAlpha(result[0]) && result[1] == ':' && result[2] == '/')
177 {
178 return result;
179 }
180 if (result == "/")
181 {
182 return result;
183 }
184 if (!result.IsEmpty())
185 {
186 if (result[result.Length() - 1] == '/')
187 {
188 return result.Substring(0, result.Length() - 1);
189 }
190 }
191 return result;
192 }
193 public static nothrow string ChangeExtension(const string& path, const string& extension)
194 {
195 long lastDotPos = path.RFind('.');
196 long lastSlashPos = path.RFind('/');
197 if (lastSlashPos != -1 && lastDotPos < lastSlashPos)
198 {
199 lastDotPos = -1;
200 }
201 if (extension.IsEmpty())
202 {
203 if (lastDotPos != -1)
204 {
205 return path.Substring(0, lastDotPos);
206 }
207 else
208 {
209 return path;
210 }
211 }
212 else
213 {
214 if (lastDotPos == -1)
215 {
216 if (extension[0] == '.')
217 {
218 return path + extension;
219 }
220 else
221 {
222 return path + "." + extension;
223 }
224 }
225 else
226 {
227 if (extension[0] == '.')
228 {
229 return path.Substring(0, lastDotPos) + extension;
230 }
231 else
232 {
233 return path.Substring(0, lastDotPos + 1) + extension;
234 }
235 }
236 }
237 }
238 public static nothrow bool HasExtension(const string& path)
239 {
240 string p = MakeCanonical(path);
241 long lastDotPos = p.RFind('.');
242 if (lastDotPos != -1)
243 {
244 long lastColon = p.Find(':', lastDotPos + 1);
245 long lastDirSep = p.Find('/', lastDotPos + 1);
246 if (lastColon > lastDotPos || lastDirSep > lastDotPos)
247 {
248 return false;
249 }
250 else if (lastDotPos < p.Length() - 1)
251 {
252 return true;
253 }
254 else
255 {
256 return false;
257 }
258 }
259 else
260 {
261 return false;
262 }
263 }
264 public static nothrow string GetExtension(const string& path)
265 {
266 string p = MakeCanonical(path);
267 long lastDotPos = p.RFind('.');
268 if (lastDotPos != -1)
269 {
270 if (p.Find('/', lastDotPos + 1) != -1)
271 {
272 return string();
273 }
274 else
275 {
276 return p.Substring(lastDotPos);
277 }
278 }
279 else
280 {
281 return string();
282 }
283 }
284 public static nothrow string GetDrive(const string& path)
285 {
286 #if (WINDOWS)
287 if (path.Length() >= 2 && IsAlpha(path[0]) && path[1] == ':')
288 {
289 char c = AsciiToUpper(path[0]);
290 string s(c);
291 s.Append(':');
292 return s;
293 }
294 #endif
295 return string();
296 }
297
298 public static nothrow string GetFileName(const string& path)
299 {
300 if (path.IsEmpty())
301 {
302 return string();
303 }
304 else
305 {
306 string p = MakeCanonical(path);
307 char lastChar = p[p.Length() - 1];
308 if (lastChar == '/' || lastChar == ':')
309 {
310 return string();
311 }
312 else
313 {
314 long lastDirSepPos = p.RFind('/');
315 if (lastDirSepPos != -1)
316 {
317 return p.Substring(lastDirSepPos + 1);
318 }
319 else
320 {
321 return p;
322 }
323 }
324 }
325 }
326 public static nothrow string GetFileNameWithoutExtension(const string& path)
327 {
328 string fileName = GetFileName(path);
329 long lastDotPos = fileName.RFind('.');
330 if (lastDotPos != -1)
331 {
332 return fileName.Substring(0, lastDotPos);
333 }
334 else
335 {
336 return fileName;
337 }
338 }
339 public static nothrow string GetDirectoryName(const string& path)
340 {
341 string p = MakeCanonical(path);
342 if (p.IsEmpty())
343 {
344 return string();
345 }
346 else if (p.Length() == 3 && IsAlpha(p[0]) && p[1] == ':' && p[2] == '/')
347 {
348 return string();
349 }
350 else
351 {
352 long lastDirSepPos = p.RFind('/');
353 if (lastDirSepPos != -1)
354 {
355 return p.Substring(0, lastDirSepPos);
356 }
357 else
358 {
359 return string();
360 }
361 }
362 }
363 public static nothrow string Combine(const string& path1, const string& path2)
364 {
365 string p1 = MakeCanonical(path1);
366 string p2 = MakeCanonical(path2);
367 if (p1.IsEmpty())
368 {
369 return p2;
370 }
371 else if (p2.IsEmpty())
372 {
373 return p1;
374 }
375 else
376 {
377 if (IsAbsolute(p2))
378 {
379 return p2;
380 }
381 else
382 {
383 string result = p1;
384 if (p1[p1.Length() - 1] != '/')
385 {
386 result.Append('/');
387 }
388 result.Append(p2);
389 return result;
390 }
391 }
392 }
393 public static nothrow bool IsAbsolute(const string& path)
394 {
395 if (path.IsEmpty())
396 {
397 return false;
398 }
399 else
400 {
401 string p = MakeCanonical(path);
402 if (p[0] == '/')
403 {
404 return true;
405 }
406 else if (p.Length() > 2 && IsAlpha(p[0]) && p[1] == ':' && p[2] == '/')
407 {
408 return true;
409 }
410 else
411 {
412 return false;
413 }
414 }
415 }
416 public static nothrow bool IsRelative(const string& path)
417 {
418 return !IsAbsolute(path);
419 }
420 public static string GetParent(const string& path)
421 {
422 string fullPath = GetFullPath(path);
423 long lastSlashPos = fullPath.RFind('/');
424 if (lastSlashPos == -1)
425 {
426 return string();
427 }
428 else
429 {
430 return fullPath.Substring(0, lastSlashPos);
431 }
432 }
433 }
434 }