summaryrefslogtreecommitdiff
path: root/generic/Handle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'generic/Handle.cpp')
-rw-r--r--generic/Handle.cpp269
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);
+ }
+}