diff --git a/doc/api/_toc.markdown b/doc/api/_toc.markdown index bed9029155..6c72559dc9 100644 --- a/doc/api/_toc.markdown +++ b/doc/api/_toc.markdown @@ -26,6 +26,7 @@ * [VM](vm.html) * [Child Processes](child_processes.html) * [Assertion Testing](assert.html) +* [TTY](tty.html) * Appendixes * [Appendix 1: Recommended Third-party Modules](appendix_1.html) * [Appendix 2: Deprecated API's](appendix_2.html) diff --git a/doc/api/tty.markdown b/doc/api/tty.markdown new file mode 100644 index 0000000000..5a6ae7923a --- /dev/null +++ b/doc/api/tty.markdown @@ -0,0 +1,40 @@ +## TTY + +Use `require('tty')` to access this module. + + +### tty.open(path, args=[]) + +Spawns a new process with the executable pointed to by `path` as the session +leader to a new pseudo terminal. + +Returns an array `[slaveFD, childProcess]`. `slaveFD` is the file descriptor +of the slave end of the pseudo terminal. `childProcess` is a child process +object. + + +### tty.isatty(fd) + +Returns `true` or `false` depending on if the `fd` is associated with a +terminal. + + +### tty.setRawMode(mode) + +`mode` should be `true` or `false`. This sets the properies of the current +process's stdin fd to act either as a raw device or default. + + +### tty.getColumns() + +Returns the number of columns associated with the current process's TTY. + +Note that each time this number is changed the process receives a `SIGWINCH` +signal. So you can keep a cache of it like this: + + var columns = tty.getColumns(); + process.on('SIGWINCH', function() { + columns = tty.getColumns(); + }); + + diff --git a/lib/readline.js b/lib/readline.js index b96499e228..cc62922623 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -10,7 +10,7 @@ var kBufSize = 10 * 1024; var util = require('util'); var inherits = require('util').inherits; var EventEmitter = require('events').EventEmitter; -var stdio = process.binding('stdio'); +var tty = require('tty'); exports.createInterface = function(output, completer) { @@ -40,7 +40,7 @@ function Interface(output, completer) { this.setPrompt('> '); - this.enabled = stdio.isatty(output.fd); + this.enabled = tty.isatty(output.fd); if (parseInt(process.env['NODE_NO_READLINE'], 10)) { this.enabled = false; @@ -56,7 +56,7 @@ function Interface(output, completer) { this.line = ''; // Check process.env.TERM ? - stdio.setRawMode(true); + tty.setRawMode(true); this.enabled = true; // Cursor position on the line. @@ -65,11 +65,11 @@ function Interface(output, completer) { this.history = []; this.historyIndex = -1; - exports.columns = process.binding('stdio').getColumns(); + exports.columns = tty.getColumns(); if (process.listeners('SIGWINCH').length === 0) { process.on('SIGWINCH', function() { - exports.columns = process.binding('stdio').getColumns(); + exports.columns = tty.getColumns(); }); } } @@ -133,7 +133,7 @@ Interface.prototype._refreshLine = function() { Interface.prototype.close = function(d) { if (this.enabled) { - stdio.setRawMode(false); + tty.setRawMode(false); } this.emit('close'); this._closed = true; diff --git a/lib/tty.js b/lib/tty.js new file mode 100644 index 0000000000..ebbb41617b --- /dev/null +++ b/lib/tty.js @@ -0,0 +1,28 @@ +var spawn = require('child_process').spawn; +var binding = process.binding('stdio'); + + +exports.isatty = binding.isatty; +exports.setRawMode = binding.setRawMode; +exports.getColumns = binding.getColumns; + + +exports.open = function(path, args) { + var fds = binding.openpty(); + + var masterFD = fds[1]; + var slaveFD = fds[0]; + + var env = { TERM: 'vt100' }; + for (var k in process.env) { + env[k] = process.env[k]; + } + + child = spawn(path, args, env, [masterFD, masterFD, masterFD]); + + return [slaveFD, child]; +}; + + + + diff --git a/src/node_stdio.cc b/src/node_stdio.cc index 4943ead3c8..b4e13c8986 100644 --- a/src/node_stdio.cc +++ b/src/node_stdio.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -189,6 +190,26 @@ static Handle IsStdoutBlocking(const Arguments& args) { } +static Handle OpenPTY(const Arguments& args) { + HandleScope scope; + + int master_fd, slave_fd; + + int r = openpty(&master_fd, &slave_fd, NULL, NULL, NULL); + + if (r == -1) { + return ThrowException(ErrnoException(errno, "openpty")); + } + + Local a = Array::New(2); + + a->Set(0, Integer::New(master_fd)); + a->Set(1, Integer::New(slave_fd)); + + return scope.Close(a); +} + + void Stdio::Flush() { if (stdin_flags != -1) { fcntl(STDIN_FILENO, F_SETFL, stdin_flags & ~O_NONBLOCK); @@ -232,6 +253,7 @@ void Stdio::Initialize(v8::Handle target) { NODE_SET_METHOD(target, "getColumns", GetColumns); NODE_SET_METHOD(target, "getRows", GetRows); NODE_SET_METHOD(target, "isatty", IsATTY); + NODE_SET_METHOD(target, "openpty", OpenPTY); struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/wscript b/wscript index 0698f0b60a..1790a9bb72 100644 --- a/wscript +++ b/wscript @@ -233,6 +233,9 @@ def configure(conf): else: Options.options.use_openssl = conf.env["USE_OPENSSL"] = False + conf.check(lib='util', libpath=['/usr/lib', '/usr/local/lib'], + uselib_store='UTIL') + # normalize DEST_CPU from --dest-cpu, DEST_CPU or built-in value if Options.options.dest_cpu and Options.options.dest_cpu: conf.env['DEST_CPU'] = canonical_cpu_type(Options.options.dest_cpu) @@ -570,7 +573,7 @@ def build(bld): node = bld.new_task_gen("cxx", product_type) node.name = "node" node.target = "node" - node.uselib = 'RT EV OPENSSL CARES EXECINFO DL KVM SOCKET NSL' + node.uselib = 'RT EV OPENSSL CARES EXECINFO DL KVM SOCKET NSL UTIL' node.add_objects = 'eio http_parser' if product_type_is_lib: node.install_path = '${PREFIX}/lib'