1 /*
2 *
3 * C++ Portable Types Library (PTypes)
4 * Version 2.1.1 Released 27-Jun-2007
5 *
6 * Copyright (C) 2001-2007 Hovik Melikyan
7 *
8 * http://www.melikyan.com/ptypes/
9 *
10 */
11
12 #include <errno.h>
13 #include <limits.h>
14
15 #ifdef WIN32
16 # include <windows.h>
17 #else
18 # include <signal.h>
19 # include <unistd.h>
20 #endif
21
22 #include "pstreams.h"
23
24
25 PTYPES_BEGIN
26
27
28 /*
29
30 Known UNIX error codes:
31
32 EPERM 1 Not owner
33 ENOENT 2 No such file or directory
34 ESRCH 3 No such process
35 EINTR 4 Interrupted system call
36 EIO 5 I/O error
37 ENXIO 6 No such device or address
38 E2BIG 7 Argument list too long
39 ENOEXEC 8 Exec format error
40 EBADF 9 Bad file number
41 ECHILD 10 No spawned processes
42 EAGAIN 11 No more processes; not enough memory; maximum nesting level reached
43 ENOMEM 12 Not enough memory
44 EACCES 13 Permission denied
45 EFAULT 14 Bad address
46 ENOTBLK 15 Block device required
47 EBUSY 16 Mount device busy
48 EEXIST 17 File exists
49 EXDEV 18 Cross-device link
50 ENODEV 19 No such device
51 ENOTDIR 20 Not a directory
52 EISDIR 21 Is a directory
53 EINVAL 22 Invalid argument
54 ENFILE 23 File table overflow
55 EMFILE 24 Too many open files
56 ENOTTY 25 Not a teletype
57 ETXTBSY 26 Text file busy
58 EFBIG 27 File too large
59 ENOSPC 28 No space left on device
60 ESPIPE 29 Illegal seek
61 EROFS 30 Read-only file system
62 EMLINK 31 Too many links
63 EPIPE 32 Broken pipe
64 EDOM 33 Math argument
65 ERANGE 34 Result too large
66 EUCLEAN 35 File system needs cleaning
67 EDEADLK 36 Resource deadlock would occur
68 EDEADLOCK 36 Resource deadlock would occur
69
70 */
71
72
73 #ifndef WIN32
74
75 static class _io_init
76 {
77 public:
78 _io_init();
79 } _io_init_inst;
80
81
_io_init()82 _io_init::_io_init()
83 {
84 // We don't like broken pipes. PTypes will throw an exception instead.
85 signal(SIGPIPE, SIG_IGN);
86 }
87
88 #endif
89
90
91
unixerrno()92 int ptdecl unixerrno()
93 {
94 #ifdef WIN32
95 switch(GetLastError())
96 {
97 case ERROR_FILE_NOT_FOUND:
98 case ERROR_PATH_NOT_FOUND: return ENOENT;
99 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
100 case ERROR_ACCESS_DENIED:
101 case ERROR_SHARING_VIOLATION: return EACCES;
102 case ERROR_INVALID_HANDLE: return EBADF;
103 case ERROR_NOT_ENOUGH_MEMORY:
104 case ERROR_OUTOFMEMORY: return ENOMEM;
105 case ERROR_INVALID_DRIVE: return ENODEV;
106 case ERROR_WRITE_PROTECT: return EROFS;
107 case ERROR_FILE_EXISTS: return EEXIST;
108 case ERROR_BROKEN_PIPE: return EPIPE;
109 case ERROR_DISK_FULL: return ENOSPC;
110 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
111 default: return EIO;
112 }
113 #else
114 return errno;
115 #endif
116 }
117
118
119 //
120 // This function gives error messages for most frequently occurring
121 // IO errors. If the function returns NULL a generic message
122 // can be given, e.g. "I/O error". See also iobase::get_errormsg()
123 //
124
unixerrmsg(int code)125 const char* ptdecl unixerrmsg(int code)
126 {
127 switch(code)
128 {
129 case EBADF: return "Invalid file descriptor";
130 case ESPIPE: return "Can not seek on this device";
131 case ENOENT: return "No such file or directory";
132 case EMFILE: return "Too many open files";
133 case EACCES: return "Access denied";
134 case ENOMEM: return "Not enough memory";
135 case ENODEV: return "No such device";
136 case EROFS: return "Read-only file system";
137 case EEXIST: return "File already exists";
138 case ENOSPC: return "Disk full";
139 case EPIPE: return "Broken pipe";
140 case EFBIG: return "File too large";
141 default: return nil;
142 }
143 }
144
145
estream(iobase * ierrstm,int icode,const char * imsg)146 estream::estream(iobase* ierrstm, int icode, const char* imsg)
147 : exception(imsg), code(icode), errstm(ierrstm) {}
148
149
estream(iobase * ierrstm,int icode,const string & imsg)150 estream::estream(iobase* ierrstm, int icode, const string& imsg)
151 : exception(imsg), code(icode), errstm(ierrstm) {}
152
153
~estream()154 estream::~estream() {}
155
156
157 int defbufsize = 8192;
158 int stmbalance = 0;
159
iobase(int ibufsize)160 iobase::iobase(int ibufsize)
161 : component(), active(false), cancelled(false), eof(true),
162 handle(invhandle), abspos(0), bufsize(0), bufdata(nil), bufpos(0), bufend(0),
163 stmerrno(0), deferrormsg(), status(IO_CREATED), onstatus(nil)
164 {
165 if (ibufsize < 0)
166 bufsize = defbufsize;
167 else
168 bufsize = ibufsize;
169 }
170
171
~iobase()172 iobase::~iobase()
173 {
174 }
175
176
bufalloc()177 void iobase::bufalloc()
178 {
179 if (bufdata != nil)
180 fatal(CRIT_FIRST + 13, "(ptypes internal) invalid buffer allocation");
181 bufdata = (char*)memalloc(bufsize);
182 }
183
184
buffree()185 void iobase::buffree()
186 {
187 bufclear();
188 memfree(bufdata);
189 bufdata = 0;
190 }
191
192
chstat(int newstat)193 void iobase::chstat(int newstat)
194 {
195 status = newstat;
196 if (onstatus != nil)
197 (*onstatus)(this, newstat);
198 }
199
200
errstminactive()201 void iobase::errstminactive()
202 {
203 error(EIO, "Stream inactive");
204 }
205
206
errbufrequired()207 void iobase::errbufrequired()
208 {
209 fatal(CRIT_FIRST + 11, "Internal: buffer required");
210 }
211
212
convertoffset(large offs)213 int iobase::convertoffset(large offs)
214 {
215 if (offs < 0 || offs > INT_MAX)
216 error(EFBIG, "File offset value too large");
217 return (int)offs;
218 }
219
220
open()221 void iobase::open()
222 {
223 cancel();
224 chstat(IO_OPENING);
225 abspos = 0;
226 cancelled = false;
227 eof = false;
228 stmerrno = 0;
229 clear(deferrormsg);
230 active = true;
231 stmbalance++;
232 bufalloc();
233 doopen();
234 chstat(IO_OPENED);
235 }
236
237
close()238 void iobase::close()
239 {
240 if (!active)
241 return;
242 stmbalance--;
243 try
244 {
245 if (bufdata != 0 && !cancelled)
246 flush();
247 doclose();
248 }
249 catch(estream* e)
250 {
251 delete e;
252 }
253 buffree();
254 active = false;
255 eof = true;
256 chstat(IO_CLOSED);
257 }
258
259
cancel()260 void iobase::cancel()
261 {
262 cancelled = true;
263 close();
264 }
265
266
seekx(large newpos,ioseekmode mode)267 large iobase::seekx(large newpos, ioseekmode mode)
268 {
269 if (!active)
270 errstminactive();
271 flush();
272 large ret = doseek(newpos, mode);
273 if (ret < 0)
274 error(ESPIPE, "Seek failed");
275 bufclear();
276 eof = false;
277 abspos = ret;
278 return ret;
279 }
280
281
flush()282 void iobase::flush()
283 {
284 }
285
286
doseek(large newpos,ioseekmode mode)287 large iobase::doseek(large newpos, ioseekmode mode)
288 {
289 if (handle == invhandle)
290 {
291 error(ESPIPE, "Can't seek on this device");
292 return -1;
293 }
294 #ifdef WIN32
295 static int wmode[3] = {FILE_BEGIN, FILE_CURRENT, FILE_END};
296 LARGE_INTEGER li;
297 li.QuadPart = newpos;
298 li.LowPart = SetFilePointer(HANDLE(handle), li.LowPart, &li.HighPart, wmode[mode]);
299 if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
300 return -1;
301 return li.QuadPart;
302 #else
303 static int umode[3] = {SEEK_SET, SEEK_CUR, SEEK_END};
304 return lseek(handle, newpos, umode[mode]);
305 #endif
306 }
307
308
doclose()309 void iobase::doclose()
310 {
311 #ifdef WIN32
312 CloseHandle(HANDLE(pexchange(&handle, invhandle)));
313 #else
314 ::close(pexchange(&handle, invhandle));
315 #endif
316 }
317
318
set_active(bool newval)319 void iobase::set_active(bool newval)
320 {
321 if (newval != active)
322 if (newval)
323 open();
324 else
325 close();
326 }
327
328
set_bufsize(int newval)329 void iobase::set_bufsize(int newval)
330 {
331 if (active)
332 fatal(CRIT_FIRST + 12, "Cannot change buffer size while stream is active");
333 if (newval < 0)
334 bufsize = defbufsize;
335 else
336 bufsize = newval;
337 }
338
339
get_errstmname()340 string iobase::get_errstmname()
341 {
342 return get_streamname();
343 }
344
345
uerrmsg(int code)346 const char* iobase::uerrmsg(int code)
347 {
348 return unixerrmsg(code);
349 }
350
351
uerrno()352 int iobase::uerrno()
353 {
354 return unixerrno();
355 }
356
357
get_errormsg()358 string iobase::get_errormsg()
359 {
360 string s = uerrmsg(stmerrno);
361 if (isempty(s))
362 s = deferrormsg;
363 if (pos('[', s) >= 0 && *(pconst(s) + length(s) - 1) == ']')
364 return s;
365 string e = get_errstmname();
366 if (isempty(e))
367 return s;
368 return s + " [" + e + ']';
369 }
370
371
372 #ifdef _MSC_VER
373 // disable "unreachable code" warning for throw (known compiler bug)
374 # pragma warning (disable: 4702)
375 #endif
376
error(int code,const char * defmsg)377 void iobase::error(int code, const char* defmsg)
378 {
379 eof = true;
380 stmerrno = code;
381 deferrormsg = defmsg;
382 throw new estream(this, code, get_errormsg());
383 }
384
385
386 PTYPES_END
387