#![allow(non_camel_case_types)] use std::{env::*, fs::OpenOptions, path::PathBuf}; use super::Builtin; use crate::*; pub struct cd; impl Builtin for cd { fn name(&self) -> &str { "cd" } fn mod_session(&self, se: &mut Session, args: &[BString]) { let mut dir = match current_dir() { Ok(path) => path.as_os_str().as_bytes().to_vec(), Err(_) => vec![b'.'], }; std::mem::swap(&mut dir, &mut se.prev_path); match args.get(0).map(|v| &v[..]) { Some(b"-") => { let _ = set_current_dir(OsStr::from_bytes(&dir)); } Some(path) => { let _ = set_current_dir(OsStr::from_bytes(path)); } None => { if let Some(home) = std::env::var_os("HOME") { let _ = set_current_dir(home); } } } // TODO: let mod_session builtins return nonzero exit code } } pub struct clear; impl Builtin for clear { fn name(&self) -> &str { "clear" } fn mod_session(&self, _: &mut Session, _: &[BString]) { print!("\x1B[2J\x1B[1;1H"); } } /// restart shell pub struct re; impl Builtin for re { fn name(&self) -> &str { "re" } fn mod_session(&self, session: &mut Session, _args: &[BString]) { session.raw.disable(); crate::reload::begin_reload(); session.raw.enable(); // something went wrong, let's restore raw mode } } pub struct Sink { name: &'static str, append: bool, } impl Builtin for Sink { fn name(&self) -> &str { self.name } fn io( &self, args: &[BString], stdin: &mut dyn Read, _stdout: &mut dyn Write, ) -> std::io::Result<()> { let Some(path) = args.get(0) else { // TODO exit code return Ok(()); }; let path = PathBuf::from(OsStr::from_bytes(path)); let mut file = OpenOptions::new() .write(true) .create(true) .append(self.append) .open(path)?; std::io::copy(stdin, &mut file)?; Ok(()) } } pub const fn sink(name: &'static str, append: bool) -> Sink { Sink { name, append } } // TODO // from" => todo!("read from file"), pub struct from; impl Builtin for from { fn name(&self) -> &str { "from" } fn io( &self, args: &[BString], _stdin: &mut dyn Read, stdout: &mut dyn Write, ) -> std::io::Result<()> { let Some(path) = args.get(0) else { // TODO exit code return Ok(()); }; let path = PathBuf::from(OsStr::from_bytes(path)); let mut file = OpenOptions::new().read(true).open(path)?; std::io::copy(&mut file, stdout)?; Ok(()) } } pub struct _type; impl Builtin for _type { fn name(&self) -> &str { "type" } fn io( &self, args: &[BString], _stdin: &mut dyn Read, stdout: &mut dyn Write, ) -> std::io::Result<()> { for arg in args { let kind = "todo"; writeln!(stdout, "{} is {}", String::from_utf8_lossy(arg), kind)?; } Ok(()) } } pub struct builtins; impl Builtin for builtins { fn name(&self) -> &str { "builtins" } fn io( &self, _args: &[BString], _stdin: &mut dyn Read, stdout: &mut dyn Write, ) -> std::io::Result<()> { for b in super::BUILTINS { write!(stdout, "{} ", b.name())?; } writeln!(stdout) } } pub struct history; impl Builtin for history { fn name(&self) -> &str { "history" } fn io( &self, _args: &[BString], _stdin: &mut dyn Read, _stdout: &mut dyn Write, ) -> std::io::Result<()> { // TODO: add location & time to history to allow better querying todo!("need session") } } pub struct escape; impl Builtin for escape { fn name(&self) -> &str { "escape" } fn io( &self, args: &[BString], _stdin: &mut dyn Read, stdout: &mut dyn Write, ) -> std::io::Result<()> { for arg in args.iter() { let escaped = arg.escape_ascii().to_string(); stdout.write_all(escaped.as_bytes())?; stdout.write_all(b" ")?; } Ok(()) } }