Recognize FileNotFound on win32
[libs/core.git] / source / file.cpp
1 /* $Id$
2
3 This file is part of libmspio
4 Copyright © 2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7 #ifndef WIN32
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #endif
12 #include <msp/strings/formatter.h>
13 #include "except.h"
14 #include "file.h"
15
16 using namespace std;
17
18 namespace Msp {
19 namespace IO {
20
21 /**
22 Creates a new file object and opens it.  If the
23 create flag is true and write access is requested and the file does exist, it
24 is created.  Otherwise a missing file is an error.
25
26 @param   fn  Name of the file to open
27 @param   m   Open mode
28 @param   cm  Flags controlling creation of a new file
29 */
30 File::File(const string &fn, Mode m, CreateMode cm)
31 {
32         if(!(m&M_RDWR))
33                 throw InvalidParameterValue("Invalid read/write mode");
34         if(cm&~(C_CREATE|C_TRUNCATE))
35                 throw InvalidParameterValue("Invalid create mode");
36
37         mode=m;
38
39 #ifdef WIN32
40         int flags=0;
41         int create_flags=OPEN_EXISTING;
42
43         if(mode&M_READ)
44                 flags|=GENERIC_READ;
45         else if(mode&M_WRITE)
46         {
47                 flags|=GENERIC_WRITE;
48
49                 switch(static_cast<int>(cm))
50                 {
51                 case C_NONE:     create_flags=OPEN_EXISTING; break;
52                 case C_CREATE:   create_flags=OPEN_ALWAYS; break;
53                 case C_TRUNCATE: create_flags=TRUNCATE_EXISTING; break;
54                 case C_CREATE+C_TRUNCATE: create_flags=CREATE_ALWAYS; break;
55                 }
56         }
57
58         handle=CreateFile(fn.c_str(), flags, 0, 0, create_flags, FILE_ATTRIBUTE_NORMAL, 0);
59         if(handle==INVALID_HANDLE_VALUE)
60         {
61                 int err=GetLastError();
62                 if(err==ERROR_FILE_NOT_FOUND)
63                         throw FileNotFound("Can't find file "+fn, fn);
64                 else
65                         throw SystemError(format("Can't open file '%s'", fn), GetLastError());
66         }
67 #else
68         int flags=0;
69         switch(mode&M_RDWR)
70         {
71         case M_READ:  flags|=O_RDONLY; break;
72         case M_WRITE: flags|=O_WRONLY; break;
73         case M_RDWR:  flags|=O_RDWR; break;
74         default:;
75         }
76
77         if(mode&M_WRITE)
78         {
79                 if(cm&C_CREATE)
80                         flags|=O_CREAT;
81                 if(cm&C_TRUNCATE)
82                         flags|=O_TRUNC;
83         }
84         if(mode&M_APPEND)
85                 flags|=O_APPEND;
86         if(mode&M_NONBLOCK)
87                 flags|=O_NONBLOCK;
88
89         handle=::open(fn.c_str(), flags, 0666);
90         if(handle==-1)
91         {
92                 int err=errno;
93                 if(err==ENOENT)
94                         throw FileNotFound("Can't find file "+fn, fn);
95                 else
96                         throw SystemError(format("Can't open file '%s'", fn), err);
97         }
98 #endif
99
100         set_events(P_INPUT);
101 }
102
103 /**
104 Closes the file.  Any attempt to access the file after this will cause an
105 exception to be thrown.
106 */
107 void File::close()
108 {
109         if(handle==MSP_IO_INVALID_HANDLE)
110                 return;
111
112         set_events(P_NONE);
113
114         signal_flush_required.emit();
115
116 #ifdef WIN32
117         CloseHandle(handle);
118 #else
119         ::close(handle);
120 #endif
121
122         handle=MSP_IO_INVALID_HANDLE;
123         signal_closed.emit();
124 }
125
126 /**
127 Sets the blocking state of the file.  If blocking is disabled, all operations
128 will return immediately, even if they can't be fully completed.
129 */
130 void File::set_block(bool b)
131 {
132         check_access(M_NONE);
133
134         mode=(mode&~M_NONBLOCK);
135         if(b)
136                 mode=(mode|M_NONBLOCK);
137 #ifndef WIN32
138         int flags=fcntl(handle, F_GETFD);
139         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
140 #endif
141 }
142
143 void File::sync()
144 {
145 #ifndef WIN32
146         signal_flush_required.emit();
147
148         fsync(handle);
149 #endif
150 }
151
152 /**
153 Seeks the file to the given byte offset.
154
155 @param   off  Offset in bytes
156 @param   st   Seek type
157
158 @return  The resulting offset
159 */
160 int File::seek(int off, SeekType st)
161 {
162         check_access(M_NONE);
163
164         signal_flush_required.emit();
165
166         int type=sys_seek_type(st);
167 #ifdef WIN32
168         DWORD ret=SetFilePointer(handle, off, 0, type);
169         if(ret==INVALID_SET_FILE_POINTER)
170                 throw SystemError("Seek failed", GetLastError());
171 #else
172         int ret=lseek(handle, off, type);
173         if(ret==-1)
174                 throw SystemError("Seek failed", errno);
175 #endif
176
177         eof_flag=false;
178
179         return ret;
180 }
181
182 /**
183 Returns the current read/write offset of the file.
184 */
185 int File::tell() const
186 {
187         check_access(M_NONE);
188
189 #ifdef WIN32
190         DWORD ret=SetFilePointer(handle, 0, 0, FILE_CURRENT);
191         if(ret==INVALID_SET_FILE_POINTER)
192                 throw SystemError("Tell failed", GetLastError());
193 #else
194         int ret=lseek(handle, 0, SEEK_CUR);
195         if(ret==-1)
196                 throw SystemError("Tell failed", errno);
197 #endif
198
199         return ret;
200 }
201
202 File::~File()
203 {
204         close();
205 }
206
207 void File::check_access(Mode m) const
208 {
209         if(handle==MSP_IO_INVALID_HANDLE)
210                 throw InvalidState("File is not open");
211         if(m==M_READ && !(mode&M_READ))
212                 throw InvalidState("File is not readable");
213         if(m==M_WRITE && !(mode&M_WRITE))
214                 throw InvalidState("File is not writable");
215 }
216
217 /**
218 Writes data from a buffer to the file.
219
220 @param   buf   Buffer to write from.
221 @param   size  Length of data to write.
222
223 @return  The number of bytes written
224 */
225 unsigned File::do_write(const char *buf, unsigned size)
226 {
227         check_access(M_WRITE);
228
229         if(size==0)
230                 return 0;
231
232 #ifdef WIN32
233         if(mode&M_APPEND)
234                 seek(0, S_END);
235         DWORD ret;
236         if(WriteFile(handle, buf, size, &ret, 0)==0)
237                 throw SystemError("Writing to file failed", GetLastError());
238 #else
239         int ret=::write(handle, buf, size);
240         if(ret==-1)
241         {
242                 if(errno==EAGAIN)
243                         return 0;
244                 else
245                         throw SystemError("Writing to file failed", errno);
246         }
247 #endif
248
249         return ret;
250 }
251
252 /**
253 Reads data from the file.
254
255 @param   buf   Buffer to read data into.
256 @param   size  Maximum size of data to read.
257
258 @return  The number of bytes read, possibly zero
259 */
260 unsigned File::do_read(char *buf, unsigned size)
261 {
262         check_access(M_READ);
263
264         if(size==0)
265                 return 0;
266
267 #ifdef WIN32
268         DWORD ret;
269         if(ReadFile(handle, buf, size, &ret, 0)==0)
270                 throw SystemError("Reading from file failed", GetLastError());
271 #else
272         int ret=::read(handle, buf, size);
273         if(ret==-1)
274         {
275                 if(errno==EAGAIN)
276                         return 0;
277                 else
278                         throw SystemError("Reading from file failed", errno);
279         }
280 #endif
281
282         if(ret==0)
283         {
284                 eof_flag=true;
285                 signal_end_of_file.emit();
286         }
287
288         return ret;
289 }
290
291 } // namespace IO
292 } // namespace Msp