summaryrefslogtreecommitdiff
path: root/src/node_stdio.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_stdio.cc')
-rw-r--r--src/node_stdio.cc268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/node_stdio.cc b/src/node_stdio.cc
new file mode 100644
index 000000000..c52d632cb
--- /dev/null
+++ b/src/node_stdio.cc
@@ -0,0 +1,268 @@
+#include "node_stdio.h"
+#include "events.h"
+#include "coupling.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+using namespace v8;
+using namespace node;
+
+static Persistent<Object> stdio;
+static Persistent<Function> emit;
+
+static struct coupling *stdin_coupling = NULL;
+static struct coupling *stdout_coupling = NULL;
+
+static int stdin_fd = -1;
+static int stdout_fd = -1;
+
+static evcom_reader in;
+static evcom_writer out;
+
+static enum encoding stdin_encoding;
+
+static void
+EmitInput (Local<Value> input)
+{
+ HandleScope scope;
+
+ Local<Array> args = Array::New(1);
+ args->Set(Integer::New(0), input);
+
+ Local<Value> argv[2] = { String::NewSymbol("data"), args };
+
+ emit->Call(stdio, 2, argv);
+}
+
+static void
+EmitClose (void)
+{
+ HandleScope scope;
+
+ Local<Value> argv[1] = { String::NewSymbol("close") };
+
+ emit->Call(stdio, 1, argv);
+}
+
+/* STDERR IS ALWAY SYNC */
+static Handle<Value>
+WriteError (const Arguments& args)
+{
+ HandleScope scope;
+
+ if (args.Length() < 1)
+ return Undefined();
+
+ String::Utf8Value msg(args[0]->ToString());
+
+ fprintf(stderr, "%s", *msg);
+ fflush(stderr);
+
+ return Undefined();
+}
+
+static Handle<Value>
+Write (const Arguments& args)
+{
+ HandleScope scope;
+
+ ssize_t len;
+
+ Local<String> string;
+ Local<Array> array;
+
+ if (args[0]->IsArray()) {
+ array = Local<Array>::Cast(args[0]);
+ len = array->Length();
+ } else {
+ string = args[0]->ToString();
+ len = string->Utf8Length();
+ }
+
+ char buf[len];
+
+ if (args[0]->IsArray()) {
+ for (ssize_t index = 0; index < len; index++) {
+ Local<Value> int_value = array->Get(Integer::New(index));
+ buf[index] = int_value->IntegerValue();
+ }
+ } else {
+ switch (ParseEncoding(args[1])) {
+ case RAW:
+ case ASCII:
+ string->WriteAscii(buf, 0, len);
+ break;
+
+ case UTF8:
+ string->WriteUtf8(buf, len);
+ break;
+
+ default:
+ return ThrowException(String::New("Unknown encoding."));
+ }
+ }
+
+ evcom_writer_write(&out, buf, len);
+
+ return Undefined();
+}
+
+static void
+detach_in (evcom_reader *r)
+{
+ assert(r == &in);
+ HandleScope scope;
+
+ EmitClose();
+
+ evcom_reader_detach(&in);
+
+ if (stdin_coupling) {
+ coupling_destroy(stdin_coupling);
+ stdin_coupling = NULL;
+ }
+
+ stdin_fd = -1;
+}
+
+static void
+detach_out (evcom_writer* w)
+{
+ assert(w == &out);
+
+ evcom_writer_detach(&out);
+ if (stdout_coupling) {
+ coupling_destroy(stdout_coupling);
+ stdout_coupling = NULL;
+ }
+ stdout_fd = -1;
+}
+
+static void
+on_read (evcom_reader *r, const void *buf, size_t len)
+{
+ assert(r == &in);
+ HandleScope scope;
+
+ if (!len) {
+ return;
+ }
+
+ Local<Value> input;
+
+ if (stdin_encoding == RAW) {
+ // raw encoding
+ Local<Array> array = Array::New(len);
+ for (size_t i = 0; i < len; i++) {
+ unsigned char val = static_cast<const unsigned char*>(buf)[i];
+ array->Set(Integer::New(i), Integer::New(val));
+ }
+ input = array;
+
+ } else {
+ // utf8 or ascii encoding
+ input = String::New((const char*)buf, len);
+ }
+
+ EmitInput(input);
+}
+
+static inline int
+set_nonblock (int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) return -1;
+
+ int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (r == -1) return -1;
+
+ return 0;
+}
+
+static Handle<Value>
+Open (const Arguments& args)
+{
+ HandleScope scope;
+
+ if (stdin_fd >= 0) {
+ return ThrowException(String::New("stdin already open"));
+ }
+
+ stdin_encoding = UTF8;
+ if (args.Length() > 0) {
+ stdin_encoding = ParseEncoding(args[0]);
+ }
+
+ if (isatty(STDIN_FILENO)) {
+ // XXX selecting on tty fds wont work in windows.
+ // Must ALWAYS make a coupling on shitty platforms.
+ stdin_fd = STDIN_FILENO;
+ } else {
+ stdin_coupling = coupling_new_pull(STDIN_FILENO);
+ stdin_fd = coupling_nonblocking_fd(stdin_coupling);
+ }
+ set_nonblock(stdin_fd);
+
+ evcom_reader_init(&in);
+
+ in.on_read = on_read;
+ in.on_close = detach_in;
+
+ evcom_reader_set(&in, stdin_fd);
+ evcom_reader_attach(EV_DEFAULT_ &in);
+
+ return Undefined();
+}
+
+static Handle<Value>
+Close (const Arguments& args)
+{
+ HandleScope scope;
+
+ if (stdin_fd < 0) {
+ return ThrowException(String::New("stdin not open"));
+ }
+
+ evcom_reader_close(&in);
+
+ return Undefined();
+}
+
+void
+Stdio::Initialize (v8::Handle<v8::Object> target)
+{
+ HandleScope scope;
+
+ Local<Object> stdio_local =
+ EventEmitter::constructor_template->GetFunction()->NewInstance(0, NULL);
+
+ stdio = Persistent<Object>::New(stdio_local);
+
+ NODE_SET_METHOD(stdio, "open", Open);
+ NODE_SET_METHOD(stdio, "write", Write);
+ NODE_SET_METHOD(stdio, "writeError", WriteError);
+ NODE_SET_METHOD(stdio, "close", Close);
+
+ target->Set(String::NewSymbol("stdio"), stdio);
+
+ Local<Value> emit_v = stdio->Get(String::NewSymbol("emit"));
+ assert(emit_v->IsFunction());
+ Local<Function> emit_f = Local<Function>::Cast(emit_v);
+ emit = Persistent<Function>::New(emit_f);
+
+ if (isatty(STDOUT_FILENO)) {
+ // XXX selecting on tty fds wont work in windows.
+ // Must ALWAYS make a coupling on shitty platforms.
+ stdout_fd = STDOUT_FILENO;
+ } else {
+ stdout_coupling = coupling_new_push(STDOUT_FILENO);
+ stdout_fd = coupling_nonblocking_fd(stdout_coupling);
+ }
+ set_nonblock(stdout_fd);
+
+ evcom_writer_init(&out);
+ out.on_close = detach_out;
+ evcom_writer_set(&out, stdout_fd);
+ evcom_writer_attach(EV_DEFAULT_ &out);
+}