Skip to content

Commit

Permalink
Start option 2
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkassimo committed Feb 13, 2020
1 parent d4865d4 commit f22a938
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 67 deletions.
3 changes: 2 additions & 1 deletion cli/js/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import { isTypedArray } from "./util.ts";
import { TypedArray } from "./types.ts";
import { TextEncoder } from "./text_encoding.ts";
import { File, stdout } from "./files.ts";
import { File } from "./files.ts";
import { stdout } from "./tty.ts";
import { cliTable } from "./console_table.ts";
import { exposeForTest } from "./internals.ts";

Expand Down
4 changes: 1 addition & 3 deletions cli/js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export {
openSync,
create,
createSync,
stdin,
stdout,
stderr,
read,
readSync,
write,
Expand Down Expand Up @@ -107,6 +104,7 @@ export { statSync, lstatSync, stat, lstat } from "./stat.ts";
export { symlinkSync, symlink } from "./symlink.ts";
export { connectTLS, listenTLS } from "./tls.ts";
export { truncateSync, truncate } from "./truncate.ts";
export { TTYInput, isatty, stdin, stdout, stderr } from "./tty.ts";
export { utimeSync, utime } from "./utime.ts";
export { version } from "./version.ts";
export { writeFileSync, writeFile, WriteFileOptions } from "./write_file.ts";
Expand Down
1 change: 1 addition & 0 deletions cli/js/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export let OP_SIGNAL_BIND: number;
export let OP_SIGNAL_UNBIND: number;
export let OP_SIGNAL_POLL: number;
export let OP_SET_RAW: number;
export let OP_ISATTY: number;

/** **WARNING:** This is only available during the snapshotting process and is
* unavailable at runtime. */
Expand Down
36 changes: 0 additions & 36 deletions cli/js/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,42 +256,6 @@ export class File
}
}

export type RestoreModeFunc = () => void;
/** Extended file abstraction with setRaw() */
export class Stdin extends File {
private _isRaw = false;
private _restoreFunc?: RestoreModeFunc;
/** Set input mode to raw (non-canonical).
* Returns a function that when called, restores previous mode.
*/
setRaw(): RestoreModeFunc {
if (this._isRaw) {
return this._restoreFunc!;
}
const restoreInfo = sendSyncJson(dispatch.OP_SET_RAW, {
rid: this.rid,
raw: true
});
this._isRaw = true;
this._restoreFunc = (): void => {
sendSyncJson(dispatch.OP_SET_RAW, {
rid: this.rid,
raw: false,
...restoreInfo
});
this._isRaw = false;
};
return this._restoreFunc!;
}
}

/** An instance of `Stdin` for stdin. */
export const stdin = new Stdin(0);
/** An instance of `File` for stdout. */
export const stdout = new File(1);
/** An instance of `File` for stderr. */
export const stderr = new File(2);

