Large diffs are not rendered by default.

@@ -22,6 +22,8 @@
TEST_DECLARE (tty)
TEST_DECLARE (tcp_ping_pong)
TEST_DECLARE (tcp_ping_pong_v6)
TEST_DECLARE (tcp_ref)
TEST_DECLARE (tcp_ref2)
TEST_DECLARE (pipe_ping_pong)
TEST_DECLARE (delayed_accept)
TEST_DECLARE (tcp_writealot)
@@ -52,6 +54,8 @@ TEST_DECLARE (connection_fail_doesnt_auto_close)
TEST_DECLARE (shutdown_eof)
TEST_DECLARE (callback_stack)
TEST_DECLARE (timer)
TEST_DECLARE (timer_ref)
TEST_DECLARE (timer_ref2)
TEST_DECLARE (timer_again)
TEST_DECLARE (idle_starvation)
TEST_DECLARE (loop_handles)
@@ -104,6 +108,12 @@ HELPER_DECLARE (pipe_echo_server)
TASK_LIST_START
TEST_ENTRY (tty)


TEST_ENTRY (tcp_ref)

TEST_ENTRY (tcp_ref2)
TEST_HELPER (tcp_ref2, tcp4_echo_server)

TEST_ENTRY (tcp_ping_pong)
TEST_HELPER (tcp_ping_pong, tcp4_echo_server)

@@ -154,6 +164,8 @@ TASK_LIST_START
TEST_HELPER (callback_stack, tcp4_echo_server)

TEST_ENTRY (timer)
TEST_ENTRY (timer_ref)
TEST_ENTRY (timer_ref2)

TEST_ENTRY (timer_again)

@@ -127,3 +127,50 @@ TEST_IMPL(tcp_close) {

return 0;
}


TEST_IMPL(tcp_ref) {
uv_tcp_t never;
int r;

/* A tcp just initialized should count as one reference. */
r = uv_tcp_init(uv_default_loop(), &never);
ASSERT(r == 0);

/* One unref should set the loop ref count to zero. */
uv_unref(uv_default_loop());

/* Therefore this does not block */
uv_run(uv_default_loop());

return 0;
}


static void never_cb(uv_connect_t* conn_req, int status) {
FATAL("never_cb should never be called");
}


TEST_IMPL(tcp_ref2) {
uv_tcp_t never;
int r;

/* A tcp just initialized should count as one reference. */
r = uv_tcp_init(uv_default_loop(), &never);
ASSERT(r == 0);

r = uv_tcp_connect(&connect_req,
&never,
uv_ip4_addr("127.0.0.1", TEST_PORT),
never_cb);
ASSERT(r == 0);

/* One unref should set the loop ref count to zero. */
uv_unref(uv_default_loop());

/* Therefore this does not block */
uv_run(uv_default_loop());

return 0;
}
@@ -35,10 +35,15 @@ static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size);
static uv_tcp_t tcp_server;
static uv_tcp_t tcp_client;
static uv_tcp_t tcp_peer; /* client socket as accept()-ed by server */
static uv_write_t write_req;
static uv_connect_t connect_req;

static int write_cb_called;
static int write_cb_error_called;

typedef struct {
uv_write_t req;
uv_buf_t buf;
} write_req_t;


