/* * Baltisot * Copyright (C) 1999-2007 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: Handle.cc,v 1.86 2008-08-14 16:21:56 pixel Exp $ */ #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 #if !defined(_WIN32) && !defined(__MIPSEL__) #include #endif #include "Handle.h" #include "gettext.h" enum { DEFLATE, INFLATE }; int Handle::nb_handles = 0; Handle::Handle(const Handle & nh) : itell(0), hFile(0), h(nh.h >= 0 ? nh.ndup() : nh.h), closed(nh.closed), nonblock(nh.closed), zfile(0), z(0), hMapObject(0), mapped(0) { #ifdef DEBUG printm(M_INFO, String(_("Duplication of handle ")) + nh.h + _(" to ") + h + "\n"); #endif if ((h >= 0) && (nh.z)) { SetZ(nh.z); } nb_handles++; } Handle::~Handle() { #ifdef DEBUG printm(M_INFO, String(_("Destroying handle ")) + h + "\n"); #endif close(); nb_handles--; } Handle::Handle(int nh) : itell(0), h(nh), closed(false), nonblock(false), zfile(0), z(0), hMapObject(0), mapped(0) { #ifdef DEBUG printm(M_INFO, String(_("Initialising handle ")) + h + "\n"); #endif nb_handles++; } int Handle::GetHandle() { return h; } void * Handle::GetHFile() { return hFile; } 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; if (closed) { throw IOGeneral("Unable to write: handle `" + GetName() + "' is closed."); } if (!count) return 0; 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 printm(M_INFO, _("write: throwing IOAgain for handle ") + GetName() + "\n"); #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; if (closed) { throw IOGeneral("Unable to read: handle `" + GetName() + "' is closed."); } if (!count) return 0; #ifdef FULLDEBUG printm(M_INFO, String(_("read: reading ")) + count + _(" bytes from handle ") + GetHandle() + "\n"); #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 printm(M_INFO, _("read: throwing IOAgain for handle ") + GetName() + "\n"); #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; #elif defined _WIN32 // HACKY HACKY HACK!! // Let's think windows will behave correctly on a non-socket here... if (!nonblock) { u_long iMode = 1; ioctlsocket(h, FIONBIO, &iMode); } nonblock = true; #endif } Handle & operator<<(Handle & h, const String & s) { const char * p; p = s.to_charp(); h.write(p, s.strlen()); 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::tagclose() { closed = 1; h = -1; } 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 GeneralException(String(_("Error during deflateEnd: ")) + zstrm.msg); } } else { err = inflateEnd(&zstrm); if (err != Z_OK) { throw GeneralException(String(_("Error during inflateEnd: ")) + zstrm.msg); } } err = nclose(); if (err) { throw GeneralException(String(_("Error during (zstream) close: ")) + strerror(errno)); } } else if (z) { #ifdef DEBUG printm(M_INFO, String(_("Performing gzclose on handle ")) + h + "\n"); #endif int err = gzclose(zfile); #ifdef DEBUG printm(M_INFO, String(_("gzclose returned ")) + err + "\n"); #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 { #ifndef NO_HFILE if (!hFile) { #endif int err = nclose(); if (err) { throw GeneralException(String(_("Error during close: ")) + strerror(errno)); } #ifndef NO_HFILE } #endif } #if defined (_WIN32) && !defined (NO_HFILE) if (hFile) { CloseHandle(hFile); hFile = 0; } #endif } if (mapped) { munmap(); } 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 throw (GeneralException) { int d; if ((d = dup(h)) < 0) { throw IOGeneral(String(_("Error dupping file `")) + GetName() + _("' (handle ") + h + "): " + strerror(errno) + " (" + errno + ")"); } return d; } 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 printm(M_INFO, _("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[5]; int index = 0; if (CanRead()) { format[index++] = 'r'; } if (CanWrite()) { format[index++] = 'w'; } format[index++] = (char) (az + '0'); format[index++] = 'b'; format[index] = 0; #ifdef FULLDEBUG printm(M_INFO, String(_("Performing gzdopen on handle ")) + h + _(" with mode \"") + format + "\"\n"); #endif if (!(zfile = gzdopen(h, format))) { throw GeneralException(String(_("Was not able to gzdopen: ")) + strerror(errno)); } z = az; } } ssize_t Handle::uwrite(const void * buf, size_t count) throw (GeneralException) { if (z >= 10) { } else if (z) { #ifdef FULLDEBUG printm(M_INFO, String(_("Performing gzwrite of ")) + count + _(" byte(s) for handle ") + h + "\n"); #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 = nwrite(buf, count); return count; } } ssize_t Handle::uread(void * buf, size_t count) { if (z >= 10) { } if (z) { #ifdef DEBUG printm(M_BARE, String(_("Performing gzread of ")) + count + _(" byte(s) for handle ") + h + "\n"); #endif int err = gzread(zfile, buf, count); if (err == -1) { throw GeneralException(String("Error reading zstream: ") + gzerror(zfile, &err)); if (err == Z_ERRNO) { return -1; } else { return 0; } } itell += err; return err; } else { #if defined (_WIN32) && !defined (NO_HFILE) if (hFile) { DWORD rcount; if (!ReadFile(hFile, buf, count, &rcount, 0)) { // Error.. } itell += count = rcount; } else #endif itell += count = nread(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 = 0; read(&r, 1); return r; } Uint16 Handle::readU16() { Uint16 r = 0; read(&r, 2); #ifdef WORDS_BIGENDIAN return bswap_16(r); #else return r; #endif } Uint32 Handle::readU32() { Uint32 r = 0; read(&r, 4); #ifdef WORDS_BIGENDIAN return bswap_32(r); #else return r; #endif } float Handle::readFloat() { #ifdef WORDS_BIGENDIAN Uint32 t = 0; read(&t, 4); t = bswap_32(t); return *((float *)&t); #else float r = 0; read(&r, 4); return r; #endif } double Handle::readDouble() { #ifdef WORDS_BIGENDIAN Uint32 t[3] = { 0, 0 }; read(t, 8); t[2] = bswap_32(t[0]); t[0] = bswap_32(t[1]); t[1] = t[2]; return *((double *)t); #else double r = 0; read(&r, 8); 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 Handle::writeFloat(float v) { #ifdef WORDS_BIGENDIAN writeU32(*((Uint32 *)&v)); #else write(&v, 4); #endif } void Handle::writeDouble(double v) { #ifdef WORDS_BIGENDIAN writeU32(*(((Uint32 *)&v) + 1)); writeU32(*(((Uint32 *)&v))); #else write(&v, 8); #endif } void Handle::copyto(Handle * dest, ssize_t s) { copy(this, dest, s); } void Handle::copyfrom(Handle * src, ssize_t s) { copy(src, this, s); } void copyone(Handle * s, Handle * d, ssize_t size) { long i; unsigned char c; long r; if (size < 0) size = s->GetSize(); for (i = 0; (i < size) || (size < 0); i++) { r = s->read(&c, 1); if (r == 0) { break; } d->write(&c, 1); } } #define BSIZE 20480 void copy(Handle * s, Handle * d, ssize_t size) { static unsigned char b[BSIZE]; long r; if (size < 0) size = s->GetSize(); LOCK; while (size) { if ((size > BSIZE) || (size < 0)) { r = s->read(b, BSIZE); if (r) d->write(b, r); } else { r = s->read(b, size); if (r) d->write(b, r); } if (!r) break; if (size > 0) size -= r; } UNLOCK; } void Handle::Flush() { if (!CanWrite()) return; if (h < 0) return; if (z >= 10) { } else if (z) { gzflush(&z, Z_FULL_FLUSH); } else { #ifdef HAVE_FSYNC fsync(h); #endif } } void * Handle::mmap(off_t offset, size_t length) throw (GeneralException) { void * r; if (h == -1) { throw GeneralException("Can't mmap() a virtual handle"); } #ifdef _WIN32 if (!hFile) { throw GeneralException("Can't mmap() a non-hFile handle under windows"); } #endif if (mapped) { throw GeneralException("Handle already mmap()ped"); } mapped = true; if (length == -1) { length = GetSize(); } maplength = length; #ifndef _WIN32 #ifdef __MIPSEL__ throw GeneralException("No mmap in ps2sdk yet."); #else r = ::mmap(0, length, (CanRead() ? PROT_READ : 0) | (CanWrite() ? PROT_WRITE : 0), MAP_SHARED, h, offset); #endif if (!r) { throw GeneralException(String("Was not able to mmap(): ") + strerror(errno)); } #else hMapObject = CreateFileMapping( hFile, 0, CanWrite() ? PAGE_READWRITE : PAGE_READONLY, 0, length, GetName().to_charp()); if (hMapObject != NULL) { r = MapViewOfFile( hMapObject, CanWrite() ? FILE_MAP_WRITE : FILE_MAP_READ, 0, offset, length); if (!r) { CloseHandle(hMapObject); throw GeneralException("Was not able to MapViewOfFile()"); } } else { throw GeneralException("Was not able to CreateFileMapping()"); } #endif mappedarea = r; return r; } void Handle::munmap() throw (GeneralException) { if (!mapped) { throw GeneralException("Can't munmap, was not mapped"); } #ifndef _WIN32 #ifndef __MIPSEL__ if (::munmap(mappedarea, maplength)) { throw GeneralException(String("Was not able to munmap(): ") + strerror(errno)); } #endif #else if (!UnmapViewOfFile(mappedarea)) { throw GeneralException("Was not able to UnmapViewOfFile()"); } CloseHandle(hMapObject); #endif mapped = false; mappedarea = 0; maplength = 0; } ssize_t Handle::nwrite(const void * buf, size_t count) throw (GeneralException) { return ::write(h, buf, count); } ssize_t Handle::nread(void * buf, size_t count) throw (GeneralException) { return ::read(h, buf, count); } int Handle::ndup() const throw (GeneralException) { return dup(h); } int Handle::nclose() throw (GeneralException) { return ::close(h); } int Handle::GetNbHandles() { return nb_handles; } #define CHUNK 10240 // // shamelessly ripped from http://www.zlib.net/zpipe.c // int Handle::zlib_inflate(Handle * in, Handle * out) throw (GeneralException) { int ret; z_stream s; unsigned char b_in[CHUNK]; unsigned char b_out[CHUNK]; unsigned int have, total_out; s.zalloc = (alloc_func) 0; s.zfree = (free_func) 0; s.opaque = (voidpf) 0; s.next_in = Z_NULL; s.next_out = Z_NULL; s.avail_in = 0; s.avail_out = 0; have = total_out = 0; ret = inflateInit(&s); if (ret != Z_OK ) { throw GeneralException("zlib: deflateInit() failed: " + String(ret)); } do { s.avail_in = in->read(b_in, CHUNK); if (s.avail_in == 0) break; s.next_in = b_in; do { s.avail_out = CHUNK; s.next_out = b_out; ret = inflate(&s, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR) { throw GeneralException("inflate returned Z_STREAM_ERROR: " + String(s.msg)); } switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&s); throw GeneralException("inflate returned an erroneous state: " + String(ret) + " - " + String(s.msg)); } have = CHUNK - s.avail_out; if (out->write(b_out, have) != have) { throw GeneralException("Output file not writable."); } total_out += have; } while (s.avail_out == 0); } while (ret != Z_STREAM_END); inflateEnd(&s); return total_out; } int Handle::zlib_deflate(Handle * in, Handle * out) throw (GeneralException) { int ret, flush; z_stream s; unsigned char b_in[CHUNK]; unsigned char b_out[CHUNK]; unsigned int have, total_out; s.zalloc = (alloc_func) 0; s.zfree = (free_func) 0; s.opaque = (voidpf) 0; s.next_in = Z_NULL; s.next_out = Z_NULL; s.avail_in = 0; s.avail_out = 0; have = total_out = 0; ret = deflateInit(&s, Z_DEFAULT_COMPRESSION); if (ret != Z_OK ) { throw GeneralException("zlib: deflateInit() failed: " + String(ret)); } do { s.avail_in = in->read(b_in, CHUNK); flush = s.avail_in == 0 ? Z_FINISH : Z_NO_FLUSH; s.next_in = b_in; do { s.avail_out = CHUNK; s.next_out = b_out; ret = deflate(&s, flush); if (ret == Z_STREAM_ERROR) { throw GeneralException("inflate returned Z_STREAM_ERROR: " + String(s.msg)); } have = CHUNK - s.avail_out; if (out->write(b_out, have) != have) { deflateEnd(&s); throw GeneralException("couldn't write properly to output."); } total_out += have; } while (s.avail_out == 0); if (s.avail_in != 0) { throw GeneralException("All input not used."); } } while (flush != Z_FINISH); deflateEnd(&s); return total_out; }