a22b832be46a4f56179544fa96cc2cc0d8de331b
[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                 throw SystemError(format("Can't open file '%s'", fn), GetLastError());
61 #else
62         int flags=0;
63         switch(mode&M_RDWR)
64         {
65         case M_READ:  flags|=O_RDONLY; break;
66         case M_WRITE: flags|=O_WRONLY; break;
67         case M_RDWR:  flags|=O_RDWR; break;
68         default:;
69         }
70
71         if(mode&M_WRITE)
72         {
73                 if(cm&C_CREATE)
74                         flags|=O_CREAT;
75                 if(cm&C_TRUNCATE)
76                         flags|=O_TRUNC;
77         }
78         if(mode&M_APPEND)
79                 flags|=O_APPEND;
80         if(mode&M_NONBLOCK)
81                 flags|=O_NONBLOCK;
82
83         handle=::open(fn.c_str(), flags, 0666);
84         if(handle==-1)
85         {
86                 int err=errno;
87                 if(err==ENOENT)
88                         throw FileNotFound("Can't find file "+fn, fn);
89                 else
90                         throw SystemError(format("Can't open file '%s'", fn), err);
91         }
92 #endif
93
94         set_events(P_INPUT);
95 }
96
97 /**
98 Closes the file.  Any attempt to access the file after this will cause an
99 exception to be thrown.
100 */
101 void File::close()
102 {
103         if(handle==MSP_IO_INVALID_HANDLE)
104                 return;
105
106         set_events(P_NONE);
107
108         signal_closing.emit();
109
110 #ifdef WIN32
111         CloseHandle(handle);
112 #else
113         ::close(handle);
114 #endif
115
116         handle=MSP_IO_INVALID_HANDLE;
117         signal_closed.emit();
118 }
119
120 /**
121 Sets the blocking state of the file.  If blocking is disabled, all operations
122 will return immediately, even if they can't be fully completed.
123 */
124 void File::set_block(bool b)
125 {
126         check_access(M_NONE);
127
128         mode=(mode&~M_NONBLOCK);
129         if(b)
130                 mode=(mode|M_NONBLOCK);
131 #ifndef WIN32
132         int flags=fcntl(handle, F_GETFD);
133         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
134 #endif
135 }
136
137 /**
138 Seeks the file to the given byte offset.
139
140 @param   off  Offset in bytes
141 @param   st   Seek type
142
143 @return  The resulting offset
144 */
145 int File::seek(int off, SeekType st)
146 {
147         check_access(M_NONE);
148
149         int type=sys_seek_type(st);
150 #ifdef WIN32
151         DWORD ret=SetFilePointer(handle, off, 0, type);
152         if(ret==INVALID_SET_FILE_POINTER)
153                 throw SystemError("Seek failed", GetLastError());
154 #else
155         int ret=lseek(handle, off, type);
156         if(ret==-1)
157                 throw SystemError("Seek failed", errno);
158 #endif
159
160         eof_flag=false;
161
162         return ret;
163 }
164
165 /**
166 Returns the current read/write offset of the file.
167 */
168 int File::tell() const
169 {
170         check_access(M_NONE);
171
172 #ifdef WIN32
173         DWORD ret=SetFilePointer(handle, 0, 0, FILE_CURRENT);
174         if(ret==INVALID_SET_FILE_POINTER)
175                 throw SystemError("Tell failed", GetLastError());
176 #else
177         int ret=lseek(handle, 0, SEEK_CUR);
178         if(ret==-1)
179                 throw SystemError("Tell failed", errno);
180 #endif
181
182         return ret;
183 }
184
185 File::~File()
186 {
187         close();
188 }
189
190 void File::check_access(Mode m) const
191 {
192         if(handle==MSP_IO_INVALID_HANDLE)
193                 throw InvalidState("File is not open");
194         if(m==M_READ && !(mode&M_READ))
195                 throw InvalidState("File is not readable");
196         if(m==M_WRITE && !(mode&M_WRITE))
197                 throw InvalidState("File is not writable");
198 }
199
200 /**
201 Writes data from a buffer to the file.
202
203 @param   buf   Buffer to write from.
204 @param   size  Length of data to write.
205
206 @return  The number of bytes written
207 */
208 unsigned File::do_write(const char *buf, unsigned size)
209 {
210         check_access(M_WRITE);
211
212         if(size==0)
213                 return 0;
214
215 #ifdef WIN32
216         if(mode&M_APPEND)
217                 seek(0, S_END);
218         DWORD ret;
219         if(WriteFile(handle, buf, size, &ret, 0)==0)
220                 throw SystemError("Writing to file failed", GetLastError());
221 #else
222         int ret=::write(handle, buf, size);
223         if(ret==-1)
224         {
225                 if(errno==EAGAIN)
226                         return 0;
227                 else
228                         throw SystemError("Writing to file failed", errno);
229         }
230 #endif
231
232         return ret;
233 }
234
235 void File::sync()
236 {
237 #ifndef WIN32
238         fsync(handle);
239 #endif
240 }
241
242 /**
243 Reads data from the file.
244
245 @param   buf   Buffer to read data into.
246 @param   size  Maximum size of data to read.
247
248 @return  The number of bytes read, possibly zero
249 */
250 unsigned File::do_read(char *buf, unsigned size)
251 {
252         check_access(M_READ);
253
254         if(size==0)
255                 return 0;
256
257 #ifdef WIN32
258         DWORD ret;
259         if(ReadFile(handle, buf, size, &ret, 0)==0)
260                 throw SystemError("Reading from file failed", GetLastError());
261 #else
262         int ret=::read(handle, buf, size);
263         if(ret==-1)
264         {
265                 if(errno==EAGAIN)
266                         return 0;
267                 else
268                         throw SystemError("Reading from file failed", errno);
269         }
270 #endif
271
272         if(ret==0)
273         {
274                 eof_flag=true;
275                 signal_end_of_file.emit();
276         }
277
278         return ret;
279 }
280
281 } // namespace IO
282 } // namespace Msp