blob: 1684552f8b7172b3a6e9354c93e9a8bf6cbfe6c4 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* The buffer for input from a terminal.
*
* This is necessary when /dev/tty and stdin can be separate streams. In that
* case, the input from the user must be buffered, and data must only be given
* to the first stream that reads it.
*/
nassh.InputBuffer = function() {
// The buffered data.
this.data_ = '';
// The queue of readers that are waiting for data. Readers are only queued
// when they attempt to read and there is no data available.
this.pendingReaders_ = [];
// This event is fired with the value true when there is data available to be
// read, and false when the buffer is empty. It is only fired when this
// status changes.
this.onDataAvailable = new lib.Event();
};
/**
* Write data to the input buffer.
*
* This may call callbacks for pending readers.
*/
nassh.InputBuffer.prototype.write = function(data) {
var wasAvailable = this.data_.length != 0;
this.data_ += data;
// First, send data to the pending readers.
for (var i = 0; i < this.pendingReaders_.length; i++) {
var onRead = this.pendingReaders_[i].onRead;
var size = this.pendingReaders_[i].size;
if (size > this.data_.length) {
size = this.data_.length;
}
if (size == 0) {
break;
}
var rv = this.data_.slice(0, size);
if (onRead(rv)) {
this.data_ = this.data_.slice(size);
}
this.pendingReaders_.shift();
}
// Now, if data is still available, notify.
if (this.data_.length > 0 && !wasAvailable) {
this.onDataAvailable(true);
}
};
/**
* Read data from the input buffer.
*
* If there is no data available to be read, this read will be queued, and
* onRead will be later called when data is written to the input buffer.
*
* This only happens if there is no data available in the buffer. If there is
* not enough data available, onRead is called with all of the data in the
* buffer.
*/
nassh.InputBuffer.prototype.read = function(size, onRead) {
var avail = this.data_.length;
var rv;
if (avail == 0) {
// No data is available. Wait for data to be available and send it to the
// queued readers.
this.pendingReaders_.push({size: size, onRead: onRead});
return;
}
if (size > avail) {
size = avail;
}
var rv = this.data_.slice(0, size);
if (onRead(rv)) {
this.data_ = this.data_.slice(size);
}
if (this.data_.length == 0) {
this.onDataAvailable(false);
}
};
/**
* The /dev/tty stream.
*
* This stream allows reads (from an nassh.InputBuffer) and writes (to a
* hterm.Terminal.IO). It is used for /dev/tty, as well as stdin, stdout and
* stderr when they are reading from/writing to a terminal.
*/
nassh.Stream.Tty = function(fd, info) {
nassh.Stream.apply(this, [fd]);
};
nassh.Stream.Tty.prototype = Object.create(nassh.Stream.prototype);
nassh.Stream.Tty.constructor = nassh.Stream.Tty;
nassh.Stream.Tty.prototype.asyncOpen_ = function(info, onOpen) {
this.allowRead_ = info.allowRead;
this.allowWrite_ = info.allowWrite;
this.inputBuffer_ = info.inputBuffer;
this.io_ = info.io;
this.acknowledgeCount_ = 0;
setTimeout(function() { onOpen(true); }, 0);
};
nassh.Stream.Tty.prototype.asyncRead = function(size, onRead) {
if (!this.open)
throw nassh.Stream.ERR_STREAM_CLOSED;
if (!this.allowRead_)
throw nassh.Stream.ERR_STREAM_CANT_READ;
this.inputBuffer_.read(size, (data) => {
if (!this.open) {
return false;
}
var b64bytes = btoa(data);
setTimeout(function() { onRead(b64bytes); }, 0);
return true;
});
};
nassh.Stream.Tty.prototype.asyncWrite = function(data, onSuccess) {
if (!this.open)
throw nassh.Stream.ERR_STREAM_CLOSED;
if (!this.allowWrite_)
throw nassh.Stream.ERR_STREAM_CANT_WRITE;
var string = atob(data);
this.acknowledgeCount_ += string.length;
this.io_.writeUTF8(string);
setTimeout(() => { onSuccess(this.acknowledgeCount_); }, 0);
};