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