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