diff options
Diffstat (limited to 'generic/Handle.cpp')
-rw-r--r-- | generic/Handle.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/generic/Handle.cpp b/generic/Handle.cpp new file mode 100644 index 0000000..8b7a278 --- /dev/null +++ b/generic/Handle.cpp @@ -0,0 +1,269 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include "Handle.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#else +#define _(x) x +#endif + +Handle::Handle(const Handle & nh) : h(nh.h >= 0 ? dup(nh.h) : nh.h), closed(nh.closed), nonblock(nh.closed), zfile(0), z(0) { +// cerr << "Duplication of handle " << nh.h << " to " << h << endl; + if ((h >= 0) && (nh.z)) { + SetZ(nh.z); + } +} + +Handle::~Handle() { +// cerr << "Destroying handle " << h << endl; + close(); +} + +Handle::Handle(int nh) : h(nh), closed(false), nonblock(false), zfile(0), z(0) { +// cerr << "Initialising handle " << h << endl; +} + +int Handle::GetHandle() { + return h; +} + +ssize_t Handle::write(const void *buf, size_t count) throw (GeneralException) { + ssize_t r, tr = 0; + bool done, full = false; + + do { + done = true; + errno = 0; + if ((r = uwrite(buf, count)) < 0) { + if ((!errno) || (errno == EAGAIN) || (errno == EINTR)) { + // Avant de déclarer une erreur, on vérifie si ce n'est pas un + // problème lié au fait qu'il n'y a plus de place libre. Cela peut + // arriver si l'on agit sur un pipe ou un handle. Nous + // attendons encore une fois avant de déclarer l'erreur, + // grace au drapeau full. + if (full) { + throw IOException(GetName(), IO_WRITE, count); + } else { + done = false; + full = true; + if (nonblock) { +// cerr << "write: throwing IOAgain for handle " << GetName() << endl; + throw IOAgain(); + } else { + sleep(1); + } + } + } else { + throw IOException(GetName(), IO_WRITE, count); + } + } else if (((size_t) r) != count) { + if (nonblock) { + return r; + } + full = done = false; + ((char *)buf) += r; + tr += r; + } + } while (!done); + + return r + tr; +} + +ssize_t Handle::read(void *buf, size_t count) throw (GeneralException) { + ssize_t r; + + errno = 0; + while ((r = uread(buf, count)) < 0) { + if ((!errno) || (errno == EAGAIN) || (errno == EINTR)) { + // Avant de déclarer une erreur, on vérifie si ce n'est pas un + // problème lié au fait qu'il n'y a plus d'octets. + if (nonblock) { +// cerr << "read: throwing IOAgain for handle " << GetName() << endl; + throw IOAgain(); + } + } else { + throw IOException(GetName(), IO_READ, count); + } + } + + if (!r) { + close(); + } + + return r; +} + +bool Handle::IsClosed(void) { + return closed; +} + +bool Handle::IsNonBlock(void) { + return nonblock; +} + +void Handle::SetNonBlock(void) { + if ((h >= 0) || !nonblock) { + fcntl(h, F_SETFL, O_NONBLOCK); + } + nonblock = true; +} + +Handle & operator<<(Handle & h, const String & s) { + const char * p; + + p = s.to_charp(); + h.write(p, strlen(p)); + + return h; +} + +Handle & operator>>(Handle & h, String & s) { + char t[BUFSIZ]; + int i = 0, r; + + while ((r = h.read(&(t[i]), 1)) && (i != (BUFSIZ - 1))) { + // Il y a souvent des \r\n dans les sockets par exemple, + // ou bien en lisant des fichiers au format MS-DOS. On + // ignore le \r pour ne garder que le \n, standard sous Unix. + if (t[i] == '\r') { + continue; + } + if (t[i] == '\n') { + break; + } else { + i++; + } + } + + t[i] = '\0'; + s = t; + return h; +} + +void Handle::close() throw (GeneralException) { + if (IsClosed()) { + return; + } + + if (h >= 0) { + if (z) { +// cerr << "Performing gzclose on handle " << h << endl; + int err = gzclose(zfile); +// cerr << "gzclose returned " << err << endl; + if (err) { + if (err == Z_ERRNO) { + throw GeneralException(String(_("Error during close: ")) + strerror(errno)); + } else { + throw GeneralException(_("Error in zlib during gzclose.")); + } + } + } else { + int err = ::close(h); + if (err) { + throw GeneralException(String(_("Error during close: ")) + strerror(errno)); + } + } + } + + h = -1; + + closed = 1; +} + +bool Handle::CanRead(void) { + return false; +} + +bool Handle::CanWrite(void) { + return false; +} + +String Handle::GetName(void) { + return _("Bare Handle - should not happend"); +} + +ssize_t Handle::GetSize(void) { + return -1; +} + +time_t Handle::GetModif(void) { + return -1; +} + +bool Handle::CanWatch(void) { + return true; +} + +void Handle::Dup(const Handle & H) { + close(); + if (H.h >= 0) { + h = dup(H.h); + } +} + +void Handle::SetZ(int az) throw (GeneralException) { + if (z) { + throw GeneralException(_("Can't SetZ a Handle twice.")); + } + if (h < 0) { + throw GeneralException(_("Can't SetZ a virtual Handle.")); + } + if (az) { + char format[4]; + int index = 0; + if (CanRead()) { + format[index++] = 'r'; + } + if (CanWrite()) { + format[index++] = 'w'; + } + format[index++] = (char) (az + '0'); + format[index] = 0; +// cerr << "Performing gzdopen on handle " << h << " with mode \"" << format << "\"\n"; + if (!(zfile = gzdopen(h, format))) { + throw GeneralException(_("Was not able to gzdopen.")); + } + z = az; + } +} + +ssize_t Handle::uwrite(const void * buf, size_t count) throw (GeneralException) { + if (z) { +// cerr << "Performing gzwrite of " << count << " byte for handle " << h << endl; + int err = gzwrite(zfile, buf, count); +// cerr << "gzwrite returned " << err << endl; + if (err == 0) { + const char * m = gzerror(zfile, &err); + if (err == Z_ERRNO) { + return -1; + } else { + throw GeneralException(String(_("Error in zlib during gzwrite: ")) + m); + } + } + return err; + } else { + return ::write(h, buf, count); + } +} + +ssize_t Handle::uread(void * buf, size_t count) { + if (z) { +// cerr << "Performing gzread of " << count << " byte for handle " << h << endl; + int err = gzread(zfile, buf, count); +// cerr << "gzwrite returned " << err << endl; + if (err == -1) { + gzerror(zfile, &err); + if (err == Z_ERRNO) { + return -1; + } else { + return 0; + } + } + return err; + } else { + return ::read(h, buf, count); + } +} |