mirror of
https://github.com/nodejs/node-v0.x-archive.git
synced 2026-04-28 03:01:10 -04:00
Introduce node.stdio
Remove old stdout, stderr, stdin objects.
This commit is contained in:
321
deps/coupling/coupling.c
vendored
Normal file
321
deps/coupling/coupling.c
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
/* Copyright (c) 2009 Ryan Dahl (ry@tinyclouds.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "coupling.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef PIPE_BUF
|
||||
# define BUFSIZE PIPE_BUF
|
||||
#else
|
||||
# define BUFSIZE 4096
|
||||
#endif
|
||||
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
// ring buffer
|
||||
typedef struct {
|
||||
int head;
|
||||
int tail;
|
||||
int size;
|
||||
char buf[BUFSIZE];
|
||||
} ring_buffer;
|
||||
|
||||
static inline void
|
||||
ring_buffer_inspect (ring_buffer *ring)
|
||||
{
|
||||
printf("size %5d head %5d tail %5d\n", ring->size, ring->head, ring->tail);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ring_buffer_init (ring_buffer *ring)
|
||||
{
|
||||
ring->head = 0;
|
||||
ring->tail = 0;
|
||||
ring->size = 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
ring_buffer_filled_p (ring_buffer *ring)
|
||||
{
|
||||
assert(BUFSIZE - (long)ring->size >= 0);
|
||||
return (BUFSIZE == ring->size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ring_buffer_empty_p (ring_buffer *ring)
|
||||
{
|
||||
return 0 == ring->size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ring_buffer_pull (ring_buffer *ring, int fd)
|
||||
{
|
||||
// DO NOT CALL WHEN FILLED
|
||||
assert(!ring_buffer_filled_p(ring));
|
||||
|
||||
struct iovec iov[2];
|
||||
int iovcnt = 1;
|
||||
|
||||
// Very tough logic. Can you follow? Barely can I.
|
||||
iov[0].iov_base = ring->buf + ring->tail;
|
||||
if (ring->tail < ring->head) {
|
||||
iov[0].iov_len = ring->head - ring->tail;
|
||||
} else {
|
||||
iov[0].iov_len = BUFSIZE - ring->tail;
|
||||
if (ring->head != 0) {
|
||||
iovcnt = 2;
|
||||
iov[1].iov_base = ring->buf;
|
||||
iov[1].iov_len = ring->head;
|
||||
}
|
||||
}
|
||||
|
||||
int r = readv(fd, iov, iovcnt);
|
||||
|
||||
if (r > 0) {
|
||||
ring->size += r;
|
||||
ring->tail = (ring->tail + r) % BUFSIZE;
|
||||
}
|
||||
assert(ring->size <= BUFSIZE);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ring_buffer_push (ring_buffer *ring, int fd)
|
||||
{
|
||||
// DO NOT CALL WHEN EMPTY
|
||||
assert(!ring_buffer_empty_p(ring));
|
||||
|
||||
struct iovec iov[2];
|
||||
int iovcnt = 1;
|
||||
|
||||
iov[0].iov_base = ring->buf + ring->head;
|
||||
if (ring->head < ring->tail) {
|
||||
iov[0].iov_len = ring->tail - ring->head;
|
||||
} else {
|
||||
iov[0].iov_len = BUFSIZE - ring->head;
|
||||
if (ring->tail != 0) {
|
||||
iovcnt = 2;
|
||||
iov[1].iov_base = ring->buf;
|
||||
iov[1].iov_len = ring->tail;
|
||||
}
|
||||
}
|
||||
|
||||
int r = writev(fd, iov, iovcnt);
|
||||
|
||||
if (r > 0) {
|
||||
ring->size -= r;
|
||||
ring->head = (ring->head + r) % BUFSIZE;
|
||||
}
|
||||
assert(0 <= (long)ring->size);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pump (int pullfd, int pushfd)
|
||||
{
|
||||
int r;
|
||||
ring_buffer ring;
|
||||
fd_set readfds, writefds, exceptfds;
|
||||
|
||||
ring_buffer_init(&ring);
|
||||
|
||||
int maxfd;
|
||||
|
||||
while (pushfd >= 0 && (pullfd >= 0 || !ring_buffer_empty_p(&ring))) {
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
|
||||
maxfd = pushfd;
|
||||
FD_SET(pushfd, &exceptfds);
|
||||
|
||||
if (pullfd >= 0) {
|
||||
FD_SET(pullfd, &exceptfds);
|
||||
maxfd = MAX(pushfd, pullfd);
|
||||
if (!ring_buffer_filled_p(&ring)) FD_SET(pullfd, &readfds);
|
||||
}
|
||||
|
||||
if (!ring_buffer_empty_p(&ring)) {
|
||||
FD_SET(pushfd, &writefds);
|
||||
}
|
||||
|
||||
r = select(maxfd+1, &readfds, &writefds, &exceptfds, NULL);
|
||||
|
||||
if (r < 0 || FD_ISSET(pushfd, &exceptfds)) {
|
||||
pushfd = pullfd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pullfd >= 0 && FD_ISSET(pullfd, &exceptfds)) {
|
||||
pullfd = -1;
|
||||
}
|
||||
|
||||
if (pullfd >= 0 && FD_ISSET(pullfd, &readfds)) {
|
||||
r = ring_buffer_pull(&ring, pullfd);
|
||||
if (r == 0) {
|
||||
/* eof */
|
||||
pullfd = -1;
|
||||
|
||||
} else if (r < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN) goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(pushfd, &writefds)) {
|
||||
r = ring_buffer_push(&ring, pushfd);
|
||||
if (r < 0) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
continue;
|
||||
|
||||
case EPIPE:
|
||||
/* TODO catch SIGPIPE? */
|
||||
pushfd = pullfd = -1;
|
||||
return;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
perror("(coupling) pump");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct coupling {
|
||||
int is_pull;
|
||||
int pullfd;
|
||||
int pushfd;
|
||||
int exposedfd;
|
||||
pthread_t tid;
|
||||
};
|
||||
|
||||
static void *
|
||||
pump_thread (void *data)
|
||||
{
|
||||
struct coupling *c = (struct coupling*)data;
|
||||
|
||||
pump(c->pullfd, c->pushfd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct coupling*
|
||||
create_coupling (int fd, int is_pull)
|
||||
{
|
||||
int pipefd[2];
|
||||
|
||||
struct coupling *c = malloc(sizeof(struct coupling));
|
||||
if (!c) return NULL;
|
||||
|
||||
int r = pipe(pipefd);
|
||||
if (r < 0) return NULL;
|
||||
|
||||
r = set_nonblock(pipefd[0]);
|
||||
if (r < 0) return NULL;
|
||||
assert(pipefd[0] >= 0);
|
||||
|
||||
r = set_nonblock(pipefd[1]);
|
||||
if (r < 0) return NULL;
|
||||
assert(pipefd[1] >= 0);
|
||||
|
||||
if (is_pull) {
|
||||
c->is_pull = 1;
|
||||
c->pullfd = fd;
|
||||
c->pushfd = pipefd[1];
|
||||
c->exposedfd = pipefd[0];
|
||||
} else {
|
||||
c->is_pull = 0;
|
||||
c->pushfd = fd;
|
||||
c->pullfd = pipefd[0];
|
||||
c->exposedfd = pipefd[1];
|
||||
}
|
||||
|
||||
r = pthread_create(&c->tid, NULL, pump_thread, c);
|
||||
if (r < 0) return NULL;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct coupling*
|
||||
coupling_new_pull (int fd)
|
||||
{
|
||||
return create_coupling(fd, 1);
|
||||
}
|
||||
|
||||
struct coupling*
|
||||
coupling_new_push (int fd)
|
||||
{
|
||||
return create_coupling(fd, 0);
|
||||
}
|
||||
|
||||
int
|
||||
coupling_nonblocking_fd (struct coupling *c)
|
||||
{
|
||||
return c->exposedfd;
|
||||
}
|
||||
|
||||
void
|
||||
coupling_join (struct coupling *c)
|
||||
{
|
||||
int r = pthread_join(c->tid, NULL);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
void
|
||||
coupling_destroy (struct coupling *c)
|
||||
{
|
||||
close(c->is_pull ? c->pushfd : c->pullfd);
|
||||
free(c);
|
||||
}
|
||||
42
deps/coupling/coupling.h
vendored
Normal file
42
deps/coupling/coupling.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2009 Ryan Dahl (ry@tinyclouds.org)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef coupling_h
|
||||
#define coupling_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct coupling;
|
||||
|
||||
struct coupling* coupling_new_pull (int fd);
|
||||
struct coupling* coupling_new_push (int fd);
|
||||
int coupling_nonblocking_fd (struct coupling*);
|
||||
void coupling_join (struct coupling*);
|
||||
void coupling_destroy (struct coupling*);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
73
deps/coupling/test.c
vendored
Normal file
73
deps/coupling/test.c
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include "coupling.h"
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define CHUNKSIZE 44231
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
struct coupling *c = coupling_new_push(STDERR_FILENO);
|
||||
int stderr_fd = coupling_nonblocking_fd(c);
|
||||
|
||||
int size;
|
||||
if (argc == 2) {
|
||||
size = atoi(argv[1]);
|
||||
printf("size = %d\n", size);
|
||||
} else {
|
||||
printf("usage: ./test 123 2> stderr; wc -c stderr\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *msg = malloc(CHUNKSIZE);
|
||||
int i, r;
|
||||
for (i = 0; i < CHUNKSIZE; i++) {
|
||||
msg[i] = 'A' + i % 26;
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
|
||||
fd_set writefds, exceptfds;
|
||||
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(stderr_fd, &exceptfds);
|
||||
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(stderr_fd, &writefds);
|
||||
|
||||
while (written < size) {
|
||||
r = select(stderr_fd+1, NULL, &writefds, &exceptfds, NULL);
|
||||
if (r < 0) {
|
||||
printf("test.c select(): %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (FD_ISSET(stderr_fd, &exceptfds)) {
|
||||
printf("exception on stderr fd\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (FD_ISSET(stderr_fd, &writefds)) {
|
||||
r = write(stderr_fd, msg, MIN(size - written, CHUNKSIZE));
|
||||
if (r < 0 && errno != EAGAIN) {
|
||||
printf("test.c write(): %s\n", strerror(errno));
|
||||
exit(1);
|
||||
} else {
|
||||
written += r;
|
||||
printf("%d\n", written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(stderr_fd);
|
||||
coupling_join(c);
|
||||
coupling_destroy(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -16,10 +16,6 @@ node::DefineConstants (Handle<Object> target)
|
||||
NODE_DEFINE_CONSTANT(target, UTF8);
|
||||
NODE_DEFINE_CONSTANT(target, ASCII);
|
||||
|
||||
NODE_DEFINE_CONSTANT(target, STDIN_FILENO);
|
||||
NODE_DEFINE_CONSTANT(target, STDOUT_FILENO);
|
||||
NODE_DEFINE_CONSTANT(target, STDERR_FILENO);
|
||||
|
||||
// file access modes
|
||||
NODE_DEFINE_CONSTANT(target, O_RDONLY);
|
||||
NODE_DEFINE_CONSTANT(target, O_WRONLY);
|
||||
|
||||
11
src/file.js
11
src/file.js
@@ -203,14 +203,3 @@ node.fs.File = function (options) {
|
||||
return self.write(data + "\n", null);
|
||||
};
|
||||
};
|
||||
|
||||
stdout = new node.fs.File({ fd: node.STDOUT_FILENO });
|
||||
stderr = new node.fs.File({ fd: node.STDERR_FILENO });
|
||||
stdin = new node.fs.File({ fd: node.STDIN_FILENO });
|
||||
|
||||
puts = stdout.puts;
|
||||
print = stdout.print;
|
||||
p = function (data) {
|
||||
return puts(JSON.stringify(data));
|
||||
}
|
||||
|
||||
|
||||
15
src/node.cc
15
src/node.cc
@@ -8,6 +8,7 @@
|
||||
#include "timer.h"
|
||||
#include "process.h"
|
||||
#include "constants.h"
|
||||
#include "node_stdio.h"
|
||||
|
||||
#include "natives.h"
|
||||
|
||||
@@ -140,17 +141,6 @@ compile (const v8::Arguments& args)
|
||||
return scope.Close(result);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value>
|
||||
debug (const v8::Arguments& args)
|
||||
{
|
||||
if (args.Length() < 1)
|
||||
return Undefined();
|
||||
HandleScope scope;
|
||||
String::Utf8Value msg(args[0]->ToString());
|
||||
fprintf(stderr, "DEBUG: %s\n", *msg);
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
static void
|
||||
OnFatalError (const char* location, const char* message)
|
||||
{
|
||||
@@ -240,11 +230,12 @@ Load (int argc, char *argv[])
|
||||
global_obj->Set(String::NewSymbol("ARGV"), arguments);
|
||||
|
||||
NODE_SET_METHOD(node_obj, "compile", compile);
|
||||
NODE_SET_METHOD(node_obj, "debug", debug);
|
||||
NODE_SET_METHOD(node_obj, "reallyExit", node_exit);
|
||||
|
||||
EventEmitter::Initialize(node_obj);
|
||||
Promise::Initialize(node_obj);
|
||||
|
||||
Stdio::Initialize(node_obj);
|
||||
Timer::Initialize(node_obj);
|
||||
Process::Initialize(node_obj);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ node.Module.prototype.load = function (callback) {
|
||||
var promise = node.cat(self.filename, "utf8");
|
||||
|
||||
promise.addErrback(function () {
|
||||
stderr.puts("Error reading " + self.filename);
|
||||
node.stdio.writeError("Error reading " + self.filename + "\n");
|
||||
node.exit(1);
|
||||
});
|
||||
|
||||
|
||||
268
src/node_stdio.cc
Normal file
268
src/node_stdio.cc
Normal file
@@ -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);
|
||||
}
|
||||
17
src/node_stdio.h
Normal file
17
src/node_stdio.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef node_stdio_h
|
||||
#define node_stdio_h
|
||||
|
||||
#include "node.h"
|
||||
|
||||
#include <v8.h>
|
||||
#include <evcom.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
class Stdio {
|
||||
public:
|
||||
static void Initialize (v8::Handle<v8::Object> target);
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
#endif
|
||||
16
src/util.js
16
src/util.js
@@ -86,3 +86,19 @@ node.path = new function () {
|
||||
return parts[parts.length-1];
|
||||
};
|
||||
};
|
||||
|
||||
print = function (x) {
|
||||
return node.stdio.write(x);
|
||||
};
|
||||
|
||||
puts = function (x) {
|
||||
return print(x.toString() + "\n");
|
||||
};
|
||||
|
||||
p = function (x) {
|
||||
return puts(JSON.stringify(x));
|
||||
};
|
||||
|
||||
node.debug = function (x) {
|
||||
return node.stdio.writeError("DEBUG: " + x.toString() + "\n");
|
||||
};
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
include("mjsunit.js");
|
||||
PORT = 8888;
|
||||
|
||||
puts("hello world");
|
||||
|
||||
var body = "exports.A = function() { return 'A';}";
|
||||
var server = node.http.createServer(function (req, res) {
|
||||
res.sendHeader(200, [
|
||||
["Content-Length", body.length],
|
||||
["Content-Type", "text/plain"]
|
||||
]);
|
||||
puts("req?");
|
||||
res.sendHeader(200, {
|
||||
"Content-Length": body.length,
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
res.sendBody(body);
|
||||
res.finish();
|
||||
});
|
||||
|
||||
@@ -102,8 +102,7 @@ execution.
|
||||
=== Helpers
|
||||
|
||||
+puts(string)+::
|
||||
Alias for +stdout.puts()+. Outputs the +string+ and a trailing new-line to
|
||||
+stdout+.
|
||||
Outputs the +string+ and a trailing new-line to +stdout+.
|
||||
+
|
||||
Everything in node is asynchronous; +puts()+ is no exception. This might
|
||||
seem ridiculous but, if for example, one is piping +stdout+ into an NFS
|
||||
@@ -138,9 +137,6 @@ Immediately ends the process with the specified code.
|
||||
An array containing the command line arguments.
|
||||
|
||||
|
||||
+stdout+, +stderr+, and +stdin+ ::
|
||||
Objects of type +node.fs.File+. (See below.)
|
||||
|
||||
+__filename+ ::
|
||||
The filename of the script being executed.
|
||||
|
||||
@@ -194,6 +190,37 @@ Adds a listener for the +"success"+ event. Returns the same promise object.
|
||||
Adds a listener for the +"error"+ event. Returns the same promise object.
|
||||
|
||||
|
||||
=== Standard I/O
|
||||
|
||||
Standard I/O is handled through a special object +node.stdio+. stdout and
|
||||
stdin are fully non-blocking (even when piping to files). stderr is
|
||||
synchronous.
|
||||
|
||||
[cols="1,2,10",options="header"]
|
||||
|=========================================================
|
||||
| Event | Parameters | Notes
|
||||
|
||||
| +"data"+ | +data+ | Made when stdin has received a chunk of data.
|
||||
Depending on the encoding that stdin was opened
|
||||
with, +data+ will be either an array of integers
|
||||
(raw encoding) or a string (ascii or utf8
|
||||
encoding). This event will only be emited after
|
||||
+node.stdio.open()+ has been called.
|
||||
| +"close"+ | | Made when stdin has been closed.
|
||||
|=========================================================
|
||||
|
||||
+node.stdio.open(encoding="utf8")+::
|
||||
Open stdin. The program will not exit until +node.stdio.close()+ has been
|
||||
called or the +"close"+ event has been emitted.
|
||||
|
||||
+node.stdio.write(data)+::
|
||||
Write data to stdout.
|
||||
|
||||
+node.stdio.writeError(data)+::
|
||||
Write data to stderr. Synchronous.
|
||||
|
||||
+node.stdio.close()+::
|
||||
Close stdin.
|
||||
|
||||
|
||||
=== Modules
|
||||
|
||||
14
wscript
14
wscript
@@ -189,6 +189,16 @@ def build(bld):
|
||||
if bld.env["USE_DEBUG"]:
|
||||
http_parser.clone("debug")
|
||||
|
||||
### coupling
|
||||
coupling = bld.new_task_gen("cc", "staticlib")
|
||||
coupling.source = "deps/coupling/coupling.c"
|
||||
coupling.includes = "deps/coupling/"
|
||||
coupling.name = "coupling"
|
||||
coupling.target = "coupling"
|
||||
coupling.install_path = None
|
||||
if bld.env["USE_DEBUG"]:
|
||||
coupling.clone("debug")
|
||||
|
||||
### src/native.cc
|
||||
def javascript_in_c(task):
|
||||
env = task.env
|
||||
@@ -220,6 +230,7 @@ def build(bld):
|
||||
src/events.cc
|
||||
src/http.cc
|
||||
src/net.cc
|
||||
src/node_stdio.cc
|
||||
src/dns.cc
|
||||
src/file.cc
|
||||
src/timer.cc
|
||||
@@ -234,8 +245,9 @@ def build(bld):
|
||||
deps/libeio
|
||||
deps/evcom
|
||||
deps/http_parser
|
||||
deps/coupling
|
||||
"""
|
||||
node.uselib_local = "evcom ev eio http_parser"
|
||||
node.uselib_local = "evcom ev eio http_parser coupling"
|
||||
node.uselib = "UDNS V8 EXECINFO PROFILER EFENCE"
|
||||
node.install_path = '${PREFIX}/bin'
|
||||
node.chmod = 0755
|
||||
|
||||
Reference in New Issue
Block a user