1
2
3
4
5
6 using System;
7 using System.Collections;
8 using System.Os;
9
10 namespace System.IO
11 {
12 public enum FileType : int
13 {
14 regular = 1, directory = 2, fifo = 3
15 }
16
17 public enum Access : int
18 {
19 none = 0, read = 1 << 2, write = 1 << 1, execute = 1 << 0
20 }
21
22 public int MakeMode(Access ownerAccess, Access groupAccess, Access otherAccess)
23 {
24 return (int(ownerAccess & 7) << 6) | ((int(groupAccess) & 7) << 3) | (int(otherAccess) & 7);
25 }
26
27 public const int rootFileSystemNumber = 0;
28
29 public class FileStatus
30 {
31 public int fileSystemNumber;
32 public int inodeNumber;
33 public FileType fileType;
34 public Access ownerAccess;
35 public Access groupAccess;
36 public Access otherAccess;
37 public bool setUID;
38 public bool setGID;
39 public int nlinks;
40 public int uid;
41 public int gid;
42 public long fileSize;
43 public DateTime ctime;
44 public DateTime mtime;
45 public DateTime atime;
46 }
47
48 public void Stat(const char* path, FileStatus& status)
49 {
50 byte[statBufSize] statBuf;
51 int result = stat(path, &statBuf[0], statBufSize);
52 if (result == -1)
53 {
54 ThrowSystemError();
55 }
56 GetFileStatus(&statBuf[0], status);
57 }
58
59 public void GetFileStatus(byte* statBuf, FileStatus& status)
60 {
61 MemoryReader reader(statBuf, statBufSize);
62 status.fileSystemNumber = reader.ReadInt();
63 status.inodeNumber = reader.ReadInt();
64 status.fileType = cast<FileType>(reader.ReadInt());
65 status.ownerAccess = cast<Access>(reader.ReadInt());
66 status.groupAccess = cast<Access>(reader.ReadInt());
67 status.otherAccess = cast<Access>(reader.ReadInt());
68 status.setUID = reader.ReadByte() == 1;
69 status.setGID = reader.ReadByte() == 1;
70 status.nlinks = reader.ReadInt();
71 status.uid = reader.ReadInt();
72 status.gid = reader.ReadInt();
73 status.fileSize = reader.ReadLong();
74 status.ctime = reader.ReadDateTime();
75 status.mtime = reader.ReadDateTime();
76 status.atime = reader.ReadDateTime();
77 }
78
79 public void UTime(const char* path, const DateTime& atime, const DateTime& mtime)
80 {
81 byte[16] timeBuf;
82 MemoryWriter writer(&timeBuf[0], 16);
83 writer.Write(atime);
84 writer.Write(mtime);
85 UTime(path, &timeBuf[0], 16);
86 }
87
88 public enum FileCopyOptions : int
89 {
90 none = 0, update = 1 << 0, verbose = 1 << 1, dontPreserveTimestamps = 1 << 2, dontPreserveMode = 1 << 3, removeCrs = 1 << 4, addCrs = 1 << 5, noBufferedCopy = 1 << 6
91 }
92
93 public static class File
94 {
95 public static bool Exists(const string& filePath)
96 {
97 return FileExists(filePath);
98 }
99 public static long Size(const string& filePath)
100 {
101 return GetFileSize(filePath);
102 }
103 public static DateTime AccessTime(const string& filePath)
104 {
105 return GetFileAccessTime(filePath);
106 }
107 public static DateTime ModificationTime(const string& filePath)
108 {
109 return GetFileModificationTime(filePath);
110 }
111 public static void GetTimes(const string& path, DateTime& accessTime, DateTime& modificationTime)
112 {
113 GetFileTimes(path, accessTime, modificationTime);
114 }
115 public static void SetTimes(const string& path, const DateTime& accessTime, const DateTime& modificationTime)
116 {
117 SetFileTimes(path, accessTime, modificationTime);
118 }
119 public static void Copy(const string& sourceFilePath, const string& destFilePath)
120 {
121 Copy(sourceFilePath, destFilePath, FileCopyOptions.none);
122 }
123 public static void Copy(const string& sourceFilePath, const string& destFilePath, FileCopyOptions options)
124 {
125 CopyFile(sourceFilePath, destFilePath, options);
126 }
127 public static void Remove(const string& filePath)
128 {
129 RemoveFile(filePath);
130 }
131 public static StreamWriter CreateText(const string& filePath)
132 {
133 return StreamWriter(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(
134 new FileStream(filePath, cast<OpenFlags>(OpenFlags.write | OpenFlags.create | OpenFlags.truncate | OpenFlags.text))))));
135 }
136 public static BinaryWriter CreateBinary(const string& filePath)
137 {
138 return CreateBinary(filePath, false);
139 }
140 public static BinaryWriter CreateBinary(const string& filePath, bool randomAccess)
141 {
142 OpenFlags flags = cast<OpenFlags>(OpenFlags.write | OpenFlags.create | OpenFlags.truncate);
143 if (randomAccess)
144 {
145 flags = cast<OpenFlags>(flags | OpenFlags.random_access);
146 }
147 return BinaryWriter(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(new FileStream(filePath, flags)))));
148 }
149 public static StreamWriter AppendText(const string& filePath)
150 {
151 return StreamWriter(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(
152 new FileStream(filePath, cast<OpenFlags>(OpenFlags.append | OpenFlags.text))))));
153 }
154 public static StreamReader OpenRead(const string& filePath)
155 {
156 return StreamReader(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(
157 new FileStream(filePath, cast<OpenFlags>(OpenFlags.read | OpenFlags.text))))));
158 }
159 public static BinaryReader OpenBinary(const string& filePath)
160 {
161 return OpenBinary(filePath, false);
162 }
163 public static BinaryReader OpenBinary(const string& filePath, bool randomAccess)
164 {
165 OpenFlags flags = OpenFlags.read;
166 if (randomAccess)
167 {
168 flags = cast<OpenFlags>(flags | OpenFlags.random_access);
169 }
170 return BinaryReader(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(new FileStream(filePath, flags)))));
171 }
172 public static string ReadAllText(const string& filePath)
173 {
174 StreamReader reader = OpenRead(filePath);
175 string content = reader.ReadToEnd();
176 if (content.Length() >= 3 && cast<byte>(content[0]) == 0xEFu && cast<byte>(content[1]) == 0xBBu && cast<byte>(content[2]) == 0xBFu)
177 {
178 return content.Substring(3);
179 }
180 else
181 {
182 return content;
183 }
184 }
185 public static List<string> ReadAllLines(const string& filePath)
186 {
187 List<string> lines;
188 bool start = true;
189 StreamReader reader = OpenRead(filePath);
190 string line = reader.ReadLine();
191 while (!reader.EndOfStream())
192 {
193 if (start)
194 {
195 if (line.Length() >= 3 && cast<byte>(line[0]) == 0xEFu && cast<byte>(line[1]) == 0xBBu && cast<byte>(line[2]) == 0xBFu)
196 {
197 line = line.Substring(3);
198 }
199 start = false;
200 }
201 lines.Add(line);
202 line = reader.ReadLine();
203 }
204 if (!line.IsEmpty())
205 {
206 lines.Add(line);
207 }
208 return lines;
209 }
210 }
211
212 public bool FileExists(const string& filePath)
213 {
214 byte[statBufSize] statBuf;
215 int result = stat(filePath.Chars(), &statBuf[0], statBufSize);
216 if (result == -1)
217 {
218 SystemError systemError = GetSystemError();
219 if (systemError.errorCode == ENOTFOUND)
220 {
221 return false;
222 }
223 else
224 {
225 ThrowSystemError();
226 }
227 }
228 else
229 {
230 FileStatus status;
231 GetFileStatus(&statBuf[0], status);
232 if (status.fileType == FileType.regular)
233 {
234 return true;
235 }
236 else
237 {
238 throw FileSystemException("path '" + filePath + "' is not a regular file path");
239 }
240 }
241 }
242
243 public long GetFileSize(const string& filePath)
244 {
245 if (filePath.IsEmpty())
246 {
247 throw InvalidPathException("file path is empty");
248 }
249 FileStatus status;
250 Stat(filePath.Chars(), status);
251 if (status.fileType == FileType.regular)
252 {
253 return status.fileSize;
254 }
255 else
256 {
257 throw InvalidPathException("path '" + filePath + "' is not a regular file path");
258 }
259 }
260
261 public DateTime GetFileAccessTime(const string& filePath)
262 {
263 if (filePath.IsEmpty())
264 {
265 throw InvalidPathException("file path is empty");
266 }
267 FileStatus status;
268 Stat(filePath.Chars(), status);
269 return status.atime;
270 }
271
272 public DateTime GetFileModificationTime(const string& filePath)
273 {
274 if (filePath.IsEmpty())
275 {
276 throw InvalidPathException("file path is empty");
277 }
278 FileStatus status;
279 Stat(filePath.Chars(), status);
280 return status.mtime;
281 }
282
283 public void GetFileTimes(const string& path, DateTime& accessTime, DateTime& modificationTime)
284 {
285 if (path.IsEmpty())
286 {
287 throw InvalidPathException("file path is empty");
288 }
289 FileStatus status;
290 Stat(path.Chars(), status);
291 accessTime = status.atime;
292 modificationTime = status.mtime;
293 }
294
295 public void SetFileTimes(const string& path, const DateTime& accessTime, const DateTime& modificationTime)
296 {
297 if (path.IsEmpty())
298 {
299 throw InvalidPathException("file path is empty");
300 }
301 UTime(path.Chars(), accessTime, modificationTime);
302 }
303
304 public void CopyFile(const string& sourceFilePath, const string& destFilePath, FileCopyOptions options)
305 {
306 DateTime sourceAccessTime;
307 DateTime sourceModificationTime;
308 GetFileTimes(sourceFilePath, sourceAccessTime, sourceModificationTime);
309 bool destinationExists = File.Exists(destFilePath);
310 bool copy = false;
311 if ((options & FileCopyOptions.update) != FileCopyOptions.none)
312 {
313 if (!destinationExists)
314 {
315 copy = true;
316 }
317 else
318 {
319 DateTime destModificationTime = GetFileModificationTime(destFilePath);
320 if (sourceModificationTime > destModificationTime)
321 {
322 copy = true;
323 }
324 }
325 }
326 else
327 {
328 copy = true;
329 }
330 if (copy)
331 {
332 bool addCrs = (options & FileCopyOptions.addCrs) != FileCopyOptions.none;
333 bool removeCrs = (options & FileCopyOptions.removeCrs) != FileCopyOptions.none;
334 if (addCrs && removeCrs)
335 {
336 throw SystemError(EPARAM, "cannot have both 'FileCopyOptions.addCrs' and 'FileCopyOptions.removeCrs' option");
337 }
338 long size = File.Size(sourceFilePath);
339 bool bufferedCopy = (options & FileCopyOptions.noBufferedCopy) == FileCopyOptions.none && !addCrs && !removeCrs;
340 if (bufferedCopy)
341 {
342 BufferedCopy(sourceFilePath, destFilePath, size);
343 }
344 else
345 {
346 BinaryReader reader = File.OpenBinary(sourceFilePath);
347 BinaryWriter writer = File.CreateBinary(destFilePath);
348 for (long i = 0; i < size; ++i;)
349 {
350 byte x = reader.ReadByte();
351 if (removeCrs && x == cast<byte>('\r'))
352 {
353 continue;
354 }
355 if (addCrs && x == cast<byte>('\n'))
356 {
357 writer.Write(cast<byte>('\r'));
358 }
359 writer.Write(x);
360 }
361 }
362 }
363 if (copy)
364 {
365 if ((options & FileCopyOptions.dontPreserveTimestamps) == FileCopyOptions.none)
366 {
367 File.SetTimes(destFilePath, sourceAccessTime, sourceModificationTime);
368 }
369 if ((options & FileCopyOptions.dontPreserveMode) == FileCopyOptions.none)
370 {
371 FileStatus status;
372 Stat(sourceFilePath.Chars(), status);
373 ChMod(destFilePath.Chars(), MakeMode(status.ownerAccess, status.groupAccess, status.otherAccess));
374 }
375 if ((options & FileCopyOptions.verbose) != FileCopyOptions.none)
376 {
377
378 Console.WriteLine(GetFullPath(sourceFilePath) + " -> " + GetFullPath(destFilePath));
379 }
380 }
381 }
382
383 public void BufferedCopy(const string& sourceFilePath, const string& destFilePath, long size)
384 {
385 FileStream sourceFileStream(sourceFilePath, OpenFlags.read);
386 FileStream destFileStream(destFilePath, cast<OpenFlags>(OpenFlags.write | OpenFlags.create | OpenFlags.truncate));
387 byte[4096] buffer;
388 while (size > 0)
389 {
390 long bytesRead = sourceFileStream.Read(&buffer[0], 4096);
391 destFileStream.Write(&buffer[0], bytesRead);
392 size = size - bytesRead;
393 }
394 }
395
396 public void RemoveFile(const string& filePath)
397 {
398 Unlink(filePath.Chars());
399 }
400 }