* Stdlib.cs: sys_* functions shouldn't be public.
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / StdioFileStream.cs
1 //
2 // Mono.Unix/StdioFileStream.cs
3 //
4 // Authors:
5 //   Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2005 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.
27 //
28
29 using System;
30 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Text;
33 using Mono.Unix;
34
35 namespace Mono.Unix {
36
37         public class StdioFileStream : Stream, IDisposable
38         {
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;
43
44                 public StdioFileStream (IntPtr fileStream)
45                         : this (fileStream, true) {}
46
47                 public StdioFileStream (IntPtr fileStream, bool ownsHandle)
48                 {
49                         if (InvalidFileStream == fileStream)
50                                 throw new ArgumentException (Locale.GetText ("Invalid file stream"), "fileStream");
51                         
52                         this.file = fileStream;
53                         this.owner = ownsHandle;
54                         
55                         long offset = Stdlib.fseek (file, 0, SeekFlags.SEEK_CUR);
56                         if (offset != -1)
57                                 canSeek = true;
58                         Stdlib.fread (IntPtr.Zero, 0, 0, file);
59                         if (Stdlib.ferror (file) == 0)
60                                 canRead = true;
61                         Stdlib.fwrite (IntPtr.Zero, 0, 0, file);
62                         if (Stdlib.ferror (file) == 0)
63                                 canWrite = true;  
64                         Stdlib.clearerr (file);
65                 }
66
67                 private void AssertNotDisposed ()
68                 {
69                         if (file == InvalidFileStream)
70                                 throw new ObjectDisposedException ("Invalid File Stream");
71                 }
72
73                 public IntPtr FileStream {
74                         get {return file;}
75                 }
76
77                 public override bool CanRead {
78                         get {return canRead;}
79                 }
80
81                 public override bool CanSeek {
82                         get {return canSeek;}
83                 }
84
85                 public override bool CanWrite {
86                         get {return canWrite;}
87                 }
88
89                 public override long Length {
90                         get {
91                                 AssertNotDisposed ();
92                                 if (!CanSeek)
93                                         throw new NotSupportedException ("File Stream doesn't support seeking");
94                                 long curPos = Stdlib.ftell (file);
95                                 if (curPos == -1)
96                                         throw new NotSupportedException ("Unable to obtain current file position");
97                                 int r = Stdlib.fseek (file, 0, SeekFlags.SEEK_END);
98                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
99
100                                 long endPos = Stdlib.ftell (file);
101                                 if (endPos == -1)
102                                         UnixMarshal.ThrowExceptionForLastError ();
103
104                                 r = Stdlib.fseek (file, curPos, SeekFlags.SEEK_SET);
105                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
106
107                                 return endPos - curPos;
108                         }
109                 }
110
111                 public override long Position {
112                         get {
113                                 AssertNotDisposed ();
114                                 if (!CanSeek)
115                                         throw new NotSupportedException ("The stream does not support seeking");
116                                 long pos = Stdlib.ftell (file);
117                                 if (pos == -1)
118                                         UnixMarshal.ThrowExceptionForLastError ();
119                                 return (long) pos;
120                         }
121                         set {
122                                 Seek (value, SeekOrigin.Begin);
123                         }
124                 }
125
126                 public FilePosition FilePosition {
127                         get {
128                                 FilePosition pos = new FilePosition ();
129                                 int r = Stdlib.fgetpos (file, pos);
130                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
131                                 return pos;
132                         }
133                         set {
134                                 if (value == null)
135                                         throw new ArgumentNullException ("value");
136                                 int r = Stdlib.fsetpos (file, value);
137                                 UnixMarshal.ThrowExceptionForLastErrorIf (r);
138                         }
139                 }
140
141                 public override void Flush ()
142                 {
143                         int r = Stdlib.fflush (file);
144                         if (r != 0)
145                                 UnixMarshal.ThrowExceptionForLastError ();
146                 }
147
148                 public override unsafe int Read ([In, Out] byte[] buffer, int offset, int count)
149                 {
150                         AssertNotDisposed ();
151                         AssertValidBuffer (buffer, offset, count);
152                         if (!CanRead)
153                                 throw new NotSupportedException ("Stream does not support reading");
154                                  
155                         ulong r = 0;
156                         fixed (byte* buf = &buffer[offset]) {
157                                 r = Stdlib.fread (buf, 1, (ulong) count, file);
158                         }
159                         if (r != (ulong) count) {
160                                 if (Stdlib.feof (file) != 0)
161                                         throw new EndOfStreamException ();
162                                 if (Stdlib.ferror (file) != 0)
163                                         throw new IOException ();
164                         }
165                         return (int) r;
166                 }
167
168                 private void AssertValidBuffer (byte[] buffer, int offset, int count)
169                 {
170                         if (buffer == null)
171                                 throw new ArgumentNullException ("buffer");
172                         if (offset < 0)
173                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
174                         if (count < 0)
175                                 throw new ArgumentOutOfRangeException ("count", "< 0");
176                         if (offset > buffer.Length)
177                                 throw new ArgumentException ("destination offset is beyond array size");
178                         if (offset > (buffer.Length - count))
179                                 throw new ArgumentException ("would overrun buffer");
180                 }
181
182                 public void Rewind ()
183                 {
184                         Stdlib.rewind (file);
185                 }
186
187                 public override long Seek (long offset, SeekOrigin origin)
188                 {
189                         AssertNotDisposed ();
190                         if (!CanSeek)
191                                 throw new NotSupportedException ("The File Stream does not support seeking");
192
193                         SeekFlags sf = SeekFlags.SEEK_CUR;
194                         switch (origin) {
195                                 case SeekOrigin.Begin:   sf = SeekFlags.SEEK_SET; break;
196                                 case SeekOrigin.Current: sf = SeekFlags.SEEK_CUR; break;
197                                 case SeekOrigin.End:     sf = SeekFlags.SEEK_END; break;
198                         }
199
200                         int r = Stdlib.fseek (file, offset, sf);
201                         if (r != 0)
202                                 UnixMarshal.ThrowExceptionForLastError ();
203
204                         long pos = Stdlib.ftell (file);
205                         if (pos == -1)
206                                 UnixMarshal.ThrowExceptionForLastError ();
207
208                         return pos;
209                 }
210
211                 public override void SetLength (long value)
212                 {
213                         throw new NotSupportedException ("ANSI C doesn't provide a way to truncate a file");
214                 }
215
216                 public override unsafe void Write (byte[] buffer, int offset, int count)
217                 {
218                         AssertNotDisposed ();
219                         AssertValidBuffer (buffer, offset, count);
220                         if (!CanWrite)
221                                 throw new NotSupportedException ("File Stream does not support writing");
222
223                         ulong r = 0;
224                         fixed (byte* buf = &buffer[offset]) {
225                                 r = Stdlib.fwrite (buf, (ulong) 1, (ulong) count, file);
226                         }
227                         if (r != (ulong) count)
228                                 UnixMarshal.ThrowExceptionForLastError ();
229                 }
230                 
231                 ~StdioFileStream ()
232                 {
233                         Close ();
234                 }
235
236                 public override void Close ()
237                 {
238                         if (file == InvalidFileStream)
239                                 return;
240                                 
241                         Flush ();
242                         int r = Stdlib.fclose (file);
243                         if (r != 0)
244                                 UnixMarshal.ThrowExceptionForLastError ();
245                         file = InvalidFileStream;
246                 }
247                 
248                 void IDisposable.Dispose ()
249                 {
250                         AssertNotDisposed ();
251                         GC.SuppressFinalize (this);
252                         if (owner) {
253                                 Close ();
254                         }
255                 }
256
257                 private bool canSeek  = false;
258                 private bool canRead  = false;
259                 private bool canWrite = false;
260                 private bool owner    = true;
261                 private IntPtr file   = InvalidFileStream;
262         }
263 }
264
265 // vim: noexpandtab