update readme (#21797)
[mono-project.git] / mcs / class / Mono.Posix / Mono.Unix / StdioFileStream.cs
blob1d0ef97e9f7e5aeb081304271e8211a586084abe
1 //
2 // Mono.Unix/StdioFileStream.cs
3 //
4 // Authors:
5 // Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2005-2006 Jonathan Pryor
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
29 using System;
30 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Text;
33 using Mono.Unix;
35 namespace Mono.Unix {
37 public class StdioFileStream : Stream
39 public static readonly IntPtr InvalidFileStream = IntPtr.Zero;
40 public static readonly IntPtr StandardInput = Native.Stdlib.stdin;
41 public static readonly IntPtr StandardOutput = Native.Stdlib.stdout;
42 public static readonly IntPtr StandardError = Native.Stdlib.stderr;
44 public StdioFileStream (IntPtr fileStream)
45 : this (fileStream, true) {}
47 public StdioFileStream (IntPtr fileStream, bool ownsHandle)
49 InitStream (fileStream, ownsHandle);
52 public StdioFileStream (IntPtr fileStream, FileAccess access)
53 : this (fileStream, access, true) {}
55 public StdioFileStream (IntPtr fileStream, FileAccess access, bool ownsHandle)
57 InitStream (fileStream, ownsHandle);
58 InitCanReadWrite (access);
61 public StdioFileStream (string path)
63 if (path == null)
64 throw new ArgumentNullException ("path");
65 InitStream (Fopen (path, "rb"), true);
68 public StdioFileStream (string path, string mode)
70 if (path == null)
71 throw new ArgumentNullException ("path");
72 InitStream (Fopen (path, mode), true);
75 public StdioFileStream (string path, FileMode mode)
77 if (path == null)
78 throw new ArgumentNullException ("path");
79 InitStream (Fopen (path, ToFopenMode (path, mode)), true);
82 public StdioFileStream (string path, FileAccess access)
84 if (path == null)
85 throw new ArgumentNullException ("path");
86 InitStream (Fopen (path, ToFopenMode (path, access)), true);
87 InitCanReadWrite (access);
90 public StdioFileStream (string path, FileMode mode, FileAccess access)
92 if (path == null)
93 throw new ArgumentNullException ("path");
94 InitStream (Fopen (path, ToFopenMode (path, mode, access)), true);
95 InitCanReadWrite (access);
98 private static IntPtr Fopen (string path, string mode)
100 if (path.Length == 0)
101 throw new ArgumentException ("path");
102 if (mode == null)
103 throw new ArgumentNullException ("mode");
104 IntPtr f = Native.Stdlib.fopen (path, mode);
105 if (f == IntPtr.Zero)
106 throw new DirectoryNotFoundException ("path",
107 UnixMarshal.CreateExceptionForLastError ());
108 return f;
111 private void InitStream (IntPtr fileStream, bool ownsHandle)
113 if (InvalidFileStream == fileStream)
114 throw new ArgumentException (Locale.GetText ("Invalid file stream"), "fileStream");
116 this.file = fileStream;
117 this.owner = ownsHandle;
119 try {
120 long offset = Native.Stdlib.fseek (file, 0, Native.SeekFlags.SEEK_CUR);
121 if (offset != -1)
122 canSeek = true;
123 Native.Stdlib.fread (IntPtr.Zero, 0, 0, file);
124 if (Native.Stdlib.ferror (file) == 0)
125 canRead = true;
126 Native.Stdlib.fwrite (IntPtr.Zero, 0, 0, file);
127 if (Native.Stdlib.ferror (file) == 0)
128 canWrite = true;
129 Native.Stdlib.clearerr (file);
131 catch (Exception) {
132 throw new ArgumentException (Locale.GetText ("Invalid file stream"), "fileStream");
134 GC.KeepAlive (this);
137 private void InitCanReadWrite (FileAccess access)
139 canRead = canRead &&
140 (access == FileAccess.Read || access == FileAccess.ReadWrite);
141 canWrite = canWrite &&
142 (access == FileAccess.Write || access == FileAccess.ReadWrite);
145 private static string ToFopenMode (string file, FileMode mode)
147 string cmode = Native.NativeConvert.ToFopenMode (mode);
148 AssertFileMode (file, mode);
149 return cmode;
152 private static string ToFopenMode (string file, FileAccess access)
154 return Native.NativeConvert.ToFopenMode (access);
157 private static string ToFopenMode (string file, FileMode mode, FileAccess access)
159 string cmode = Native.NativeConvert.ToFopenMode (mode, access);
160 bool exists = AssertFileMode (file, mode);
161 // HACK: for open-or-create & read, mode is "rb", which doesn't create
162 // files. If the file doesn't exist, we need to use "w+b" to ensure
163 // file creation.
164 if (mode == FileMode.OpenOrCreate && access == FileAccess.Read && !exists)
165 cmode = "w+b";
166 return cmode;
169 private static bool AssertFileMode (string file, FileMode mode)
171 bool exists = FileExists (file);
172 if (mode == FileMode.CreateNew && exists)
173 throw new IOException ("File exists and FileMode.CreateNew specified");
174 if ((mode == FileMode.Open || mode == FileMode.Truncate) && !exists)
175 throw new FileNotFoundException ("File doesn't exist and FileMode.Open specified", file);
176 return exists;
179 private static bool FileExists (string file)
181 bool found = false;
182 IntPtr f = Native.Stdlib.fopen (file, "r");
183 found = f != IntPtr.Zero;
184 if (f != IntPtr.Zero)
185 Native.Stdlib.fclose (f);
186 return found;
189 private void AssertNotDisposed ()
191 if (file == InvalidFileStream)
192 throw new ObjectDisposedException ("Invalid File Stream");
193 GC.KeepAlive (this);
196 public IntPtr Handle {
197 get {
198 AssertNotDisposed ();
199 GC.KeepAlive (this);
200 return file;
204 public override bool CanRead {
205 get {return canRead;}
208 public override bool CanSeek {
209 get {return canSeek;}
212 public override bool CanWrite {
213 get {return canWrite;}
216 public override long Length {
217 get {
218 AssertNotDisposed ();
219 if (!CanSeek)
220 throw new NotSupportedException ("File Stream doesn't support seeking");
221 long curPos = Native.Stdlib.ftell (file);
222 if (curPos == -1)
223 throw new NotSupportedException ("Unable to obtain current file position");
224 int r = Native.Stdlib.fseek (file, 0, Native.SeekFlags.SEEK_END);
225 UnixMarshal.ThrowExceptionForLastErrorIf (r);
227 long endPos = Native.Stdlib.ftell (file);
228 if (endPos == -1)
229 UnixMarshal.ThrowExceptionForLastError ();
231 r = Native.Stdlib.fseek (file, curPos, Native.SeekFlags.SEEK_SET);
232 UnixMarshal.ThrowExceptionForLastErrorIf (r);
234 GC.KeepAlive (this);
235 return endPos;
239 public override long Position {
240 get {
241 AssertNotDisposed ();
242 if (!CanSeek)
243 throw new NotSupportedException ("The stream does not support seeking");
244 long pos = Native.Stdlib.ftell (file);
245 if (pos == -1)
246 UnixMarshal.ThrowExceptionForLastError ();
247 GC.KeepAlive (this);
248 return (long) pos;
250 set {
251 AssertNotDisposed ();
252 Seek (value, SeekOrigin.Begin);
256 public void SaveFilePosition (Native.FilePosition pos)
258 AssertNotDisposed ();
259 int r = Native.Stdlib.fgetpos (file, pos);
260 UnixMarshal.ThrowExceptionForLastErrorIf (r);
261 GC.KeepAlive (this);
264 public void RestoreFilePosition (Native.FilePosition pos)
266 AssertNotDisposed ();
267 if (pos == null)
268 throw new ArgumentNullException ("value");
269 int r = Native.Stdlib.fsetpos (file, pos);
270 UnixMarshal.ThrowExceptionForLastErrorIf (r);
271 GC.KeepAlive (this);
274 public override void Flush ()
276 AssertNotDisposed ();
277 int r = Native.Stdlib.fflush (file);
278 if (r != 0)
279 UnixMarshal.ThrowExceptionForLastError ();
280 GC.KeepAlive (this);
283 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
285 AssertNotDisposed ();
286 AssertValidBuffer (buffer, offset, count);
287 if (!CanRead)
288 throw new NotSupportedException ("Stream does not support reading");
290 ulong r = 0;
291 fixed (byte* buf = &buffer[offset]) {
292 r = Native.Stdlib.fread (buf, 1, (ulong) count, file);
294 if (r != (ulong) count) {
295 if (Native.Stdlib.ferror (file) != 0)
296 throw new IOException ();
298 GC.KeepAlive (this);
299 return (int) r;
302 private void AssertValidBuffer (byte[] buffer, int offset, int count)
304 if (buffer == null)
305 throw new ArgumentNullException ("buffer");
306 if (offset < 0)
307 throw new ArgumentOutOfRangeException ("offset", "< 0");
308 if (count < 0)
309 throw new ArgumentOutOfRangeException ("count", "< 0");
310 if (offset > buffer.Length)
311 throw new ArgumentException ("destination offset is beyond array size");
312 if (offset > (buffer.Length - count))
313 throw new ArgumentException ("would overrun buffer");
316 public void Rewind ()
318 AssertNotDisposed ();
319 Native.Stdlib.rewind (file);
320 GC.KeepAlive (this);
323 public override long Seek (long offset, SeekOrigin origin)
325 AssertNotDisposed ();
326 if (!CanSeek)
327 throw new NotSupportedException ("The File Stream does not support seeking");
329 Native.SeekFlags sf = Native.SeekFlags.SEEK_CUR;
330 switch (origin) {
331 case SeekOrigin.Begin: sf = Native.SeekFlags.SEEK_SET; break;
332 case SeekOrigin.Current: sf = Native.SeekFlags.SEEK_CUR; break;
333 case SeekOrigin.End: sf = Native.SeekFlags.SEEK_END; break;
334 default: throw new ArgumentException ("origin");
337 int r = Native.Stdlib.fseek (file, offset, sf);
338 if (r != 0)
339 throw new IOException ("Unable to seek",
340 UnixMarshal.CreateExceptionForLastError ());
342 long pos = Native.Stdlib.ftell (file);
343 if (pos == -1)
344 throw new IOException ("Unable to get current file position",
345 UnixMarshal.CreateExceptionForLastError ());
347 GC.KeepAlive (this);
348 return pos;
351 public override void SetLength (long value)
353 throw new NotSupportedException ("ANSI C doesn't provide a way to truncate a file");
356 public override unsafe void Write (byte[] buffer, int offset, int count)
358 AssertNotDisposed ();
359 AssertValidBuffer (buffer, offset, count);
360 if (!CanWrite)
361 throw new NotSupportedException ("File Stream does not support writing");
363 ulong r = 0;
364 fixed (byte* buf = &buffer[offset]) {
365 r = Native.Stdlib.fwrite (buf, (ulong) 1, (ulong) count, file);
367 if (r != (ulong) count)
368 UnixMarshal.ThrowExceptionForLastError ();
369 GC.KeepAlive (this);
372 ~StdioFileStream ()
374 Close ();
377 public override void Close ()
379 if (file == InvalidFileStream)
380 return;
382 if (owner) {
383 int r = Native.Stdlib.fclose (file);
384 if (r != 0)
385 UnixMarshal.ThrowExceptionForLastError ();
386 } else
387 Flush ();
389 file = InvalidFileStream;
390 canRead = false;
391 canSeek = false;
392 canWrite = false;
394 GC.SuppressFinalize (this);
395 GC.KeepAlive (this);
398 private bool canSeek = false;
399 private bool canRead = false;
400 private bool canWrite = false;
401 private bool owner = true;
402 private IntPtr file = InvalidFileStream;
406 // vim: noexpandtab