#include #include #include #include #include #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), itell(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) { #ifdef HAVE_ZLIB 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 { #else { #endif 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); } } #ifdef HAVE_ZLIB 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; } } #endif ssize_t Handle::uwrite(const void * buf, size_t count) throw (GeneralException) { #ifdef HAVE_ZLIB if (z) { itell += count; // 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 { #else { #endif itell += count = ::write(h, buf, count); return count; } } ssize_t Handle::uread(void * buf, size_t count) { #ifdef HAVE_ZLIB if (z) { itell += count; // 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 { #else { #endif itell += count = ::read(h, buf, count); return count; } } off_t Handle::tell() { return itell; }