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.
269 lines
5.2 KiB
269 lines
5.2 KiB
16 years ago
|
#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);
|
||
|
}
|