diff --git a/.gitignore b/.gitignore index 756db55..91bc99a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ Makefile.gyp *.filters *.user *.project -test.js \ No newline at end of file +.idea +test.js diff --git a/lib/pty.js b/lib/pty.js index 70b1c96..2622efe 100644 --- a/lib/pty.js +++ b/lib/pty.js @@ -5,9 +5,11 @@ */ var extend = require('extend'); +var EventEmitter = require('events').EventEmitter; var pty = require('../build/Release/pty.node'); var net = require('net'); var tty = require('tty'); +var nextTick = global.setImmediate || process.nextTick; var version = process.versions.node.split('.').map(function(n) { return +(n + '').split('-')[0]; @@ -37,6 +39,8 @@ function Terminal(file, args, opt) { , name , cols , rows + , uid + , gid , term; // backward compatibility @@ -50,6 +54,9 @@ function Terminal(file, args, opt) { args = []; } + // for 'close' + this._internalee = new EventEmitter; + // arguments args = args || []; file = file || 'sh'; @@ -58,6 +65,9 @@ function Terminal(file, args, opt) { cols = opt.cols || 80; rows = opt.rows || 24; + uid = opt.uid != null ? opt.uid : -1; + gid = opt.gid != null ? opt.gid : -1; + opt.env = opt.env || process.env; env = extend({}, opt.env); @@ -88,19 +98,35 @@ function Terminal(file, args, opt) { cwd = opt.cwd || process.cwd(); name = opt.name || env.TERM || 'xterm'; env.TERM = name; - env.LINES = rows + ''; - env.COLUMNS = cols + ''; + // XXX Shouldn't be necessary: + // env.LINES = rows + ''; + // env.COLUMNS = cols + ''; env = environ(env); + function onexit(code, signal) { + // XXX Sometimes a data event is emitted + // after exit. Wait til socket is destroyed. + if (!self._emittedClose) { + if (self._boundClose) return; + self._boundClose = true; + self.once('close', function() { + self.emit('exit', code, signal); + }); + return; + } + self.emit('exit', code, signal); + } + // fork - term = opt.uid && opt.gid - ? pty.fork(file, args, env, cwd, cols, rows, opt.uid, opt.gid) - : pty.fork(file, args, env, cwd, cols, rows); + term = pty.fork(file, args, env, cwd, cols, rows, uid, gid, onexit); this.socket = TTYStream(term.fd); this.socket.setEncoding('utf8'); - this.socket.resume(); + + if (opt.resume !== false) { + this.socket.resume(); + } // setup this.socket.on('error', function(err) { @@ -112,10 +138,10 @@ function Terminal(file, args, opt) { // close self._close(); // EIO on exit from fs.ReadStream: - if (!self._emittedExit) { - self._emittedExit = true; + if (!self._emittedClose) { + self._emittedClose = true; Terminal.total--; - self.emit('exit', null); + self.emit('close'); } // EIO, happens when someone closes our child @@ -146,13 +172,13 @@ function Terminal(file, args, opt) { this.writable = true; Terminal.total++; - // XXX This never gets emitted with v0.12.0 + this.socket.on('close', function() { - if (self._emittedExit) return; - self._emittedExit = true; + if (self._emittedClose) return; + self._emittedClose = true; Terminal.total--; self._close(); - self.emit('exit', null); + self.emit('close'); }); env = null; @@ -268,11 +294,18 @@ Terminal.prototype.setEncoding = function(enc) { Terminal.prototype.addListener = Terminal.prototype.on = function(type, func) { + if (type === 'close') { + this._internalee.on('close', func); + return this; + } this.socket.on(type, func); return this; }; -Terminal.prototype.emit = function() { +Terminal.prototype.emit = function(evt) { + if (evt === 'close') { + return this._internalee.emit.apply(this._internalee, arguments); + } return this.socket.emit.apply(this.socket, arguments); }; diff --git a/package.json b/package.json index 4e4d023..2f5407b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "pty.js", "description": "Pseudo terminals for node.", "author": "Christopher Jeffrey", - "version": "0.2.7-1", + "version": "0.3.0", + "license": "MIT", "main": "./index.js", "repository": "git://github.com/chjj/pty.js.git", "homepage": "https://github.com/chjj/pty.js", @@ -24,7 +25,7 @@ ], "dependencies": { "extend": "~1.2.1", - "nan": "^1.8.4" + "nan": "2.2.0" }, "devDependencies": { "mocha": "~1.17.1" diff --git a/src/unix/pty.cc b/src/unix/pty.cc index 60bc64c..9bb9ed3 100644 --- a/src/unix/pty.cc +++ b/src/unix/pty.cc @@ -13,17 +13,21 @@ * man forkpty */ +/** + * Includes + */ + #include "nan.h" +#include #include #include #include -#include #include #include -#include #include +#include #include /* forkpty */ @@ -32,8 +36,8 @@ #include #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) /** - * From node v0.10.28 (at least?) there is also a "util.h" in node/src, which would confuse - * the compiler when looking for "util.h". + * From node v0.10.28 (at least?) there is also a "util.h" in node/src, which + * would confuse the compiler when looking for "util.h". */ #if NODE_VERSION_AT_LEAST(0, 10, 28) #include <../include/util.h> @@ -68,14 +72,38 @@ extern char **environ; #include #endif +/** + * Namespace + */ + using namespace node; using namespace v8; +/** + * Structs + */ + +struct pty_baton { + Nan::Persistent cb; + int exit_code; + int signal_code; + pid_t pid; + uv_async_t async; + uv_thread_t tid; +}; + +/** + * Methods + */ + NAN_METHOD(PtyFork); NAN_METHOD(PtyOpen); NAN_METHOD(PtyResize); NAN_METHOD(PtyGetProc); -NAN_METHOD(PtyKill); + +/** + * Functions + */ static int pty_execvpe(const char *, char **, char **); @@ -96,77 +124,83 @@ pty_forkpty(int *, char *, const struct termios *, const struct winsize *); -extern "C" void -init(Handle); +static void +pty_waitpid(void *); + +static void +#if NODE_VERSION_AT_LEAST(0, 11, 0) +pty_after_waitpid(uv_async_t *); +#else +pty_after_waitpid(uv_async_t *, int); +#endif + +static void +pty_after_close(uv_handle_t *); /** * PtyFork - * pty.fork(file, args, env, cwd, cols, rows[, uid, gid]) + * pty.fork(file, args, env, cwd, cols, rows, uid, gid, onexit) */ NAN_METHOD(PtyFork) { - NanScope(); - - if (args.Length() < 6 - || !args[0]->IsString() - || !args[1]->IsArray() - || !args[2]->IsArray() - || !args[3]->IsString() - || !args[4]->IsNumber() - || !args[5]->IsNumber() - || (args.Length() >= 8 && !args[6]->IsNumber()) - || (args.Length() >= 8 && !args[7]->IsNumber())) { - return NanThrowError( - "Usage: pty.fork(file, args, env, cwd, cols, rows[, uid, gid])"); + Nan::HandleScope scope; + + if (info.Length() != 9 + || !info[0]->IsString() // file + || !info[1]->IsArray() // args + || !info[2]->IsArray() // env + || !info[3]->IsString() // cwd + || !info[4]->IsNumber() // cols + || !info[5]->IsNumber() // rows + || !info[6]->IsNumber() // uid + || !info[7]->IsNumber() // gid + || !info[8]->IsFunction() // onexit + ) { + return Nan::ThrowError( + "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, onexit)"); } - // node/src/node_child_process.cc - // file - String::Utf8Value file(args[0]->ToString()); + String::Utf8Value file(info[0]->ToString()); // args int i = 0; - Local argv_ = Local::Cast(args[1]); + Local argv_ = Local::Cast(info[1]); int argc = argv_->Length(); int argl = argc + 1 + 1; char **argv = new char*[argl]; argv[0] = strdup(*file); argv[argl-1] = NULL; for (; i < argc; i++) { - String::Utf8Value arg(argv_->Get(NanNew(i))->ToString()); + String::Utf8Value arg(argv_->Get(Nan::New(i))->ToString()); argv[i+1] = strdup(*arg); } // env i = 0; - Local env_ = Local::Cast(args[2]); + Local env_ = Local::Cast(info[2]); int envc = env_->Length(); char **env = new char*[envc+1]; env[envc] = NULL; for (; i < envc; i++) { - String::Utf8Value pair(env_->Get(NanNew(i))->ToString()); + String::Utf8Value pair(env_->Get(Nan::New(i))->ToString()); env[i] = strdup(*pair); } // cwd - String::Utf8Value cwd_(args[3]->ToString()); + String::Utf8Value cwd_(info[3]->ToString()); char *cwd = strdup(*cwd_); // size struct winsize winp; - winp.ws_col = args[4]->IntegerValue(); - winp.ws_row = args[5]->IntegerValue(); + winp.ws_col = info[4]->IntegerValue(); + winp.ws_row = info[5]->IntegerValue(); winp.ws_xpixel = 0; winp.ws_ypixel = 0; // uid / gid - int uid = -1; - int gid = -1; - if (args.Length() >= 8) { - uid = args[6]->IntegerValue(); - gid = args[7]->IntegerValue(); - } + int uid = info[6]->IntegerValue(); + int gid = info[7]->IntegerValue(); // fork the pty int master = -1; @@ -183,7 +217,7 @@ NAN_METHOD(PtyFork) { switch (pid) { case -1: - return NanThrowError("forkpty(3) failed."); + return Nan::ThrowError("forkpty(3) failed."); case 0: if (strlen(cwd)) chdir(cwd); @@ -204,18 +238,35 @@ NAN_METHOD(PtyFork) { _exit(1); default: if (pty_nonblock(master) == -1) { - return NanThrowError("Could not set master fd to nonblocking."); + return Nan::ThrowError("Could not set master fd to nonblocking."); } - Local obj = NanNew(); - obj->Set(NanNew("fd"), NanNew(master)); - obj->Set(NanNew("pid"), NanNew(pid)); - obj->Set(NanNew("pty"), NanNew(name)); - - NanReturnValue(obj); + Local obj = Nan::New(); + Nan::Set(obj, + Nan::New("fd").ToLocalChecked(), + Nan::New(master)); + Nan::Set(obj, + Nan::New("pid").ToLocalChecked(), + Nan::New(pid)); + Nan::Set(obj, + Nan::New("pty").ToLocalChecked(), + Nan::New(name).ToLocalChecked()); + + pty_baton *baton = new pty_baton(); + baton->exit_code = 0; + baton->signal_code = 0; + baton->cb.Reset(Local::Cast(info[8])); + baton->pid = pid; + baton->async.data = baton; + + uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid); + + uv_thread_create(&baton->tid, pty_waitpid, static_cast(baton)); + + return info.GetReturnValue().Set(obj); } - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } /** @@ -224,18 +275,18 @@ NAN_METHOD(PtyFork) { */ NAN_METHOD(PtyOpen) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 2 - || !args[0]->IsNumber() - || !args[1]->IsNumber()) { - return NanThrowError("Usage: pty.open(cols, rows)"); + if (info.Length() != 2 + || !info[0]->IsNumber() + || !info[1]->IsNumber()) { + return Nan::ThrowError("Usage: pty.open(cols, rows)"); } // size struct winsize winp; - winp.ws_col = args[0]->IntegerValue(); - winp.ws_row = args[1]->IntegerValue(); + winp.ws_col = info[0]->IntegerValue(); + winp.ws_row = info[1]->IntegerValue(); winp.ws_xpixel = 0; winp.ws_ypixel = 0; @@ -245,23 +296,29 @@ NAN_METHOD(PtyOpen) { int ret = pty_openpty(&master, &slave, name, NULL, &winp); if (ret == -1) { - return NanThrowError("openpty(3) failed."); + return Nan::ThrowError("openpty(3) failed."); } if (pty_nonblock(master) == -1) { - return NanThrowError("Could not set master fd to nonblocking."); + return Nan::ThrowError("Could not set master fd to nonblocking."); } if (pty_nonblock(slave) == -1) { - return NanThrowError("Could not set slave fd to nonblocking."); + return Nan::ThrowError("Could not set slave fd to nonblocking."); } - Local obj = NanNew(); - obj->Set(NanNew("master"), NanNew(master)); - obj->Set(NanNew("slave"), NanNew(slave)); - obj->Set(NanNew("pty"), NanNew(name)); - - NanReturnValue(obj); + Local obj = Nan::New(); + Nan::Set(obj, + Nan::New("master").ToLocalChecked(), + Nan::New(master)); + Nan::Set(obj, + Nan::New("slave").ToLocalChecked(), + Nan::New(slave)); + Nan::Set(obj, + Nan::New("pty").ToLocalChecked(), + Nan::New(name).ToLocalChecked()); + + return info.GetReturnValue().Set(obj); } /** @@ -270,28 +327,28 @@ NAN_METHOD(PtyOpen) { */ NAN_METHOD(PtyResize) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 3 - || !args[0]->IsNumber() - || !args[1]->IsNumber() - || !args[2]->IsNumber()) { - return NanThrowError("Usage: pty.resize(fd, cols, rows)"); + if (info.Length() != 3 + || !info[0]->IsNumber() + || !info[1]->IsNumber() + || !info[2]->IsNumber()) { + return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)"); } - int fd = args[0]->IntegerValue(); + int fd = info[0]->IntegerValue(); struct winsize winp; - winp.ws_col = args[1]->IntegerValue(); - winp.ws_row = args[2]->IntegerValue(); + winp.ws_col = info[1]->IntegerValue(); + winp.ws_row = info[2]->IntegerValue(); winp.ws_xpixel = 0; winp.ws_ypixel = 0; if (ioctl(fd, TIOCSWINSZ, &winp) == -1) { - return NanThrowError("ioctl(2) failed."); + return Nan::ThrowError("ioctl(2) failed."); } - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } /** @@ -301,55 +358,28 @@ NAN_METHOD(PtyResize) { */ NAN_METHOD(PtyGetProc) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 2 - || !args[0]->IsNumber() - || !args[1]->IsString()) { - return NanThrowError("Usage: pty.process(fd, tty)"); + if (info.Length() != 2 + || !info[0]->IsNumber() + || !info[1]->IsString()) { + return Nan::ThrowError("Usage: pty.process(fd, tty)"); } - int fd = args[0]->IntegerValue(); + int fd = info[0]->IntegerValue(); - String::Utf8Value tty_(args[1]->ToString()); + String::Utf8Value tty_(info[1]->ToString()); char *tty = strdup(*tty_); char *name = pty_getproc(fd, tty); free(tty); if (name == NULL) { - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } - Local name_ = NanNew(name); + Local name_ = Nan::New(name).ToLocalChecked(); free(name); - NanReturnValue(name_); -} - -/** - * PtyKill - * Gracefully terminates childrens - * pty.process(fd, tty) - */ - -NAN_METHOD(PtyKill) { - NanScope(); - - if (args.Length() < 1) { - return ThrowException( - Exception::TypeError(String::New("First argument must be a number")) - ); - } - - Local integer = args[0]->ToInteger(); - int32_t seq = integer->Value(); - pid_t pid = (pid_t)seq; - int status; - - if(waitpid(pid, &status, 0) == pid) { - } else { - kill(pid, SIGTERM); - } - NanReturnUndefined(); + return info.GetReturnValue().Set(name_); } /** @@ -378,6 +408,83 @@ pty_nonblock(int fd) { return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } +/** + * pty_waitpid + * Wait for SIGCHLD to read exit status. + */ + +static void +pty_waitpid(void *data) { + int ret; + int stat_loc; + + pty_baton *baton = static_cast(data); + + errno = 0; + + if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) { + if (ret == -1 && errno == EINTR) { + return pty_waitpid(baton); + } + if (ret == -1 && errno == ECHILD) { + // XXX node v0.8.x seems to have this problem. + // waitpid is already handled elsewhere. + ; + } else { + assert(false); + } + } + + if (WIFEXITED(stat_loc)) { + baton->exit_code = WEXITSTATUS(stat_loc); // errno? + } + + if (WIFSIGNALED(stat_loc)) { + baton->signal_code = WTERMSIG(stat_loc); + } + + uv_async_send(&baton->async); +} + +/** + * pty_after_waitpid + * Callback after exit status has been read. + */ + +static void +#if NODE_VERSION_AT_LEAST(0, 11, 0) +pty_after_waitpid(uv_async_t *async) { +#else +pty_after_waitpid(uv_async_t *async, int unhelpful) { +#endif + Nan::HandleScope scope; + pty_baton *baton = static_cast(async->data); + + Local argv[] = { + Nan::New(baton->exit_code), + Nan::New(baton->signal_code), + }; + + Local cb = Nan::New(baton->cb); + baton->cb.Reset(); + memset(&baton->cb, -1, sizeof(baton->cb)); + Nan::Callback(cb).Call(Nan::GetCurrentContext()->Global(), 2, argv); + + uv_close((uv_handle_t *)async, pty_after_close); +} + +/** + * pty_after_close + * uv_close() callback - free handle data + */ + +static void +pty_after_close(uv_handle_t *handle) { + uv_async_t *async = (uv_async_t *)handle; + pty_baton *baton = static_cast(async->data); + delete baton; +} + /** * pty_getproc * Taken from tmux. @@ -543,7 +650,9 @@ pty_forkpty(int *amaster, char *name, #if defined(TIOCSCTTY) // glibc does this - if (ioctl(slave, TIOCSCTTY, NULL) == -1) _exit(1); + if (ioctl(slave, TIOCSCTTY, NULL) == -1) { + _exit(1); + } #endif dup2(slave, 0); @@ -568,14 +677,20 @@ pty_forkpty(int *amaster, char *name, * Init */ -extern "C" void -init(Handle target) { - NanScope(); - NODE_SET_METHOD(target, "fork", PtyFork); - NODE_SET_METHOD(target, "open", PtyOpen); - NODE_SET_METHOD(target, "resize", PtyResize); - NODE_SET_METHOD(target, "process", PtyGetProc); - NODE_SET_METHOD(target, "kill", PtyKill); +NAN_MODULE_INIT(init) { + Nan::HandleScope scope; + Nan::Set(target, + Nan::New("fork").ToLocalChecked(), + Nan::New(PtyFork)->GetFunction()); + Nan::Set(target, + Nan::New("open").ToLocalChecked(), + Nan::New(PtyOpen)->GetFunction()); + Nan::Set(target, + Nan::New("resize").ToLocalChecked(), + Nan::New(PtyResize)->GetFunction()); + Nan::Set(target, + Nan::New("process").ToLocalChecked(), + Nan::New(PtyGetProc)->GetFunction()); } NODE_MODULE(pty, init) diff --git a/src/win/pty.cc b/src/win/pty.cc index 29e48c1..51fd4f2 100644 --- a/src/win/pty.cc +++ b/src/win/pty.cc @@ -177,21 +177,21 @@ x64) http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-2 */ static NAN_METHOD(PtyOpen) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 4 - || !args[0]->IsString() // dataPipe - || !args[1]->IsNumber() // cols - || !args[2]->IsNumber() // rows - || !args[3]->IsBoolean()) // debug + if (info.Length() != 4 + || !info[0]->IsString() // dataPipe + || !info[1]->IsNumber() // cols + || !info[2]->IsNumber() // rows + || !info[3]->IsBoolean()) // debug { - return NanThrowError("Usage: pty.open(dataPipe, cols, rows, debug)"); + return Nan::ThrowError("Usage: pty.open(dataPipe, cols, rows, debug)"); } - std::wstring pipeName = to_wstring(String::Utf8Value(args[0]->ToString())); - int cols = args[1]->Int32Value(); - int rows = args[2]->Int32Value(); - bool debug = args[3]->ToBoolean()->IsTrue(); + std::wstring pipeName = to_wstring(String::Utf8Value(info[0]->ToString())); + int cols = info[1]->Int32Value(); + int rows = info[2]->Int32Value(); + bool debug = info[3]->ToBoolean()->IsTrue(); // Enable/disable debugging SetEnvironmentVariable(WINPTY_DBG_VARIABLE, debug ? "1" : NULL); // NULL = deletes variable @@ -206,12 +206,12 @@ static NAN_METHOD(PtyOpen) { ptyHandles.insert(ptyHandles.end(), pc); // Pty object values. - Local marshal = NanNew(); - marshal->Set(NanNew("pid"), NanNew((int)pc->controlPipe)); - marshal->Set(NanNew("pty"), NanNew(InterlockedIncrement(&ptyCounter))); - marshal->Set(NanNew("fd"), NanNew(-1)); + Local marshal = Nan::New(); + marshal->Set(Nan::New("pid").ToLocalChecked(), Nan::New((int)pc->controlPipe)); + marshal->Set(Nan::New("pty").ToLocalChecked(), Nan::New(InterlockedIncrement(&ptyCounter))); + marshal->Set(Nan::New("fd").ToLocalChecked(), Nan::New(-1)); - NanReturnValue(marshal); + return info.GetReturnValue().Set(marshal); } /* @@ -220,34 +220,33 @@ static NAN_METHOD(PtyOpen) { */ static NAN_METHOD(PtyStartProcess) { - NanScope(); - - if (args.Length() != 5 - || !args[0]->IsNumber() // pid - || !args[1]->IsString() // file - || !args[2]->IsString() // cmdline - || !args[3]->IsArray() // env - || !args[4]->IsString()) // cwd + Nan::HandleScope scope; + + if (info.Length() != 5 + || !info[0]->IsNumber() // pid + || !info[1]->IsString() // file + || !info[2]->IsString() // cmdline + || !info[3]->IsArray() // env + || !info[4]->IsString()) // cwd { - return NanThrowError( + return Nan::ThrowError( "Usage: pty.startProcess(pid, file, cmdline, env, cwd)"); } - Handle exception; std::stringstream why; // Get winpty_t by control pipe handle - int pid = args[0]->Int32Value(); + int pid = info[0]->Int32Value(); winpty_t *pc = get_pipe_handle(pid); assert(pc != nullptr); - const wchar_t *filename = to_wstring(String::Utf8Value(args[1]->ToString())); - const wchar_t *cmdline = to_wstring(String::Utf8Value(args[2]->ToString())); - const wchar_t *cwd = to_wstring(String::Utf8Value(args[4]->ToString())); + const wchar_t *filename = to_wstring(String::Utf8Value(info[1]->ToString())); + const wchar_t *cmdline = to_wstring(String::Utf8Value(info[2]->ToString())); + const wchar_t *cwd = to_wstring(String::Utf8Value(info[4]->ToString())); // create environment block wchar_t *env = NULL; - const Handle envValues = Handle::Cast(args[3]); + const Handle envValues = Handle::Cast(info[3]); if(!envValues.IsEmpty()) { std::wstringstream envBlock; @@ -287,13 +286,13 @@ static NAN_METHOD(PtyStartProcess) { int result = winpty_start_process(pc, shellpath.c_str(), cmdline, cwd, env); if(result != 0) { why << "Unable to start terminal process. Win32 error code: " << result; - exception = NanThrowError(why.str().c_str()); + Nan::ThrowError(why.str().c_str()); } goto cleanup; invalid_filename: why << "File not found: " << shellpath_; - exception = NanThrowError(why.str().c_str()); + Nan::ThrowError(why.str().c_str()); goto cleanup; cleanup: @@ -302,11 +301,7 @@ static NAN_METHOD(PtyStartProcess) { delete cwd; delete env; - if(!exception.IsEmpty()) { - return exception; - } - - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } /* @@ -314,26 +309,26 @@ static NAN_METHOD(PtyStartProcess) { * pty.resize(pid, cols, rows); */ static NAN_METHOD(PtyResize) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 3 - || !args[0]->IsNumber() // pid - || !args[1]->IsNumber() // cols - || !args[2]->IsNumber()) // rows + if (info.Length() != 3 + || !info[0]->IsNumber() // pid + || !info[1]->IsNumber() // cols + || !info[2]->IsNumber()) // rows { - return NanThrowError("Usage: pty.resize(pid, cols, rows)"); + return Nan::ThrowError("Usage: pty.resize(pid, cols, rows)"); } - int handle = args[0]->Int32Value(); - int cols = args[1]->Int32Value(); - int rows = args[2]->Int32Value(); + int handle = info[0]->Int32Value(); + int cols = info[1]->Int32Value(); + int rows = info[2]->Int32Value(); winpty_t *pc = get_pipe_handle(handle); assert(pc != nullptr); assert(0 == winpty_set_size(pc, cols, rows)); - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } /* @@ -341,15 +336,15 @@ static NAN_METHOD(PtyResize) { * pty.kill(pid); */ static NAN_METHOD(PtyKill) { - NanScope(); + Nan::HandleScope scope; - if (args.Length() != 1 - || !args[0]->IsNumber()) // pid + if (info.Length() != 1 + || !info[0]->IsNumber()) // pid { - return NanThrowError("Usage: pty.kill(pid)"); + return Nan::ThrowError("Usage: pty.kill(pid)"); } - int handle = args[0]->Int32Value(); + int handle = info[0]->Int32Value(); winpty_t *pc = get_pipe_handle(handle); @@ -357,7 +352,7 @@ static NAN_METHOD(PtyKill) { winpty_exit(pc); assert(true == remove_pipe_handle(handle)); - NanReturnUndefined(); + return info.GetReturnValue().SetUndefined(); } /** @@ -365,11 +360,11 @@ static NAN_METHOD(PtyKill) { */ extern "C" void init(Handle target) { - NanScope(); - NODE_SET_METHOD(target, "open", PtyOpen); - NODE_SET_METHOD(target, "startProcess", PtyStartProcess); - NODE_SET_METHOD(target, "resize", PtyResize); - NODE_SET_METHOD(target, "kill", PtyKill); + Nan::HandleScope scope; + Nan::SetMethod(target, "open", PtyOpen); + Nan::SetMethod(target, "startProcess", PtyStartProcess); + Nan::SetMethod(target, "resize", PtyResize); + Nan::SetMethod(target, "kill", PtyKill); }; NODE_MODULE(pty, init); diff --git a/test/index.js b/test/index.js index c6eef2c..9716a63 100644 --- a/test/index.js +++ b/test/index.js @@ -61,4 +61,14 @@ describe('Pty', function() { setTimeout(testCase.test.bind(term), 1000); }); }); + + it('should support starting with a paused socket', function (done) { + var term = pty.spawn('echo', ['Immediate output'], { resume: false }); + setTimeout(function () { + term.stdout.on('data', function (chunk) { + assert.equal(chunk.trim(), 'Immediate output'); + done(); + }); + }, 1); + }); });