2 // Mono.Unix/StdioFileStream.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2005 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 = Stdlib.stdin;
41 public static readonly IntPtr StandardOutput = Stdlib.stdout;
42 public static readonly IntPtr StandardError = 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 InitStream (Fopen (path, "r"), true);
66 public StdioFileStream (string path, string mode)
68 InitStream (Fopen (path, mode), true);
71 public StdioFileStream (string path, FileMode mode)
73 InitStream (Fopen (path, ToFopenMode (path, mode)), true);
76 public StdioFileStream (string path, FileAccess access)
78 InitStream (Fopen (path, ToFopenMode (path, access)), true);
79 InitCanReadWrite (access);
82 public StdioFileStream (string path, FileMode mode, FileAccess access)
84 InitStream (Fopen (path, ToFopenMode (path, mode, access)), true);
85 InitCanReadWrite (access);
88 private static IntPtr Fopen (string path, string mode)
91 throw new ArgumentNullException ("path");
93 throw new ArgumentException ("path");
95 throw new ArgumentNullException ("path");
96 IntPtr f = Stdlib.fopen (path, mode);
98 throw new DirectoryNotFoundException ("path",
99 UnixMarshal.CreateExceptionForLastError ());
103 private void InitStream (IntPtr fileStream, bool ownsHandle)
105 if (InvalidFileStream == fileStream)
106 throw new ArgumentException (Locale.GetText ("Invalid file stream"), "fileStream");
108 this.file = fileStream;
109 this.owner = ownsHandle;
112 long offset = Stdlib.fseek (file, 0, SeekFlags.SEEK_CUR);
115 Stdlib.fread (IntPtr.Zero, 0, 0, file);
116 if (Stdlib.ferror (file) == 0)
118 Stdlib.fwrite (IntPtr.Zero, 0, 0, file);
119 if (Stdlib.ferror (file) == 0)
121 Stdlib.clearerr (file);
123 catch (Exception e) {
124 throw new ArgumentException (Locale.GetText ("Invalid file stream"), "fileStream");
128 private void InitCanReadWrite (FileAccess access)
131 (access == FileAccess.Read || access == FileAccess.ReadWrite);
132 canWrite = canWrite &&
133 (access == FileAccess.Write || access == FileAccess.ReadWrite);
136 private static string ToFopenMode (string file, FileMode mode)
138 string cmode = UnixConvert.ToFopenMode (mode);
139 AssertFileMode (file, mode);
143 private static string ToFopenMode (string file, FileAccess access)
145 return UnixConvert.ToFopenMode (access);
148 private static string ToFopenMode (string file, FileMode mode, FileAccess access)
150 string cmode = UnixConvert.ToFopenMode (mode, access);
151 bool exists = AssertFileMode (file, mode);
152 // HACK: for open-or-create & read, mode is "rb", which doesn't create
153 // files. If the file doesn't exist, we need to use "w+b" to ensure
155 if (mode == FileMode.OpenOrCreate && access == FileAccess.Read && !exists)
160 private static bool AssertFileMode (string file, FileMode mode)
162 bool exists = FileExists (file);
163 if (mode == FileMode.CreateNew && exists)
164 throw new IOException ("File exists and FileMode.CreateNew specified");
165 if ((mode == FileMode.Open || mode == FileMode.Truncate) && !exists)
166 throw new FileNotFoundException ("File doesn't exist and FileMode.Open specified", file);
170 private static bool FileExists (string file)
173 IntPtr f = Stdlib.fopen (file, "r");
174 found = f != IntPtr.Zero;
175 if (f != IntPtr.Zero)
180 private void AssertNotDisposed ()
182 if (file == InvalidFileStream)
183 throw new ObjectDisposedException ("Invalid File Stream");
186 public IntPtr Handle {
187 get {AssertNotDisposed (); return file;}
190 public override bool CanRead {
191 get {return canRead;}
194 public override bool CanSeek {
195 get {return canSeek;}
198 public override bool CanWrite {
199 get {return canWrite;}
202 public override long Length {
204 AssertNotDisposed ();
206 throw new NotSupportedException ("File Stream doesn't support seeking");
207 long curPos = Stdlib.ftell (file);
209 throw new NotSupportedException ("Unable to obtain current file position");
210 int r = Stdlib.fseek (file, 0, SeekFlags.SEEK_END);
211 UnixMarshal.ThrowExceptionForLastErrorIf (r);
213 long endPos = Stdlib.ftell (file);
215 UnixMarshal.ThrowExceptionForLastError ();
217 r = Stdlib.fseek (file, curPos, SeekFlags.SEEK_SET);
218 UnixMarshal.ThrowExceptionForLastErrorIf (r);
224 public override long Position {
226 AssertNotDisposed ();
228 throw new NotSupportedException ("The stream does not support seeking");
229 long pos = Stdlib.ftell (file);
231 UnixMarshal.ThrowExceptionForLastError ();
235 AssertNotDisposed ();
236 Seek (value, SeekOrigin.Begin);
240 public void SaveFilePosition (FilePosition pos)
242 AssertNotDisposed ();
243 int r = Stdlib.fgetpos (file, pos);
244 UnixMarshal.ThrowExceptionForLastErrorIf (r);
247 public void RestoreFilePosition (FilePosition pos)
249 AssertNotDisposed ();
251 throw new ArgumentNullException ("value");
252 int r = Stdlib.fsetpos (file, pos);
253 UnixMarshal.ThrowExceptionForLastErrorIf (r);
256 public override void Flush ()
258 AssertNotDisposed ();
259 int r = Stdlib.fflush (file);
261 UnixMarshal.ThrowExceptionForLastError ();
264 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
266 AssertNotDisposed ();
267 AssertValidBuffer (buffer, offset, count);
269 throw new NotSupportedException ("Stream does not support reading");
272 fixed (byte* buf = &buffer[offset]) {
273 r = Stdlib.fread (buf, 1, (ulong) count, file);
275 if (r != (ulong) count) {
276 if (Stdlib.ferror (file) != 0)
277 throw new IOException ();
282 private void AssertValidBuffer (byte[] buffer, int offset, int count)
285 throw new ArgumentNullException ("buffer");
287 throw new ArgumentOutOfRangeException ("offset", "< 0");
289 throw new ArgumentOutOfRangeException ("count", "< 0");
290 if (offset > buffer.Length)
291 throw new ArgumentException ("destination offset is beyond array size");
292 if (offset > (buffer.Length - count))
293 throw new ArgumentException ("would overrun buffer");
296 public void Rewind ()
298 AssertNotDisposed ();
299 Stdlib.rewind (file);
302 public override long Seek (long offset, SeekOrigin origin)
304 AssertNotDisposed ();
306 throw new NotSupportedException ("The File Stream does not support seeking");
308 SeekFlags sf = SeekFlags.SEEK_CUR;
310 case SeekOrigin.Begin: sf = SeekFlags.SEEK_SET; break;
311 case SeekOrigin.Current: sf = SeekFlags.SEEK_CUR; break;
312 case SeekOrigin.End: sf = SeekFlags.SEEK_END; break;
313 default: throw new ArgumentException ("origin");
316 int r = Stdlib.fseek (file, offset, sf);
318 throw new IOException ("Unable to seek",
319 UnixMarshal.CreateExceptionForLastError ());
321 long pos = Stdlib.ftell (file);
323 throw new IOException ("Unable to get current file position",
324 UnixMarshal.CreateExceptionForLastError ());
329 public override void SetLength (long value)
331 throw new NotSupportedException ("ANSI C doesn't provide a way to truncate a file");
334 public override unsafe void Write (byte[] buffer, int offset, int count)
336 AssertNotDisposed ();
337 AssertValidBuffer (buffer, offset, count);
339 throw new NotSupportedException ("File Stream does not support writing");
342 fixed (byte* buf = &buffer[offset]) {
343 r = Stdlib.fwrite (buf, (ulong) 1, (ulong) count, file);
345 if (r != (ulong) count)
346 UnixMarshal.ThrowExceptionForLastError ();
354 public override void Close ()
356 if (file == InvalidFileStream)
360 int r = Stdlib.fclose (file);
362 UnixMarshal.ThrowExceptionForLastError ();
366 file = InvalidFileStream;
371 GC.SuppressFinalize (this);
374 private bool canSeek = false;
375 private bool canRead = false;
376 private bool canWrite = false;
377 private bool owner = true;
378 private IntPtr file = InvalidFileStream;