export interface OpenOptions {
/** Sets the option for read access. This option, when true, will indicate that the file should be read-able if opened. */
read?: boolean;
Expand Down
32 changes: 18 additions & 14 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,20 +480,6 @@ declare namespace Deno {
seekSync(offset: number, whence: SeekMode): void;
close(): void;
}
export type RestoreModeFunc = () => void;
/** Extended file abstraction with setRaw() */
export class Stdin extends File {
/** Set input mode to raw (non-canonical).
* Returns a function that when called, restores previous mode.
*/
setRaw(): RestoreModeFunc;
}
/** An instance of `Stdin` for stdin. */
export const stdin: Stdin;
/** An instance of `File` for stdout. */
export const stdout: File;
/** An instance of `File` for stderr. */
export const stderr: File;

export interface OpenOptions {
/** Sets the option for read access. This option, when true, will indicate that the file should be read-able if opened. */
Expand Down Expand Up @@ -551,6 +537,24 @@ declare namespace Deno {
/** Read-write. Behaves like `x` and allows to read from file. */
| "x+";

// @url js/tty.d.ts

/** Check if a given resource is TTY. */
export function isatty(rid: number): boolean;
/** Extended file abstraction for TTY input */
export class TTYInput extends File {
/** Is TTY under raw mode. */
get isRaw(): boolean;
/** Set TTY to be under raw mode. */
setRaw(mode: boolean): void;
}
/** An instance of `TTYInput` for stdin. */
export const stdin: TTYInput;
/** An instance of `File` for stdout. */
export const stdout: File;
/** An instance of `File` for stderr. */
export const stderr: File;

// @url js/buffer.d.ts

/** A Buffer is a variable-sized buffer of bytes with read() and write()
Expand Down
55 changes: 55 additions & 0 deletions cli/js/tty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { File } from "./files.ts";
import { sendSync } from "./dispatch_json.ts";
import * as dispatch from "./dispatch.ts";

/** Check if a given resource is TTY. */
export function isatty(rid: number): boolean {
return sendSync(dispatch.OP_ISATTY, { rid });
}

/** Extended file abstraction for TTY input */
export class TTYInput extends File {
constructor(rid: number) {
super(rid);
if (rid !== 0 && !isatty(rid)) {
throw new Error("Given resource is not a TTY");
}
}

/** Is TTY under raw mode. */
get isRaw(): boolean {
return this._isRaw;
}
private _isRaw = false;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _restoreInfo: { [key: string]: any } = {};

/** Set TTY to be under raw mode. */
setRaw(mode: boolean): void {
if (this._isRaw === mode) {
return;
}
if (mode) {
this._restoreInfo = sendSync(dispatch.OP_SET_RAW, {
rid: this.rid,
raw: true
});
this._isRaw = true;
} else {
sendSync(dispatch.OP_SET_RAW, {
rid: this.rid,
raw: false,
...this._restoreInfo
});
this._isRaw = false;
}
}
}

/** An instance of `TTYInput` for stdin. */
export const stdin = new TTYInput(0);
/** An instance of `File` for stdout. */
export const stdout = new File(1);
/** An instance of `File` for stderr. */
export const stderr = new File(2);
116 changes: 103 additions & 13 deletions cli/ops/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::deno_error::bad_resource;
use crate::deno_error::other_error;
use crate::ops::json_op;
use crate::state::State;
use atty;
use deno_core::*;
#[cfg(unix)]
use nix::sys::termios;
Expand All @@ -20,9 +21,25 @@ use winapi::um::wincon;
const RAW_MODE_MASK: DWORD = wincon::ENABLE_LINE_INPUT
| wincon::ENABLE_ECHO_INPUT
| wincon::ENABLE_PROCESSED_INPUT;
#[cfg(windows)]
fn get_windows_handle(
f: &std::fs::File,
) -> Result<std::os::windows::io::RawHandle, ErrBox> {
use std::os::windows::io::AsRawHandle;
use winapi::um::handleapi;

let handle = f.as_raw_handle();
if handle == handleapi::INVALID_HANDLE_VALUE {
return Err(ErrBox::from(std::io::Error::last_os_error()));
} else if handle.is_null() {
return Err(ErrBox::from(other_error("null handle".to_owned())));
}
Ok(handle)
}

pub fn init(i: &mut Isolate, s: &State) {
i.register_op("set_raw", s.core_op(json_op(s.stateful_op(op_set_raw))));
i.register_op("isatty", s.core_op(json_op(s.stateful_op(op_isatty))));
}

#[cfg(windows)]
Expand Down Expand Up @@ -98,24 +115,29 @@ pub fn op_set_raw(
return Err(bad_resource());
}

// For now, only stdin.
match resource.unwrap() {
StreamResource::Stdin(_) => (),
_ => {
return Err(other_error(
"Resource other than stdin is not implemented".to_owned(),
))
}
}

// From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs
// and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
// and https://github.com/crossterm-rs/crossterm/blob/e35d4d2c1cc4c919e36d242e014af75f6127ab50/src/terminal/sys/windows.rs
// Copyright (c) 2015 Katsu Kawakami & Rustyline authors. MIT license.
// Copyright (c) 2019 Timon. MIT license.
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
use winapi::um::{consoleapi, handleapi};

let handle = std::io::stdin().as_raw_handle();
// For now, only stdin.
let handle = match resource.unwrap() {
StreamResource::Stdin(_) => std::io::stdin().as_raw_handle(),
StreamResource::FsFile(f) => {
let tokio_file = futures::executor::block_on(f.try_clone())?;
let std_file = futures::executor::block_on(tokio_file.into_std());
std_file.as_raw_handle()
}
_ => {
return Err(other_error("Not implemented".to_owned()));
}
};

if handle == handleapi::INVALID_HANDLE_VALUE {
return Err(ErrBox::from(std::io::Error::last_os_error()));
} else if handle.is_null() {
Expand All @@ -132,11 +154,20 @@ pub fn op_set_raw(

Ok(JsonOp::Sync(json!({})))
}
// From https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let raw_fd = std::io::stdin().as_raw_fd();
let raw_fd = match resource.unwrap() {
StreamResource::Stdin(_) => std::io::stdin().as_raw_fd(),
StreamResource::FsFile(f) => {
let tokio_file = futures::executor::block_on(f.try_clone())?;
let std_file = futures::executor::block_on(tokio_file.into_std());
std_file.as_raw_fd()
}
_ => {
return Err(other_error("Not implemented".to_owned()));
}
};

if is_raw {
let original_mode = termios::tcgetattr(raw_fd)?;
Expand Down Expand Up @@ -181,3 +212,62 @@ pub fn op_set_raw(
}
}
}

#[derive(Deserialize)]
struct IsattyArgs {
rid: u32,
}

pub fn op_isatty(
state_: &State,
args: Value,
_zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, ErrBox> {
let args: IsattyArgs = serde_json::from_value(args)?;
let rid = args.rid;

let state = state_.borrow_mut();
if !state.resource_table.has(rid) {
return Err(bad_resource());
}

let resource = state.resource_table.get::<StreamResource>(rid);
if resource.is_none() {
return Ok(JsonOp::Sync(json!(false)));
}

match resource.unwrap() {
StreamResource::Stdin(_) => {
Ok(JsonOp::Sync(json!(atty::is(atty::Stream::Stdin))))
}
StreamResource::Stdout(_) => {
Ok(JsonOp::Sync(json!(atty::is(atty::Stream::Stdout))))
}
StreamResource::Stderr(_) => {
Ok(JsonOp::Sync(json!(atty::is(atty::Stream::Stderr))))
}
StreamResource::FsFile(f) => {
let tokio_file = futures::executor::block_on(f.try_clone())?;
let std_file = futures::executor::block_on(tokio_file.into_std());
#[cfg(windows)]
{
use winapi::um::consoleapi;

let handle = get_windows_handle(&std_file)?;
let mut test_mode: DWORD = 0;
// If I cannot get mode out of console, it is not a console.
let result =
unsafe { consoleapi::GetConsoleMode(handle, &mut test_mode) != 0 };
Ok(JsonOp::Sync(json!(result)))
}
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let raw_fd = std_file.as_raw_fd();
let result = unsafe { libc::isatty(raw_fd as libc::c_int) == 1 };
Ok(JsonOp::Sync(json!(result)))
}
}
_ => Ok(JsonOp::Sync(json!(false))),
}
}
4 changes: 4 additions & 0 deletions core/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ pub struct ResourceTable {
}

impl ResourceTable {
pub fn has(&self, rid: ResourceId) -> bool {
self.map.contains_key(&rid)
}

pub fn get<T: Resource>(&self, rid: ResourceId) -> Option<&T> {
if let Some((_name, resource)) = self.map.get(&rid) {
return resource.downcast_ref::<T>();
Expand Down

0 comments on commit f22a938

Please sign in to comment.