Introduce require('tty')
authorRyan Dahl <ry@tinyclouds.org>
Fri, 26 Nov 2010 03:09:28 +0000 (19:09 -0800)
committerRyan Dahl <ry@tinyclouds.org>
Fri, 3 Dec 2010 02:06:43 +0000 (18:06 -0800)
You may need to reconfigure after this commit due to the new library.

doc/api/_toc.markdown
doc/api/tty.markdown [new file with mode: 0644]
lib/readline.js
lib/tty.js [new file with mode: 0644]
src/node_stdio.cc
wscript

index bed902915552f7784b95a67eb3fc1df70d374729..6c72559dc9ff91acb16c18046f243c7ae16368f8 100644 (file)
@@ -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 (file)
index 0000000..5a6ae79
--- /dev/null
@@ -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();
+    });
+
+
index b96499e22851e4174a49c47ae83cb9c49192ee54..cc62922623bc4b0a103bd94d85f1de3fc7b802b3 100644 (file)
@@ -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 (file)
index 0000000..ebbb416
--- /dev/null
@@ -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];
+};
+
+
+
+
index 4943ead3c8b77a94490c073afa83175a56f55092..b4e13c89869ce99e750e91835cb86954c205062f 100644 (file)
@@ -5,6 +5,7 @@
 #include <fcntl.h>
 #include <string.h>
 #include <errno.h>
+#include <pty.h>
 
 #include <termios.h>
 #include <sys/ioctl.h>
@@ -189,6 +190,26 @@ static Handle<Value> IsStdoutBlocking(const Arguments& args) {
 }
 
 
+static Handle<Value> 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<Array> 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<v8::Object> 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 0698f0b60a42cd9f34dfd613f074a38b43c6bfda..1790a9bb72b2601c929a6cd2ddfc3d6898c969e0 100644 (file)
--- 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'