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