summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--includes/Output.h26
-rw-r--r--includes/Printer.h1
-rw-r--r--src/Output.cc130
-rw-r--r--tests/test-Handles.cc8
5 files changed, 166 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 34ea1bd..a7aadd7 100644
--- a/Makefile
+++ b/Makefile
@@ -119,6 +119,7 @@ Printer.cc \
\
Handle.cc \
Input.cc \
+Output.cc \
Socket.cc \
\
Task.cc \
diff --git a/includes/Output.h b/includes/Output.h
new file mode 100644
index 0000000..59d9d67
--- /dev/null
+++ b/includes/Output.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <Handle.h>
+
+namespace Balau {
+
+class Output : public SeekableHandle {
+ public:
+ Output(const char * fname, bool truncate = true) throw (GeneralException);
+ virtual void close() throw (GeneralException);
+ virtual ssize_t write(const void * buf, size_t count) throw (GeneralException);
+ virtual bool isClosed();
+ virtual bool canWrite();
+ virtual const char * getName();
+ virtual off_t getSize();
+ virtual time_t getMTime();
+ const char * getFName() { return m_fname.to_charp(); }
+ private:
+ int m_fd;
+ String m_name;
+ String m_fname;
+ off_t m_size;
+ time_t m_mtime;
+};
+
+};
diff --git a/includes/Printer.h b/includes/Printer.h
index e8d9a03..2c40088 100644
--- a/includes/Printer.h
+++ b/includes/Printer.h
@@ -36,6 +36,7 @@ enum {
E_INPUT = 16,
E_SOCKET = 32,
E_THREAD = 64,
+ E_OUTPUT = 128,
};
class Printer {
diff --git a/src/Output.cc b/src/Output.cc
new file mode 100644
index 0000000..3f66a7c
--- /dev/null
+++ b/src/Output.cc
@@ -0,0 +1,130 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "eio.h"
+#include "Output.h"
+#include "Task.h"
+#include "Printer.h"
+
+#ifdef _WIN32
+static const char * strerror_r(int errorno, char * buf, size_t bufsize) {
+#ifdef _MSVC
+ strerror_s(buf, bufsize, errorno);
+ return buf;
+#else
+ return strerror(errorno);
+#endif
+}
+#endif
+
+struct cbResults_t {
+ Balau::Events::Custom evt;
+ int result, errorno;
+};
+
+static int eioDone(eio_req * req) {
+ cbResults_t * cbResults = (cbResults_t *) req->data;
+ cbResults->result = req->result;
+ cbResults->errorno = req->errorno;
+ cbResults->evt.doSignal();
+ return 0;
+}
+
+struct cbStatsResults_t {
+ Balau::Events::Custom evt;
+ int result, errorno;
+ EIO_STRUCT_STAT statdata;
+};
+
+static int eioStatsDone(eio_req * req) {
+ cbStatsResults_t * cbStatsResults = (cbStatsResults_t *) req->data;
+ cbStatsResults->result = req->result;
+ cbStatsResults->errorno = req->errorno;
+ cbStatsResults->statdata = *(EIO_STRUCT_STAT *) req->ptr2;
+ cbStatsResults->evt.doSignal();
+ return 0;
+}
+
+Balau::Output::Output(const char * fname, bool truncate) throw (GeneralException) : m_fd(-1), m_size(-1), m_mtime(-1) {
+ m_name.set("Output(%s)", fname);
+ m_fname = fname;
+
+ Printer::elog(E_OUTPUT, "Opening file %s", fname);
+
+ cbResults_t cbResults;
+ eio_req * r = eio_open(fname, O_WRONLY | O_CREAT | (truncate ? O_TRUNC : 0), 0755, 0, eioDone, &cbResults);
+ Assert(r != 0);
+ Task::yield(&cbResults.evt);
+ if (cbResults.result < 0) {
+ char str[4096];
+ if (cbResults.errorno == ENOENT) {
+ throw ENoEnt(fname);
+ } else {
+ throw GeneralException(String("Unable to open file ") + m_name + " for reading: " + strerror_r(cbResults.errorno, str, sizeof(str)) + " (err#" + cbResults.errorno + ")");
+ }
+ } else {
+ m_fd = cbResults.result;
+ }
+
+ cbStatsResults_t cbStatsResults;
+ r = eio_fstat(m_fd, 0, eioStatsDone, &cbStatsResults);
+ Assert(r != 0);
+ Task::yield(&cbStatsResults.evt);
+ if (cbStatsResults.result == 0) {
+ m_size = cbStatsResults.statdata.st_size;
+ m_mtime = cbStatsResults.statdata.st_mtime;
+ }
+}
+
+void Balau::Output::close() throw (GeneralException) {
+ if (m_fd < 0)
+ return;
+ cbResults_t cbResults;
+ eio_req * r = eio_close(m_fd, 0, eioDone, &cbResults);
+ Assert(r != 0);
+ m_fd = -1;
+ Task::yield(&cbResults.evt);
+ if (cbResults.result < 0) {
+ char str[4096];
+ strerror_r(cbResults.errorno, str, sizeof(str));
+ throw GeneralException(String("Unable to close file ") + m_name + ": " + str);
+ } else {
+ m_fd = cbResults.result;
+ }
+}
+
+ssize_t Balau::Output::write(const void * buf, size_t count) throw (GeneralException) {
+ cbResults_t cbResults;
+ eio_req * r = eio_write(m_fd, const_cast<void *>(buf), count, getWOffset(), 0, eioDone, &cbResults);
+ Assert(r != 0);
+ Task::yield(&cbResults.evt);
+ if (cbResults.result > 0) {
+ wseek(cbResults.result, SEEK_CUR);
+ } else {
+ char str[4096];
+ throw GeneralException(String("Unable to write file ") + m_name + ": " + strerror_r(cbResults.errorno, str, sizeof(str)) + " (err#" + cbResults.errorno + ")");
+ }
+ return cbResults.result;
+}
+
+bool Balau::Output::isClosed() {
+ return m_fd < 0;
+}
+
+const char * Balau::Output::getName() {
+ return m_name.to_charp();
+}
+
+off_t Balau::Output::getSize() {
+ return m_size;
+}
+
+time_t Balau::Output::getMTime() {
+ return m_mtime;
+}
+
+bool Balau::Output::canWrite() {
+ return true;
+}
diff --git a/tests/test-Handles.cc b/tests/test-Handles.cc
index c500394..f9fd055 100644
--- a/tests/test-Handles.cc
+++ b/tests/test-Handles.cc
@@ -1,5 +1,6 @@
#include <Main.h>
#include <Input.h>
+#include <Output.h>
#ifdef _WIN32
void ctime_r(const time_t * t, char * str) {
@@ -62,5 +63,12 @@ void MainTask::Do() {
Assert(r == (s - 5));
Assert(memcmp(buf1, buf2, s) == 0);
+ IO<Handle> o(new Output("tests/out.txt"));
+ s = o->wtell();
+ Assert(s == 0);
+ s = o->getSize();
+ Assert(s == 0);
+ o->write("foo\n", 4);
+
Printer::log(M_STATUS, "Test::Handles passed.");
}