vfs: add is_terminal()

This commit is contained in:
Mark Poliakov 2024-12-21 00:28:28 +02:00
parent 8dbbc07ff6
commit 4acb148d0e
9 changed files with 86 additions and 32 deletions

View File

@ -93,12 +93,6 @@ impl CharFile {
self.device.0.read_nonblocking(buf)
}
}
// if self.read {
// self.device.0.read(buf)
// } else {
// Err(Error::InvalidOperation)
// }
// }
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
if !self.write {
@ -111,10 +105,8 @@ impl CharFile {
self.device.0.write_nonblocking(buf)
}
}
// if self.write {
// self.device.0.write(buf)
// } else {
// Err(Error::ReadOnly)
// }
// }
pub fn is_terminal(&self) -> bool {
self.device.0.is_terminal()
}
}

View File

@ -341,6 +341,14 @@ impl File {
/// Performs a device-specific request
pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match req {
DeviceRequest::IsTerminal(value) => {
*value = self.is_terminal();
return Ok(());
}
_ => (),
}
match self {
Self::Char(f) => f.device.device_request(req),
Self::Block(f) => f.device.device_request(req),
@ -375,6 +383,14 @@ impl File {
_ => Err(Error::InvalidOperation),
}
}
pub fn is_terminal(&self) -> bool {
match self {
Self::Char(dev) => dev.is_terminal(),
Self::PtySlave(_) | Self::PtyMaster(_) => true,
_ => false,
}
}
}
impl PageProvider for File {

View File

@ -29,6 +29,8 @@ pub enum DeviceRequest {
GetTerminalSize(MaybeUninit<terminal::TerminalSize>),
/// Sets a foreground process group ID for the terminal
SetTerminalGroup(ProcessGroupId),
/// Returns `true` if a file/device is a terminal
IsTerminal(bool),
/// "Acquires" ownership of the device, preventing others from accessing it
AcquireDevice,

View File

@ -28,7 +28,7 @@ pub use abi::io::{
FileType, OpenOptions, PipeOptions, RawFd, SeekFrom, TimerOptions,
};
use abi::{error::Error, process::ProcessOption, util::FixedString};
use abi::{error::Error, io::DeviceRequest, process::ProcessOption, util::FixedString};
use alloc::string::String;
pub fn current_directory<T, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> {
@ -50,3 +50,12 @@ pub fn set_current_directory(path: &str) -> Result<(), Error> {
unsafe { crate::sys::set_process_option(&mut option) }?;
Ok(())
}
pub fn is_terminal(f: RawFd) -> bool {
let mut option = DeviceRequest::IsTerminal(false);
let res = unsafe { crate::sys::device_request(f, &mut option) };
match (res, option) {
(Ok(()), DeviceRequest::IsTerminal(true)) => true,
_ => false,
}
}

View File

@ -1,8 +1,10 @@
use std::{
fmt,
io::{self, Read, Stdin},
io::{self, Read},
};
use crate::TermInput;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum TermKey {
Char(char),
@ -23,7 +25,7 @@ pub trait ReadChar {
fn read_char(&mut self) -> Result<char, InputError>;
}
impl ReadChar for Stdin {
impl ReadChar for TermInput {
fn read_char(&mut self) -> Result<char, InputError> {
let mut buf = [0; 4];
self.read_exact(&mut buf[..1])

View File

@ -1,9 +1,7 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os, rustc_private))]
use std::{
fmt,
io::{self, stdin, stdout, Stdin, Stdout, Write},
os::fd::{AsRawFd, RawFd},
fmt, fs::File, io::{self, stdin, stdout, IsTerminal, Read, Stdin, Stdout, Write}, os::fd::{AsRawFd, RawFd}
};
pub use self::{input::ReadChar, sys::RawMode};
@ -43,8 +41,31 @@ pub trait RawTerminal {
fn raw_size(&self) -> io::Result<(usize, usize)>;
}
enum TermInput {
Stdin(Stdin),
File(File)
}
impl Read for TermInput {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Stdin(stdin) => stdin.read(buf),
Self::File(file) => file.read(buf)
}
}
}
impl AsRawFd for TermInput {
fn as_raw_fd(&self) -> RawFd {
match self {
Self::Stdin(stdin) => stdin.as_raw_fd(),
Self::File(file) => file.as_raw_fd(),
}
}
}
pub struct Term {
stdin: Stdin,
stdin: TermInput,
stdout: Stdout,
raw: RawMode,
}
@ -134,9 +155,8 @@ impl RawTerminal for Stdout {
}
impl Term {
pub fn is_tty() -> bool {
// TODO
true
pub fn stdin_is_tty() -> bool {
stdin().is_terminal()
}
pub fn input_fd(&self) -> RawFd {
@ -145,6 +165,12 @@ impl Term {
pub fn open() -> Result<Self, Error> {
let stdin = stdin();
let stdin = if stdin.is_terminal() {
TermInput::Stdin(stdin)
} else {
let file = File::open("/dev/tty")?;
TermInput::File(file)
};
let mut stdout = stdout();
// Set stdin to raw mode

View File

@ -1,10 +1,10 @@
use std::{
io::{self, Stdin, Stdout},
io::{self, Stdout},
mem::MaybeUninit,
os::yggdrasil::io::{
os::{fd::AsRawFd, yggdrasil::io::{
device::{DeviceRequest, FdDeviceRequest},
terminal::{update_terminal_options, TerminalOptions},
},
}},
};
pub struct RawMode(TerminalOptions);
@ -13,15 +13,15 @@ impl RawMode {
/// # Safety
///
/// May leave the terminal in broken state, unsafe.
pub unsafe fn enter(stdin: &Stdin) -> io::Result<Self> {
update_terminal_options(stdin, |_| TerminalOptions::raw_input()).map(RawMode)
pub unsafe fn enter<F: AsRawFd>(stdin: &F) -> io::Result<Self> {
update_terminal_options(stdin.as_raw_fd(), |_| TerminalOptions::raw_input()).map(RawMode)
}
/// # Safety
///
/// May leave the terminal in broken state, unsafe.
pub unsafe fn leave(&self, stdin: &Stdin) {
update_terminal_options(stdin, |_| self.0).ok();
pub unsafe fn leave<F: AsRawFd>(&self, stdin: &F) {
update_terminal_options(stdin.as_raw_fd(), |_| self.0).ok();
}
}

View File

@ -347,7 +347,7 @@ fn main() {
return;
}
if !Term::is_tty() {
if !Term::stdin_is_tty() {
eprintln!("Not a tty");
return;
}

View File

@ -1,6 +1,7 @@
use std::{
fmt::{self, Write},
io,
process::ExitCode,
};
use clap::Parser;
@ -160,9 +161,15 @@ struct Args {
filename: String,
}
fn main() -> Result<(), Error> {
fn main() -> ExitCode {
// TODO check if running in a terminal
let args = Args::parse();
let view = View::open(&args.filename, !args.no_bar).unwrap();
view.run()
match view.run() {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
yggdrasil_rt::debug_trace!("view: {error}");
ExitCode::FAILURE
}
}
}