#include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "generic.h" #if defined WORDS_BIGENDIAN #if defined HAVE_BYTESWAP_H #include #else inline Uint16 bswap_16(Uint16 w) { return (w >> 8) | (w << 8); } inline Uint32 bswap_32(Uint32 w) { return (w >> 24) | ((w >> 8) & 0x0000ff00) | ((w << 8) & 0x00ff0000) | (w << 24); } #endif #endif #ifdef HAVE_UNISTD_H #include #else #include #endif #include "Handle.h" #include "gettext.h" enum { DEFLATE, INFLATE }; Handle::Handle(const Handle & nh) : itell(0), h(nh.h >= 0 ? dup(nh.h) : nh.h), closed(nh.closed), nonblock(nh.closed), zfile(0), z(0) { #ifdef DEBUG std::cerr << _("Duplication of handle ") << nh.h << _(" to ") << h << std::endl; #endif if ((h >= 0) && (nh.z)) { SetZ(nh.z); } } Handle::~Handle() { #ifdef DEBUG std::cerr << _("Destroying handle ") << h << std::endl; #endif close(); } Handle::Handle(int nh) : h(nh), closed(false), nonblock(false), zfile(0), z(0) { #ifdef DEBUG std::cerr << _("Initialising handle ") << h << std::endl; #endif } int Handle::GetHandle() { return h; } int Handle::GetHandle() const { return h; } ssize_t Handle::write(const void *cbuf, size_t count) throw (GeneralException) { ssize_t r, tr = 0; bool done, full = false; const char * buf = (const char *)cbuf; 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) { #ifdef DEBUG std::cerr << _("write: throwing IOAgain for handle ") << GetName() << std::endl; #endif throw IOAgain(); } else { #ifdef HAVE_SLEEP sleep(1); #endif } } } else { throw IOException(GetName(), IO_WRITE, count); } } else if (((size_t) r) != count) { if (nonblock) { return r; } full = done = false; buf += r; tr += r; } } while (!done); return r + tr; } ssize_t Handle::read(void *buf, size_t count) throw (GeneralException) { ssize_t r; #ifdef FULLDEBUG std::cerr << _("read: reading ") << count << _(" bytes from handle ") << GetHandle() << std::endl; #endif 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) { #ifdef DEBUG std::cerr << _("read: throwing IOAgain for handle ") << GetName() << std::endl; #endif throw IOAgain(); } } else { throw IOException(GetName(), IO_READ, count); } } if (!r) { close(); } return r; } bool Handle::IsClosed(void) const { return closed; } bool Handle::IsNonBlock(void) const { return nonblock; } void Handle::SetNonBlock(void) { #ifdef HAVE_FCNTL if ((h >= 0) || !nonblock) { fcntl(h, F_SETFL, O_NONBLOCK); } nonblock = true; #endif } 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; } Flush(); if (h >= 0) { if (z >= 10) { int err; if (c == DEFLATE) { err = deflateEnd(&zstrm); if (err != Z_OK) { throw (String(_("Error during deflateEnd: ")) + zstrm.msg); } } else { err = inflateEnd(&zstrm); if (err != Z_OK) { throw (String(_("Error during inflateEnd: ")) + zstrm.msg); } } err = ::close(h); if (err) { throw GeneralException(String(_("Error during (zstream) close: ")) + strerror(errno)); } } else if (z) { #ifdef DEBUG std::cerr << _("Performing gzclose on handle ") << h << std::endl; #endif int err = gzclose(zfile); #ifdef DEBUG std::cerr << _("gzclose returned ") << err << std::endl; #endif if (err) { if (err == Z_ERRNO) { throw GeneralException(String(_("Error during close: ")) + strerror(errno)); } else { throw GeneralException(String(_("Error in zlib during gzclose: ")) + gzerror(zfile, &err)); } } } else { int err = ::close(h); if (err) { throw GeneralException(String(_("Error during close: ")) + strerror(errno)); } } } h = -1; closed = 1; } bool Handle::CanRead(void) const { return false; } bool Handle::CanWrite(void) const { return false; } String Handle::GetName(void) const { return _("Bare Handle - should not happend"); } ssize_t Handle::GetSize(void) const { return -1; } time_t Handle::GetModif(void) const { return -1; } bool Handle::CanWatch(void) const { return true; } int Handle::Dup() const { return dup(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 >= 10) { #ifdef DEBUG std::cerr << _("Setting up zstream using inflate/deflate...\n"); #endif int err; zstrm.zalloc = Z_NULL; zstrm.zfree = Z_NULL; if (CanWrite()) { c = DEFLATE; err = deflateInit(&zstrm, az - 10); if (err != Z_OK) { throw GeneralException(String(_("Error in deflateInit: ")) + zstrm.msg); } } else { c = INFLATE; zstrm.next_in = 0; zstrm.avail_in = 0; err = inflateInit(&zstrm); if (err != Z_OK) { throw GeneralException(String(_("Error in inflateInit: ")) + zstrm.msg); } } } else 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; #ifdef FULLDEBUG std::cerr << _("Performing gzdopen on handle ") << h << _(" with mode \"") << format << "\"\n"; #endif 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 >= 10) { } else if (z) { #ifdef FULLDEBUG std::cerr << _("Performing gzwrite of ") << count << _(" byte(s) for handle ") << h << std::endl; #endif #ifdef HAVE_WD_ZLIB int err = gzwrite(zfile, buf, count); #else int err = gzwrite(zfile, (char *) buf, count); #endif 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); } } itell += err; return err; } else { itell += count = ::write(h, buf, count); return count; } } ssize_t Handle::uread(void * buf, size_t count) { if (z >= 10) { } if (z) { #ifdef DEBUG std::cerr << _("Performing gzread of ") << count << _(" byte(s) for handle ") << h << std::endl; #endif int err = gzread(zfile, buf, count); if (err == -1) { gzerror(zfile, &err); if (err == Z_ERRNO) { return -1; } else { return 0; } } itell += err; return err; } else { itell += count = ::read(h, buf, count); return count; } } off_t Handle::tell() const { if (z) { return gztell(zfile); } else { return itell; } } bool Handle::CanSeek() const { return 0; } off_t Handle::seek(off_t offset, int whence) throw(GeneralException) { if (z) { return itell = gzseek(zfile, offset, whence); } else { throw IOGeneral(_("Handle ") + GetName() + _(" can't seek")); } } Uint8 Handle::readU8() { Uint8 r; read(&r, 1); return r; } Uint16 Handle::readU16() { Uint16 r; read(&r, 2); #ifdef WORDS_BIGENDIAN return bswap_16(r); #else return r; #endif } Uint32 Handle::readU32() { Uint32 r; read(&r, 4); #ifdef WORDS_BIGENDIAN return bswap_32(r); #else return r; #endif } void Handle::writeU8(Uint8 v) { write(&v, 1); } void Handle::writeU16(Uint16 v) { #ifdef WORDS_BIGENDIAN Uint16 t = bswap_16(v); write(&t, 2); #else write(&v, 2); #endif } void Handle::writeU32(Uint32 v) { #ifdef WORDS_BIGENDIAN Uint32 t = bswap_32(v); write(&t, 4); #else write(&v, 4); #endif } void copy(Handle * s, Handle * d, ssize_t size) { long i; unsigned char c; long r; for (i = 0; (i < size) || (size < 0); i++) { r = s->read(&c, 1); if (r == 0) { break; } d->write(&c, 1); } } void Handle::Flush() { if (h < 0) return; if (z >= 10) { } else if (z) { gzflush(&z, Z_FULL_FLUSH); } else { #ifdef HAVE_FSYNC fsync(h); #endif } }