diff options
author | Ryan <ry@tinyclouds.org> | 2009-08-24 20:25:24 +0200 |
---|---|---|
committer | Ryan <ry@tinyclouds.org> | 2009-08-24 21:20:26 +0200 |
commit | 17c6a67f15944962f27326072d9ea2df40ec0491 (patch) | |
tree | 3484088236696332fea28bdc300f3a02b02ea22c /src/node_stdio.cc | |
parent | 0727fcc9edb35d1ce7fc9e3759e9755e78f7129a (diff) | |
download | node-17c6a67f15944962f27326072d9ea2df40ec0491.tar.gz |
Introduce node.stdio
Remove old stdout, stderr, stdin objects.
Diffstat (limited to 'src/node_stdio.cc')
-rw-r--r-- | src/node_stdio.cc | 268 |
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); +} |