mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
5.4 KiB
271 lines
5.4 KiB
#include <node_stdio.h>
|
|
#include <node_events.h>
|
|
#include <coupling.h>
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.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<Value> argv[2] = { String::NewSymbol("data"), input };
|
|
|
|
emit->Call(stdio, 2, argv);
|
|
}
|
|
|
|
static void
|
|
EmitClose (void)
|
|
{
|
|
HandleScope scope;
|
|
|
|
Local<Value> argv[1] = { String::NewSymbol("close") };
|
|
|
|
emit->Call(stdio, 1, argv);
|
|
}
|
|
|
|
|
|
static inline Local<Value> errno_exception(int errorno) {
|
|
Local<Value> e = Exception::Error(String::NewSymbol(strerror(errorno)));
|
|
Local<Object> obj = e->ToObject();
|
|
obj->Set(String::NewSymbol("errno"), Integer::New(errorno));
|
|
return e;
|
|
}
|
|
|
|
|
|
/* 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());
|
|
|
|
ssize_t r;
|
|
size_t written = 0;
|
|
while (written < msg.length()) {
|
|
r = write(STDERR_FILENO, (*msg) + written, msg.length() - written);
|
|
if (r < 0) {
|
|
if (errno == EAGAIN || errno == EIO) {
|
|
usleep(100);
|
|
continue;
|
|
}
|
|
return ThrowException(errno_exception(errno));
|
|
}
|
|
written += (size_t)r;
|
|
}
|
|
|
|
return Undefined();
|
|
}
|
|
|
|
static Handle<Value>
|
|
Write (const Arguments& args)
|
|
{
|
|
HandleScope scope;
|
|
|
|
if (args.Length() == 0) {
|
|
return ThrowException(Exception::Error(String::New("Bad argument")));
|
|
}
|
|
|
|
enum encoding enc = UTF8;
|
|
if (args.Length() > 1) enc = ParseEncoding(args[1], UTF8);
|
|
|
|
ssize_t len = DecodeBytes(args[0], enc);
|
|
|
|
if (len < 0) {
|
|
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
|
|
return ThrowException(exception);
|
|
}
|
|
|
|
char buf[len];
|
|
ssize_t written = DecodeWrite(buf, len, args[0], enc);
|
|
|
|
assert(written == len);
|
|
|
|
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> data = Encode(buf, len, stdin_encoding);
|
|
|
|
EmitInput(data);
|
|
}
|
|
|
|
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(Exception::Error(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;
|
|
|
|
assert(stdio == args.Holder());
|
|
|
|
if (stdin_fd < 0) {
|
|
return ThrowException(Exception::Error(String::New("stdin not open")));
|
|
}
|
|
|
|
evcom_reader_close(&in);
|
|
|
|
return Undefined();
|
|
}
|
|
|
|
void Stdio::Flush() {
|
|
if (stdout_fd >= 0) {
|
|
close(stdout_fd);
|
|
stdout_fd = -1;
|
|
}
|
|
|
|
if (stdout_coupling) {
|
|
coupling_join(stdout_coupling);
|
|
coupling_destroy(stdout_coupling);
|
|
stdout_coupling = NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|