]> git.tdb.fi Git - libs/core.git/blob - source/io/file.cpp
Rework exceptions for IO
[libs/core.git] / source / io / file.cpp
1 #ifndef WIN32
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5 #endif
6 #include <msp/core/systemerror.h>
7 #include <msp/strings/formatter.h>
8 #include "file.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace IO {
14
15 File::File(const string &fn, Mode m, CreateMode cm)
16 {
17         if(!(m&M_RDWR))
18                 throw invalid_argument("File::File mode");
19         if(cm&~(C_CREATE|C_TRUNCATE))
20                 throw invalid_argument("File::File create");
21
22         mode = m;
23
24 #ifdef WIN32
25         int flags = 0;
26         int create_flags = OPEN_EXISTING;
27
28         if(mode&M_READ)
29                 flags |= GENERIC_READ;
30         else if(mode&M_WRITE)
31         {
32                 flags |= GENERIC_WRITE;
33
34                 switch(static_cast<int>(cm))
35                 {
36                 case C_NONE:     create_flags = OPEN_EXISTING; break;
37                 case C_CREATE:   create_flags = OPEN_ALWAYS; break;
38                 case C_TRUNCATE: create_flags = TRUNCATE_EXISTING; break;
39                 case C_CREATE+C_TRUNCATE: create_flags = CREATE_ALWAYS; break;
40                 }
41         }
42
43         handle = CreateFile(fn.c_str(), flags, 0, 0, create_flags, FILE_ATTRIBUTE_NORMAL, 0);
44         if(handle==INVALID_HANDLE_VALUE)
45         {
46                 int err = GetLastError();
47                 if(err==ERROR_FILE_NOT_FOUND)
48                         throw file_not_found(fn);
49                 else
50                         throw system_error(format("CreateFile(%s)", fn), err);
51         }
52 #else
53         int flags = 0;
54         switch(mode&M_RDWR)
55         {
56         case M_READ:  flags |= O_RDONLY; break;
57         case M_WRITE: flags |= O_WRONLY; break;
58         case M_RDWR:  flags |= O_RDWR; break;
59         default:;
60         }
61
62         if(mode&M_WRITE)
63         {
64                 if(cm&C_CREATE)
65                         flags |= O_CREAT;
66                 if(cm&C_TRUNCATE)
67                         flags |= O_TRUNC;
68         }
69         if(mode&M_APPEND)
70                 flags |= O_APPEND;
71         if(mode&M_NONBLOCK)
72                 flags |= O_NONBLOCK;
73
74         handle = ::open(fn.c_str(), flags, 0666);
75         if(handle==-1)
76         {
77                 int err = errno;
78                 if(err==ENOENT)
79                         throw file_not_found(fn);
80                 else
81                         throw system_error(format("open(%s)", fn), err);
82         }
83 #endif
84
85         set_events(P_INPUT);
86 }
87
88 File::~File()
89 {
90         close();
91 }
92
93 void File::close()
94 {
95         if(handle==MSP_IO_INVALID_HANDLE)
96                 return;
97
98         set_events(P_NONE);
99
100         signal_flush_required.emit();
101
102 #ifdef WIN32
103         CloseHandle(handle);
104 #else
105         ::close(handle);
106 #endif
107
108         handle = MSP_IO_INVALID_HANDLE;
109         signal_closed.emit();
110 }
111
112 void File::set_block(bool b)
113 {
114         check_access(M_NONE);
115
116         mode = (mode&~M_NONBLOCK);
117         if(b)
118                 mode = (mode|M_NONBLOCK);
119 #ifndef WIN32
120         int flags = fcntl(handle, F_GETFD);
121         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
122 #endif
123 }
124
125 unsigned File::do_write(const char *buf, unsigned size)
126 {
127         check_access(M_WRITE);
128
129         if(size==0)
130                 return 0;
131
132 #ifdef WIN32
133         if(mode&M_APPEND)
134                 seek(0, S_END);
135         DWORD ret;
136         if(WriteFile(handle, buf, size, &ret, 0)==0)
137                 throw system_error("WriteFile");
138 #else
139         int ret = ::write(handle, buf, size);
140         if(ret==-1)
141         {
142                 if(errno==EAGAIN)
143                         return 0;
144                 else
145                         throw system_error("write");
146         }
147 #endif
148
149         return ret;
150 }
151
152 unsigned File::do_read(char *buf, unsigned size)
153 {
154         check_access(M_READ);
155
156         if(size==0)
157                 return 0;
158
159 #ifdef WIN32
160         DWORD ret;
161         if(ReadFile(handle, buf, size, &ret, 0)==0)
162                 throw system_error("ReadFile");
163 #else
164         int ret = ::read(handle, buf, size);
165         if(ret==-1)
166         {
167                 if(errno==EAGAIN)
168                         return 0;
169                 else
170                         throw system_error("read");
171         }
172 #endif
173
174         if(ret==0)
175         {
176                 eof_flag = true;
177                 signal_end_of_file.emit();
178         }
179
180         return ret;
181 }
182
183 void File::sync()
184 {
185 #ifndef WIN32
186         signal_flush_required.emit();
187
188         fsync(handle);
189 #endif
190 }
191
192 int File::seek(int off, SeekType st)
193 {
194         check_access(M_NONE);
195
196         signal_flush_required.emit();
197
198         int type = sys_seek_type(st);
199 #ifdef WIN32
200         DWORD ret = SetFilePointer(handle, off, 0, type);
201         if(ret==INVALID_SET_FILE_POINTER)
202                 throw system_error("SetFilePointer");
203 #else
204         int ret = lseek(handle, off, type);
205         if(ret==-1)
206                 throw system_error("lseek");
207 #endif
208
209         eof_flag = false;
210
211         return ret;
212 }
213
214 int File::tell() const
215 {
216         check_access(M_NONE);
217
218 #ifdef WIN32
219         DWORD ret = SetFilePointer(handle, 0, 0, FILE_CURRENT);
220         if(ret==INVALID_SET_FILE_POINTER)
221                 throw system_error("SetFilePointer");
222 #else
223         int ret = lseek(handle, 0, SEEK_CUR);
224         if(ret==-1)
225                 throw system_error("lseek");
226 #endif
227
228         return ret;
229 }
230
231 void File::check_access(Mode m) const
232 {
233         if(handle==MSP_IO_INVALID_HANDLE || (m && !(mode&m)))
234                 throw invalid_access(m);
235 }
236
237 } // namespace IO
238 } // namespace Msp