mirror of https://github.com/lukechilds/node.git
13 changed files with 793 additions and 38 deletions
@ -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); |
|||
} |
@ -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 |
@ -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; |
|||
} |
@ -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); |
|||
} |
@ -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 |
Loading…
Reference in new issue