2 // Mono.Unix/StdioFileStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2005-2006 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.
31 using System
.Runtime
.InteropServices
;
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
)
64 throw new ArgumentNullException ("path");
65 InitStream (Fopen (path
, "rb"), true);
68 public StdioFileStream (string path
, string mode
)
71 throw new ArgumentNullException ("path");
72 InitStream (Fopen (path
, mode
), true);
75 public StdioFileStream (string path
, FileMode mode
)
78 throw new ArgumentNullException ("path");
79 InitStream (Fopen (path
, ToFopenMode (path
, mode
)), true);
82 public StdioFileStream (string path
, FileAccess access
)
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
)
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");
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 ());
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
;
120 long offset
= Native
.Stdlib
.fseek (file
, 0, Native
.SeekFlags
.SEEK_CUR
);
123 Native
.Stdlib
.fread (IntPtr
.Zero
, 0, 0, file
);
124 if (Native
.Stdlib
.ferror (file
) == 0)
126 Native
.Stdlib
.fwrite (IntPtr
.Zero
, 0, 0, file
);
127 if (Native
.Stdlib
.ferror (file
) == 0)
129 Native
.Stdlib
.clearerr (file
);
132 throw new ArgumentException (Locale
.GetText ("Invalid file stream"), "fileStream");
137 private void InitCanReadWrite (FileAccess access
)
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
);
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
164 if (mode
== FileMode
.OpenOrCreate
&& access
== FileAccess
.Read
&& !exists
)
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
);
179 private static bool FileExists (string file
)
182 IntPtr f
= Native
.Stdlib
.fopen (file
, "r");
183 found
= f
!= IntPtr
.Zero
;
184 if (f
!= IntPtr
.Zero
)
185 Native
.Stdlib
.fclose (f
);
189 private void AssertNotDisposed ()
191 if (file
== InvalidFileStream
)
192 throw new ObjectDisposedException ("Invalid File Stream");
196 public IntPtr Handle
{
198 AssertNotDisposed ();
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
{
218 AssertNotDisposed ();
220 throw new NotSupportedException ("File Stream doesn't support seeking");
221 long curPos
= Native
.Stdlib
.ftell (file
);
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
);
229 UnixMarshal
.ThrowExceptionForLastError ();
231 r
= Native
.Stdlib
.fseek (file
, curPos
, Native
.SeekFlags
.SEEK_SET
);
232 UnixMarshal
.ThrowExceptionForLastErrorIf (r
);
239 public override long Position
{
241 AssertNotDisposed ();
243 throw new NotSupportedException ("The stream does not support seeking");
244 long pos
= Native
.Stdlib
.ftell (file
);
246 UnixMarshal
.ThrowExceptionForLastError ();
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
);
264 public void RestoreFilePosition (Native
.FilePosition pos
)
266 AssertNotDisposed ();
268 throw new ArgumentNullException ("value");
269 int r
= Native
.Stdlib
.fsetpos (file
, pos
);
270 UnixMarshal
.ThrowExceptionForLastErrorIf (r
);
274 public override void Flush ()
276 AssertNotDisposed ();
277 int r
= Native
.Stdlib
.fflush (file
);
279 UnixMarshal
.ThrowExceptionForLastError ();
283 public override unsafe int Read ([In
, Out
] byte[] buffer
, int offset
, int count
)
285 AssertNotDisposed ();
286 AssertValidBuffer (buffer
, offset
, count
);
288 throw new NotSupportedException ("Stream does not support reading");
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 ();
302 private void AssertValidBuffer (byte[] buffer
, int offset
, int count
)
305 throw new ArgumentNullException ("buffer");
307 throw new ArgumentOutOfRangeException ("offset", "< 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
);
323 public override long Seek (long offset
, SeekOrigin origin
)
325 AssertNotDisposed ();
327 throw new NotSupportedException ("The File Stream does not support seeking");
329 Native
.SeekFlags sf
= Native
.SeekFlags
.SEEK_CUR
;
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
);
339 throw new IOException ("Unable to seek",
340 UnixMarshal
.CreateExceptionForLastError ());
342 long pos
= Native
.Stdlib
.ftell (file
);
344 throw new IOException ("Unable to get current file position",
345 UnixMarshal
.CreateExceptionForLastError ());
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
);
361 throw new NotSupportedException ("File Stream does not support writing");
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 ();
377 public override void Close ()
379 if (file
== InvalidFileStream
)
383 int r
= Native
.Stdlib
.fclose (file
);
385 UnixMarshal
.ThrowExceptionForLastError ();
389 file
= InvalidFileStream
;
394 GC
.SuppressFinalize (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
;