static void connection_cb(uv_stream_t* server, int status) {
@@ -75,40 +80,47 @@ static void connect_cb(uv_connect_t* req, int status) {
size_t size;
char* data;
int r;
write_req_t* wr;

ASSERT(req == &connect_req);
ASSERT(status == 0);

size = 10*1024*1024;
data = malloc(size);
ASSERT(data != NULL);

memset(data, '$', size);
buf = uv_buf_init(data, size);

write_req.data = data;

r = uv_write(&write_req, req->handle, &buf, 1, write_cb);
ASSERT(r == 0);

/* Write queue should have been updated. */
ASSERT(req->handle->write_queue_size > 0);

/* write_queue_size <= size, part may have already been written. */
ASSERT(req->handle->write_queue_size <= size);
while (1) {
size = 10 * 1024 * 1024;
data = malloc(size);
ASSERT(data != NULL);

memset(data, '$', size);
buf = uv_buf_init(data, size);

wr = (write_req_t*) malloc(sizeof *wr);
wr->buf = buf;
wr->req.data = data;

r = uv_write(&(wr->req), req->handle, &wr->buf, 1, write_cb);
ASSERT(r == 0);

if (req->handle->write_queue_size > 0) {
break;
}
}
}


static void write_cb(uv_write_t* req, int status) {
ASSERT(req == &write_req);
ASSERT(status == -1);
write_req_t* wr;
wr = (write_req_t*) req;

/* This is what this test is all about. */
ASSERT(tcp_client.write_queue_size == 0);
if (status == -1) {
write_cb_error_called++;
}

free(write_req.data);
if (req->handle->write_queue_size == 0) {
uv_close((uv_handle_t*)&tcp_client, NULL);
}

uv_close((uv_handle_t*)&tcp_client, NULL);
free(wr->buf.base);
free(wr);

write_cb_called++;
}
@@ -148,7 +160,9 @@ TEST_IMPL(tcp_write_error) {
r = uv_run(loop);
ASSERT(r == 0);

ASSERT(write_cb_called == 1);
ASSERT(write_cb_called > 0);
ASSERT(write_cb_error_called == 1);
ASSERT(tcp_client.write_queue_size == 0);

return 0;
}
@@ -130,3 +130,43 @@ TEST_IMPL(timer) {

return 0;
}


TEST_IMPL(timer_ref) {
uv_timer_t never;
int r;

/* A timer just initialized should count as one reference. */
r = uv_timer_init(uv_default_loop(), &never);
ASSERT(r == 0);

/* One unref should set the loop ref count to zero. */
uv_unref(uv_default_loop());

/* Therefore this does not block */
uv_run(uv_default_loop());

return 0;
}


TEST_IMPL(timer_ref2) {
uv_timer_t never;
int r;

/* A timer just initialized should count as one reference. */
r = uv_timer_init(uv_default_loop(), &never);
ASSERT(r == 0);

/* We start the timer, this should not effect the ref count. */
r = uv_timer_start(&never, never_cb, 1000, 1000);
ASSERT(r == 0);

/* One unref should set the loop ref count to zero. */
uv_unref(uv_default_loop());

/* Therefore this does not block */
uv_run(uv_default_loop());

return 0;
}
@@ -95,12 +95,12 @@ function Interface(input, output, completer) {
this.history = [];
this.historyIndex = -1;

var winSize = tty.getWindowSize(output.fd);
var winSize = output.getWindowSize();
exports.columns = winSize[1];

if (process.listeners('SIGWINCH').length === 0) {
process.on('SIGWINCH', function() {
var winSize = tty.getWindowSize(output.fd);
var winSize = output.getWindowSize();
exports.columns = winSize[1];
});
}
@@ -56,11 +56,7 @@ function hasOwnProperty(obj, prop) {

var context;

exports.disableColors = true;
if (process.platform != 'win32') {
exports.disableColors = process.env.NODE_DISABLE_COLORS ? true : false;
}

exports.disableColors = process.env.NODE_DISABLE_COLORS ? true : false;

// hack for require.resolve("./relative") to work properly.
module.filename = process.cwd() + '/repl';
File renamed without changes.
@@ -0,0 +1,375 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.

var assert = require('assert');
var inherits = require('util').inherits;
var net = require('net');
var TTY = process.binding('tty_wrap').TTY;
var isTTY = process.binding('tty_wrap').isTTY;

var stdinHandle;


exports.isatty = function(fd) {
return isTTY(fd);
};


exports.setRawMode = function(flag) {
assert.ok(stdinHandle, "stdin must be initialized before calling setRawMode");
stdinHandle.setRawMode(flag);
};


exports.getWindowSize = function() {
//throw new Error("implement me");
return 80;
};


exports.setWindowSize = function() {
throw new Error("implement me");
};


function ReadStream(fd) {
if (!(this instanceof ReadStream)) return new ReadStream(fd);
net.Socket.call(this, {
handle: new TTY(fd)
});

this.writable = false;

var self = this,
keypressListeners = this.listeners('keypress');

function onData(b) {
if (keypressListeners.length) {
self._emitKey(b);
} else {
// Nobody's watching anyway
self.removeListener('data', onData);
self.on('newListener', onNewListener);
}
}

function onNewListener(event) {
if (event == 'keypress') {
self.on('data', onData);
self.removeListener('newListener', onNewListener);
}
}

if (!stdinHandle) stdinHandle = this._handle;

this.on('newListener', onNewListener);
}
inherits(ReadStream, net.Socket);
exports.ReadStream = ReadStream;


ReadStream.prototype.isTTY = true;


/*
Some patterns seen in terminal key escape codes, derived from combos seen
at http://www.midnight-commander.org/browser/lib/tty/key.c
ESC letter
ESC [ letter
ESC [ modifier letter
ESC [ 1 ; modifier letter
ESC [ num char
ESC [ num ; modifier char
ESC O letter
ESC O modifier letter
ESC O 1 ; modifier letter
ESC N letter
ESC [ [ num ; modifier char
ESC [ [ 1 ; modifier letter
ESC ESC [ num char
ESC ESC O letter
- char is usually ~ but $ and ^ also happen with rxvt
- modifier is 1 +
(shift * 1) +
(left_alt * 2) +
(ctrl * 4) +
(right_alt * 8)
- two leading ESCs apparently mean the same as one leading ESC
*/


// Regexes used for ansi escape code splitting
var metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
var functionKeyCodeRe =
/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;


ReadStream.prototype._emitKey = function(s) {
var char,
key = {
name: undefined,
ctrl: false,
meta: false,
shift: false
},
parts;

if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString(this.encoding || 'utf-8');
} else {
s = s.toString(this.encoding || 'utf-8');
}
}

if (s === '\r' || s === '\n') {
// enter
key.name = 'enter';

} else if (s === '\t') {
// tab
key.name = 'tab';

} else if (s === '\b' || s === '\x7f' ||
s === '\x1b\x7f' || s === '\x1b\b') {
// backspace or ctrl+h
key.name = 'backspace';
key.meta = (s.charAt(0) === '\x1b');

} else if (s === '\x1b' || s === '\x1b\x1b') {
// escape key
key.name = 'escape';
key.meta = (s.length === 2);

} else if (s === ' ' || s === '\x1b ') {
key.name = 'space';
key.meta = (s.length === 2);

} else if (s <= '\x1a') {
// ctrl+letter
key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
key.ctrl = true;

} else if (s.length === 1 && s >= 'a' && s <= 'z') {
// lowercase letter
key.name = s;

} else if (s.length === 1 && s >= 'A' && s <= 'Z') {
// shift+letter
key.name = s.toLowerCase();
key.shift = true;

} else if (parts = metaKeyCodeRe.exec(s)) {
// meta+character key
key.name = parts[1].toLowerCase();
key.meta = true;
key.shift = /^[A-Z]$/.test(parts[1]);

} else if (parts = functionKeyCodeRe.exec(s)) {
// ansi escape sequence

// reassemble the key code leaving out leading \x1b's,
// the modifier key bitflag and any meaningless "1;" sequence
var code = (parts[1] || '') + (parts[2] || '') +
(parts[4] || '') + (parts[6] || ''),
modifier = (parts[3] || parts[5] || 1) - 1;

// Parse the key modifier
key.ctrl = !!(modifier & 4);
key.meta = !!(modifier & 10);
key.shift = !!(modifier & 1);

// Parse the key itself
switch (code) {
/* xterm/gnome ESC O letter */
case 'OP': key.name = 'f1'; break;
case 'OQ': key.name = 'f2'; break;
case 'OR': key.name = 'f3'; break;
case 'OS': key.name = 'f4'; break;

/* xterm/rxvt ESC [ number ~ */
case '[11~': key.name = 'f1'; break;
case '[12~': key.name = 'f2'; break;
case '[13~': key.name = 'f3'; break;
case '[14~': key.name = 'f4'; break;

/* common */
case '[15~': key.name = 'f5'; break;
case '[17~': key.name = 'f6'; break;
case '[18~': key.name = 'f7'; break;
case '[19~': key.name = 'f8'; break;
case '[20~': key.name = 'f9'; break;
case '[21~': key.name = 'f10'; break;
case '[23~': key.name = 'f11'; break;
case '[24~': key.name = 'f12'; break;

/* xterm ESC [ letter */
case '[A': key.name = 'up'; break;
case '[B': key.name = 'down'; break;
case '[C': key.name = 'right'; break;
case '[D': key.name = 'left'; break;
case '[E': key.name = 'clear'; break;
case '[F': key.name = 'end'; break;
case '[H': key.name = 'home'; break;

/* xterm/gnome ESC O letter */
case 'OA': key.name = 'up'; break;
case 'OB': key.name = 'down'; break;
case 'OC': key.name = 'right'; break;
case 'OD': key.name = 'left'; break;
case 'OE': key.name = 'clear'; break;
case 'OF': key.name = 'end'; break;
case 'OH': key.name = 'home'; break;

/* xterm/rxvt ESC [ number ~ */
case '[1~': key.name = 'home'; break;
case '[2~': key.name = 'insert'; break;
case '[3~': key.name = 'delete'; break;
case '[4~': key.name = 'end'; break;
case '[5~': key.name = 'pageup'; break;
case '[6~': key.name = 'pagedown'; break;

/* putty */
case '[[5~': key.name = 'pageup'; break;
case '[[6~': key.name = 'pagedown'; break;

/* rxvt */
case '[7~': key.name = 'home'; break;
case '[8~': key.name = 'end'; break;

/* rxvt keys with modifiers */
case '[a': key.name = 'up'; key.shift = true; break;
case '[b': key.name = 'down'; key.shift = true; break;
case '[c': key.name = 'right'; key.shift = true; break;
case '[d': key.name = 'left'; key.shift = true; break;
case '[e': key.name = 'clear'; key.shift = true; break;

case '[2$': key.name = 'insert'; key.shift = true; break;
case '[3$': key.name = 'delete'; key.shift = true; break;
case '[5$': key.name = 'pageup'; key.shift = true; break;
case '[6$': key.name = 'pagedown'; key.shift = true; break;
case '[7$': key.name = 'home'; key.shift = true; break;
case '[8$': key.name = 'end'; key.shift = true; break;

case 'Oa': key.name = 'up'; key.ctrl = true; break;
case 'Ob': key.name = 'down'; key.ctrl = true; break;
case 'Oc': key.name = 'right'; key.ctrl = true; break;
case 'Od': key.name = 'left'; key.ctrl = true; break;
case 'Oe': key.name = 'clear'; key.ctrl = true; break;

case '[2^': key.name = 'insert'; key.ctrl = true; break;
case '[3^': key.name = 'delete'; key.ctrl = true; break;
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
case '[7^': key.name = 'home'; key.ctrl = true; break;
case '[8^': key.name = 'end'; key.ctrl = true; break;

/* misc. */
case '[Z': key.name = 'tab'; key.shift = true; break;

}
} else if (s.length > 1 && s[0] !== '\x1b') {
// Got a longer-than-one string of characters.
// Probably a paste, since it wasn't a control sequence.
Array.prototype.forEach.call(s, this._emitKey, this);
return;
}

// Don't emit a key if no name was found
if (key.name === undefined) {
key = undefined;
}

if (s.length === 1) {
char = s;
}

if (key || char) {
this.emit('keypress', char, key);
}
};


function WriteStream(fd) {
if (!(this instanceof WriteStream)) return new WriteStream(fd);
net.Socket.call(this, {
handle: new TTY(fd)
});

this.readable = false;
}
inherits(WriteStream, net.Socket);
exports.WriteStream = WriteStream;


WriteStream.prototype.isTTY = true;


WriteStream.prototype.cursorTo = function(x, y) {
if (typeof x !== 'number' && typeof y !== 'number')
return;

if (typeof x !== 'number')
throw new Error("Can't set cursor row without also setting it's column");

if (typeof y !== 'number') {
this.write('\x1b[' + (x + 1) + 'G');
} else {
this.write('\x1b[' + (y + 1) + ';' + (x + 1) + 'H');
}
};


WriteStream.prototype.moveCursor = function(dx, dy) {
if (dx < 0) {
this.write('\x1b[' + (-dx) + 'D');
} else if (dx > 0) {
this.write('\x1b[' + dx + 'C');
}

if (dy < 0) {
this.write('\x1b[' + (-dy) + 'A');
} else if (dy > 0) {
this.write('\x1b[' + dy + 'B');
}
};


WriteStream.prototype.clearLine = function(dir) {
if (dir < 0) {
// to the beginning
this.write('\x1b[1K');
} else if (dir > 0) {
// to the end
this.write('\x1b[0K');
} else {
// entire line
this.write('\x1b[2K');
}
};


WriteStream.prototype.getWindowSize = function() {
return this._handle.getWindowSize();
};
@@ -43,8 +43,9 @@
'lib/timers_legacy.js',
'lib/timers_uv.js',
'lib/tls.js',
'lib/tty.js',
'lib/tty_legacy.js',
'lib/tty_posix.js',
'lib/tty_uv.js',
'lib/tty_win32.js',
'lib/url.js',
'lib/util.js',
@@ -108,10 +108,10 @@

} else {
var binding = process.binding('stdio');
var fd = binding.openStdin();
var Module = NativeModule.require('module');

if (NativeModule.require('tty').isatty(fd)) {
// If stdin is a TTY.
if (NativeModule.require('tty').isatty(0)) {
// REPL
Module.requireRepl().start();

@@ -223,43 +223,54 @@
process.__defineGetter__('stdout', function() {
if (stdout) return stdout;

var binding = process.binding('stdio'),
fd = binding.stdoutFD;
var tty_wrap = process.binding('tty_wrap');
var binding = process.binding('stdio');
var fd = 1;

// Note stdout._type is used for test-module-load-list.js

if (binding.isatty(fd)) {
var tty = NativeModule.require('tty');
stdout = new tty.WriteStream(fd);
stdout._type = "tty";

// FIXME Hack to have stdout not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
binding.unref();
stdout.on('close', function() {
binding.ref();
});
} else if (binding.isStdoutBlocking()) {
var fs = NativeModule.require('fs');
stdout = new fs.WriteStream(null, {fd: fd});
stdout._type = "fs";
} else {
var net = NativeModule.require('net');
stdout = new net.Stream(fd);

// FIXME Hack to have stdout not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
binding.unref();
stdout.on('close', function() {
binding.ref();
});

// FIXME Should probably have an option in net.Stream to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stdout.readable = false;
stdout._type = "pipe";
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
var tty = NativeModule.require('tty');
stdout = new tty.WriteStream(fd);
stdout._type = "tty";

// FIXME Hack to have stdout not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
binding.unref();
stdout.on('close', function() {
binding.ref();
});
break;

case 'FILE':
var fs = NativeModule.require('fs');
stdout = new fs.WriteStream(null, {fd: fd});
stdout._type = "fs";
break;

case 'PIPE':
var net = NativeModule.require('net');
stdout = new net.Stream(fd);

// FIXME Should probably have an option in net.Stream to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stdout.readable = false;
stdout._type = "pipe";

// FIXME Hack to have stdout not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
binding.unref();
stdout.on('close', function() {
binding.ref();
});
break;

default:
// Probably an error on in uv_guess_handle()
throw new Error("Implement me. Unknown stdout file type!");
}

// For supporting legacy API we put the FD here.
@@ -280,19 +291,30 @@
process.__defineGetter__('stdin', function() {
if (stdin) return stdin;

var binding = process.binding('stdio'),
fd = binding.openStdin();

if (binding.isatty(fd)) {
var tty = NativeModule.require('tty');
stdin = new tty.ReadStream(fd);
} else if (binding.isStdinBlocking()) {
var fs = NativeModule.require('fs');
stdin = new fs.ReadStream(null, {fd: fd});
} else {
var net = NativeModule.require('net');
stdin = new net.Stream(fd);
stdin.readable = true;
var tty_wrap = process.binding('tty_wrap');
var binding = process.binding('stdio');
var fd = 0;

switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
var tty = NativeModule.require('tty');
stdin = new tty.ReadStream(fd);
break;

case 'FILE':
var fs = NativeModule.require('fs');
stdin = new fs.ReadStream(null, {fd: fd});
break;

case 'PIPE':
var net = NativeModule.require('net');
stdin = new net.Stream(fd);
stdin.readable = true;
break;

default:
// Probably an error on in uv_guess_handle()
throw new Error("Implement me. Unknown stdin file type!");
}

// For supporting legacy API we put the FD here.
@@ -447,6 +469,9 @@
case 'net':
return process.features.uv ? 'net_uv' : 'net_legacy';

case 'tty':
return process.features.uv ? 'tty_uv' : 'tty_legacy';

case 'child_process':
return process.features.uv ? 'child_process_uv' :
'child_process_legacy';
@@ -21,6 +21,16 @@ using v8::Arguments;
using v8::Integer;
using v8::Undefined;

#define UNWRAP \
assert(!args.Holder().IsEmpty()); \
assert(args.Holder()->InternalFieldCount() > 0); \
TTYWrap* wrap = \
static_cast<TTYWrap*>(args.Holder()->GetPointerFromInternalField(0)); \
if (!wrap) { \
SetErrno(UV_EBADF); \
return scope.Close(Integer::New(-1)); \
}


class TTYWrap : StreamWrap {
public:
@@ -40,19 +50,80 @@ class TTYWrap : StreamWrap {
NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop);
NODE_SET_PROTOTYPE_METHOD(t, "write", StreamWrap::Write);

NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize);
NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode);

NODE_SET_METHOD(target, "isTTY", IsTTY);
NODE_SET_METHOD(target, "guessHandleType", GuessHandleType);

target->Set(String::NewSymbol("TTY"), t->GetFunction());
}

private:
static Handle<Value> GuessHandleType(const Arguments& args) {
HandleScope scope;
int fd = args[0]->Int32Value();
assert(fd >= 0);

uv_handle_type t = uv_guess_handle(fd);

switch (t) {
case UV_TTY:
return scope.Close(String::New("TTY"));

case UV_NAMED_PIPE:
return scope.Close(String::New("PIPE"));

case UV_FILE:
return scope.Close(String::New("FILE"));

default:
assert(0);
return v8::Undefined();
}
}

static Handle<Value> IsTTY(const Arguments& args) {
HandleScope scope;
int fd = args[0]->Int32Value();
assert(fd >= 0);
return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False();
}

static Handle<Value> GetWindowSize(const Arguments& args) {
HandleScope scope;

UNWRAP

int width, height;
int r = uv_tty_get_winsize(&wrap->handle_, &width, &height);

if (r) {
SetErrno(uv_last_error(uv_default_loop()).code);
return v8::Undefined();
}

Local<v8::Array> a = v8::Array::New(2);
a->Set(0, Integer::New(width));
a->Set(1, Integer::New(height));

return scope.Close(a);
}

static Handle<Value> SetRawMode(const Arguments& args) {
HandleScope scope;

UNWRAP

int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue());

if (r) {
SetErrno(uv_last_error(uv_default_loop()).code);
}

return scope.Close(Integer::New(r));
}

static Handle<Value> New(const Arguments& args) {
HandleScope scope;

@@ -78,25 +78,26 @@ if (!process.features.uv) {
case 'fs':
expected = expected.concat([
'NativeModule console',
'Binding tty_wrap',
]);
break;

case 'tty':
expected = expected.concat([
'NativeModule console',
'NativeModule tty',
'NativeModule tty_posix',
'Binding tty_wrap',
'NativeModule tty_uv',
'NativeModule net_uv',
'NativeModule timers_uv',
'Binding timer_wrap',
'NativeModule _linklist',
'Binding pipe_wrap',
'NativeModule _linklist'
]);
break;

case 'pipe':
expected = expected.concat([
'NativeModule console',
'Binding tty_wrap',
'NativeModule net_uv',
'NativeModule timers_uv',
'Binding timer_wrap',