Implemented prompt expansion
This commit is contained in:
@@ -8,7 +8,8 @@ pub fn cd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
let dir_raw = argv_iter.next().map(|arg| shenv.input_slice(arg.span()).into()).unwrap_or(std::env::var("HOME")?);
|
let dir_raw = argv_iter.next().map(|arg| shenv.input_slice(arg.span()).into()).unwrap_or(std::env::var("HOME")?);
|
||||||
let dir = PathBuf::from(&dir_raw);
|
let dir = PathBuf::from(&dir_raw);
|
||||||
std::env::set_current_dir(dir)?;
|
std::env::set_current_dir(dir)?;
|
||||||
shenv.vars_mut().export("PWD",&dir_raw);
|
let new_dir = std::env::current_dir()?;
|
||||||
|
shenv.vars_mut().export("PWD",new_dir.to_str().unwrap());
|
||||||
shenv.set_code(0);
|
shenv.set_code(0);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ pub fn export(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
let mut argv_iter = argv.into_iter();
|
let mut argv_iter = argv.into_iter();
|
||||||
argv_iter.next(); // Ignore 'export'
|
argv_iter.next(); // Ignore 'export'
|
||||||
while let Some(arg) = argv_iter.next() {
|
while let Some(arg) = argv_iter.next() {
|
||||||
let arg_raw = shenv.input_slice(arg.span()).to_string();
|
let arg_raw = arg.as_raw(shenv);
|
||||||
if let Some((var,val)) = arg_raw.split_once('=') {
|
if let Some((var,val)) = arg_raw.split_once('=') {
|
||||||
shenv.vars_mut().export(var, val);
|
shenv.vars_mut().export(var, &clean_string(val));
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Expected an assignment in export args, found this: {}", arg_raw)
|
eprintln!("Expected an assignment in export args, found this: {}", arg_raw)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()>
|
|||||||
let parse_time = std::time::Instant::now();
|
let parse_time = std::time::Instant::now();
|
||||||
let syn_tree = Parser::new(token_stream,shenv).parse()?;
|
let syn_tree = Parser::new(token_stream,shenv).parse()?;
|
||||||
log!(INFO, "Parsing done in {:?}", parse_time.elapsed());
|
log!(INFO, "Parsing done in {:?}", parse_time.elapsed());
|
||||||
shenv.save_io()?;
|
if !shenv.ctx().flags().contains(ExecFlags::IN_FUNC) {
|
||||||
|
shenv.save_io()?;
|
||||||
|
}
|
||||||
|
|
||||||
let exec_time = std::time::Instant::now();
|
let exec_time = std::time::Instant::now();
|
||||||
if let Err(e) = Executor::new(syn_tree, shenv).walk() {
|
if let Err(e) = Executor::new(syn_tree, shenv).walk() {
|
||||||
@@ -32,13 +34,18 @@ pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()>
|
|||||||
let code = shenv.get_code();
|
let code = shenv.get_code();
|
||||||
sh_quit(code);
|
sh_quit(code);
|
||||||
} else {
|
} else {
|
||||||
shenv.reset_io()?;
|
if !shenv.ctx().flags().contains(ExecFlags::IN_FUNC) {
|
||||||
|
shenv.reset_io()?;
|
||||||
|
}
|
||||||
return Err(e.into())
|
return Err(e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log!(INFO, "Executing done in {:?}", exec_time.elapsed());
|
log!(INFO, "Executing done in {:?}", exec_time.elapsed());
|
||||||
log!(INFO, "Total time spent: {:?}", total_time.elapsed());
|
log!(INFO, "Total time spent: {:?}", total_time.elapsed());
|
||||||
shenv.reset_io()?;
|
if !shenv.ctx().flags().contains(ExecFlags::IN_FUNC) {
|
||||||
|
shenv.reset_io()?;
|
||||||
|
}
|
||||||
|
log!(INFO, "Io reset");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +158,7 @@ fn exec_func(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
|||||||
let body = shenv.logic().get_function(&func_name).unwrap().to_string();
|
let body = shenv.logic().get_function(&func_name).unwrap().to_string();
|
||||||
let snapshot = shenv.clone();
|
let snapshot = shenv.clone();
|
||||||
shenv.vars_mut().reset_params();
|
shenv.vars_mut().reset_params();
|
||||||
|
shenv.ctx_mut().set_flag(ExecFlags::IN_FUNC);
|
||||||
while let Some(arg) = argv_iter.next() {
|
while let Some(arg) = argv_iter.next() {
|
||||||
let arg_raw = shenv.input_slice(arg.span()).to_string();
|
let arg_raw = shenv.input_slice(arg.span()).to_string();
|
||||||
shenv.vars_mut().bpush_arg(&arg_raw);
|
shenv.vars_mut().bpush_arg(&arg_raw);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub mod tilde;
|
|||||||
pub mod alias;
|
pub mod alias;
|
||||||
pub mod cmdsub;
|
pub mod cmdsub;
|
||||||
pub mod arithmetic;
|
pub mod arithmetic;
|
||||||
|
pub mod prompt;
|
||||||
|
|
||||||
use arithmetic::expand_arith_token;
|
use arithmetic::expand_arith_token;
|
||||||
use cmdsub::expand_cmdsub_token;
|
use cmdsub::expand_cmdsub_token;
|
||||||
|
|||||||
385
src/expand/prompt.rs
Normal file
385
src/expand/prompt.rs
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PromptTk {
|
||||||
|
AsciiOct(i32),
|
||||||
|
Text(String),
|
||||||
|
AnsiSeq(String),
|
||||||
|
VisGrp,
|
||||||
|
UserSeq,
|
||||||
|
Runtime,
|
||||||
|
Weekday,
|
||||||
|
Dquote,
|
||||||
|
Squote,
|
||||||
|
Return,
|
||||||
|
Newline,
|
||||||
|
Pwd,
|
||||||
|
PwdShort,
|
||||||
|
Hostname,
|
||||||
|
HostnameShort,
|
||||||
|
ShellName,
|
||||||
|
Username,
|
||||||
|
PromptSymbol,
|
||||||
|
ExitCode,
|
||||||
|
SuccessSymbol,
|
||||||
|
FailureSymbol,
|
||||||
|
JobCount
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_cmd_runtime(dur: std::time::Duration) -> String {
|
||||||
|
const ETERNITY: u128 = f32::INFINITY as u128;
|
||||||
|
let mut micros = dur.as_micros();
|
||||||
|
let mut millis = 0;
|
||||||
|
let mut seconds = 0;
|
||||||
|
let mut minutes = 0;
|
||||||
|
let mut hours = 0;
|
||||||
|
let mut days = 0;
|
||||||
|
let mut weeks = 0;
|
||||||
|
let mut months = 0;
|
||||||
|
let mut years = 0;
|
||||||
|
let mut decades = 0;
|
||||||
|
let mut centuries = 0;
|
||||||
|
let mut millennia = 0;
|
||||||
|
let mut epochs = 0;
|
||||||
|
let mut aeons = 0;
|
||||||
|
let mut eternities = 0;
|
||||||
|
|
||||||
|
if micros >= 1000 {
|
||||||
|
millis = micros / 1000;
|
||||||
|
micros %= 1000;
|
||||||
|
}
|
||||||
|
if millis >= 1000 {
|
||||||
|
seconds = millis / 1000;
|
||||||
|
millis %= 1000;
|
||||||
|
}
|
||||||
|
if seconds >= 60 {
|
||||||
|
minutes = seconds / 60;
|
||||||
|
seconds %= 60;
|
||||||
|
}
|
||||||
|
if minutes >= 60 {
|
||||||
|
hours = minutes / 60;
|
||||||
|
minutes %= 60;
|
||||||
|
}
|
||||||
|
if hours >= 24 {
|
||||||
|
days = hours / 24;
|
||||||
|
hours %= 24;
|
||||||
|
}
|
||||||
|
if days >= 7 {
|
||||||
|
weeks = days / 7;
|
||||||
|
days %= 7;
|
||||||
|
}
|
||||||
|
if weeks >= 4 {
|
||||||
|
months = weeks / 4;
|
||||||
|
weeks %= 4;
|
||||||
|
}
|
||||||
|
if months >= 12 {
|
||||||
|
years = months / 12;
|
||||||
|
weeks %= 12;
|
||||||
|
}
|
||||||
|
if years >= 10 {
|
||||||
|
decades = years / 10;
|
||||||
|
years %= 10;
|
||||||
|
}
|
||||||
|
if decades >= 10 {
|
||||||
|
centuries = decades / 10;
|
||||||
|
decades %= 10;
|
||||||
|
}
|
||||||
|
if centuries >= 10 {
|
||||||
|
millennia = centuries / 10;
|
||||||
|
centuries %= 10;
|
||||||
|
}
|
||||||
|
if millennia >= 1000 {
|
||||||
|
epochs = millennia / 1000;
|
||||||
|
millennia %= 1000;
|
||||||
|
}
|
||||||
|
if epochs >= 1000 {
|
||||||
|
aeons = epochs / 1000;
|
||||||
|
epochs %= aeons;
|
||||||
|
}
|
||||||
|
if aeons == ETERNITY {
|
||||||
|
eternities = aeons / ETERNITY;
|
||||||
|
aeons %= ETERNITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the result
|
||||||
|
let mut result = Vec::new();
|
||||||
|
if eternities > 0 {
|
||||||
|
let mut string = format!("{} eternit", eternities);
|
||||||
|
if eternities > 1 {
|
||||||
|
string.push_str("ies");
|
||||||
|
} else {
|
||||||
|
string.push('y');
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if aeons > 0 {
|
||||||
|
let mut string = format!("{} aeon", aeons);
|
||||||
|
if aeons > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if epochs > 0 {
|
||||||
|
let mut string = format!("{} epoch", epochs);
|
||||||
|
if epochs > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if millennia > 0 {
|
||||||
|
let mut string = format!("{} millenni", millennia);
|
||||||
|
if millennia > 1 {
|
||||||
|
string.push_str("um")
|
||||||
|
} else {
|
||||||
|
string.push('a')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if centuries > 0 {
|
||||||
|
let mut string = format!("{} centur", centuries);
|
||||||
|
if centuries > 1 {
|
||||||
|
string.push_str("ies")
|
||||||
|
} else {
|
||||||
|
string.push('y')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if decades > 0 {
|
||||||
|
let mut string = format!("{} decade", decades);
|
||||||
|
if decades > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if years > 0 {
|
||||||
|
let mut string = format!("{} year", years);
|
||||||
|
if years > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if months > 0 {
|
||||||
|
let mut string = format!("{} month", months);
|
||||||
|
if months > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if weeks > 0 {
|
||||||
|
let mut string = format!("{} week", weeks);
|
||||||
|
if weeks > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if days > 0 {
|
||||||
|
let mut string = format!("{} day", days);
|
||||||
|
if days > 1 {
|
||||||
|
string.push('s')
|
||||||
|
}
|
||||||
|
result.push(string)
|
||||||
|
}
|
||||||
|
if hours > 0 {
|
||||||
|
let string = format!("{}h", hours);
|
||||||
|
result.push(string);
|
||||||
|
}
|
||||||
|
if minutes > 0 {
|
||||||
|
let string = format!("{}m", minutes);
|
||||||
|
result.push(string);
|
||||||
|
}
|
||||||
|
if seconds > 0 {
|
||||||
|
let string = format!("{}s", seconds);
|
||||||
|
result.push(string);
|
||||||
|
}
|
||||||
|
if millis > 0 {
|
||||||
|
let string = format!("{}ms",millis);
|
||||||
|
result.push(string);
|
||||||
|
}
|
||||||
|
if result.is_empty() && micros > 0 {
|
||||||
|
let string = format!("{}ms",micros);
|
||||||
|
result.push(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tokenize_prompt(raw: &str) -> Vec<PromptTk> {
|
||||||
|
let mut chars = raw.chars().peekable();
|
||||||
|
let mut tk_text = String::new();
|
||||||
|
let mut tokens = vec![];
|
||||||
|
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
log!(DEBUG,tokens);
|
||||||
|
log!(DEBUG,ch);
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
// Push any accumulated text as a token
|
||||||
|
if !tk_text.is_empty() {
|
||||||
|
tokens.push(PromptTk::Text(std::mem::take(&mut tk_text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the escape sequence
|
||||||
|
if let Some(ch) = chars.next() {
|
||||||
|
match ch {
|
||||||
|
'w' => tokens.push(PromptTk::Pwd),
|
||||||
|
'W' => tokens.push(PromptTk::PwdShort),
|
||||||
|
'h' => tokens.push(PromptTk::Hostname),
|
||||||
|
'H' => tokens.push(PromptTk::HostnameShort),
|
||||||
|
's' => tokens.push(PromptTk::ShellName),
|
||||||
|
'u' => tokens.push(PromptTk::Username),
|
||||||
|
'$' => tokens.push(PromptTk::PromptSymbol),
|
||||||
|
'n' => tokens.push(PromptTk::Text("\n".into())),
|
||||||
|
'r' => tokens.push(PromptTk::Text("\r".into())),
|
||||||
|
'T' => tokens.push(PromptTk::Runtime),
|
||||||
|
'\\' => tokens.push(PromptTk::Text("\\".into())),
|
||||||
|
'"' => tokens.push(PromptTk::Text("\"".into())),
|
||||||
|
'\'' => tokens.push(PromptTk::Text("'".into())),
|
||||||
|
'e' => {
|
||||||
|
if chars.next() == Some('[') {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
// Collect parameters and final character
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
match ch {
|
||||||
|
'0'..='9' | ';' | '?' | ':' => params.push(ch), // Valid parameter characters
|
||||||
|
'A'..='Z' | 'a'..='z' => { // Final character (letter)
|
||||||
|
params.push(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Invalid character in ANSI sequence
|
||||||
|
tokens.push(PromptTk::Text(format!("\x1b[{params}")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.push(PromptTk::AnsiSeq(format!("\x1b[{params}")));
|
||||||
|
} else {
|
||||||
|
// Handle case where 'e' is not followed by '['
|
||||||
|
tokens.push(PromptTk::Text("\\e".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'0'..='7' => {
|
||||||
|
// Handle octal escape
|
||||||
|
let mut octal_str = String::new();
|
||||||
|
octal_str.push(ch);
|
||||||
|
|
||||||
|
// Collect up to 2 more octal digits
|
||||||
|
for _ in 0..2 {
|
||||||
|
if let Some(&next_ch) = chars.peek() {
|
||||||
|
if next_ch >= '0' && next_ch <= '7' {
|
||||||
|
octal_str.push(chars.next().unwrap());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the octal string into an integer
|
||||||
|
if let Ok(octal) = i32::from_str_radix(&octal_str, 8) {
|
||||||
|
tokens.push(PromptTk::AsciiOct(octal));
|
||||||
|
} else {
|
||||||
|
// Fallback: treat as raw text
|
||||||
|
tokens.push(PromptTk::Text(format!("\\{octal_str}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Unknown escape sequence: treat as raw text
|
||||||
|
tokens.push(PromptTk::Text(format!("\\{ch}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle trailing backslash
|
||||||
|
tokens.push(PromptTk::Text("\\".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Accumulate non-escape characters
|
||||||
|
tk_text.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push any remaining text as a token
|
||||||
|
if !tk_text.is_empty() {
|
||||||
|
tokens.push(PromptTk::Text(tk_text));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_prompt(raw: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
||||||
|
let mut tokens = tokenize_prompt(raw).into_iter();
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
while let Some(token) = tokens.next() {
|
||||||
|
match token {
|
||||||
|
PromptTk::AsciiOct(_) => todo!(),
|
||||||
|
PromptTk::Text(txt) => result.push_str(&txt),
|
||||||
|
PromptTk::AnsiSeq(params) => result.push_str(¶ms),
|
||||||
|
PromptTk::Runtime => {
|
||||||
|
if let Some(runtime) = shenv.meta().get_runtime() {
|
||||||
|
let runtime_fmt = format_cmd_runtime(runtime);
|
||||||
|
result.push_str(&runtime_fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PromptTk::Pwd => {
|
||||||
|
let mut pwd = std::env::var("PWD")?;
|
||||||
|
let home = std::env::var("HOME")?;
|
||||||
|
if pwd.starts_with(&home) {
|
||||||
|
pwd = pwd.replacen(&home, "~", 1);
|
||||||
|
}
|
||||||
|
result.push_str(&pwd);
|
||||||
|
}
|
||||||
|
PromptTk::PwdShort => {
|
||||||
|
let mut path = std::env::var("PWD")?;
|
||||||
|
let home = std::env::var("HOME")?;
|
||||||
|
if path.starts_with(&home) {
|
||||||
|
path = path.replacen(&home, "~", 1);
|
||||||
|
}
|
||||||
|
let pathbuf = PathBuf::from(&path);
|
||||||
|
let mut segments = pathbuf.iter().count();
|
||||||
|
let mut path_iter = pathbuf.into_iter();
|
||||||
|
while segments > 4 {
|
||||||
|
path_iter.next();
|
||||||
|
segments -= 1;
|
||||||
|
}
|
||||||
|
let path_rebuilt: PathBuf = path_iter.collect();
|
||||||
|
let mut path_rebuilt = path_rebuilt.to_str().unwrap().to_string();
|
||||||
|
if path_rebuilt.starts_with(&home) {
|
||||||
|
path_rebuilt = path_rebuilt.replacen(&home, "~", 1);
|
||||||
|
}
|
||||||
|
result.push_str(&path_rebuilt);
|
||||||
|
}
|
||||||
|
PromptTk::Hostname => {
|
||||||
|
let hostname = std::env::var("HOSTNAME")?;
|
||||||
|
result.push_str(&hostname);
|
||||||
|
}
|
||||||
|
PromptTk::HostnameShort => todo!(),
|
||||||
|
PromptTk::ShellName => result.push_str("fern"),
|
||||||
|
PromptTk::Username => {
|
||||||
|
let username = std::env::var("USER")?;
|
||||||
|
result.push_str(&username);
|
||||||
|
}
|
||||||
|
PromptTk::PromptSymbol => {
|
||||||
|
let uid = std::env::var("UID")?;
|
||||||
|
let symbol = if &uid == "0" {
|
||||||
|
'#'
|
||||||
|
} else {
|
||||||
|
'$'
|
||||||
|
};
|
||||||
|
result.push(symbol);
|
||||||
|
}
|
||||||
|
PromptTk::ExitCode => todo!(),
|
||||||
|
PromptTk::SuccessSymbol => todo!(),
|
||||||
|
PromptTk::FailureSymbol => todo!(),
|
||||||
|
PromptTk::JobCount => todo!(),
|
||||||
|
_ => unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
@@ -6,8 +6,7 @@ pub fn expand_tilde_token(tilde_sub: Token, shenv: &mut ShEnv) -> Token {
|
|||||||
if result == tilde_sub_raw {
|
if result == tilde_sub_raw {
|
||||||
return tilde_sub
|
return tilde_sub
|
||||||
}
|
}
|
||||||
let mut tokens = Lexer::new(result,shenv).lex();
|
shenv.expand_input(&result, tilde_sub.span()).pop().unwrap_or(tilde_sub)
|
||||||
tokens.pop().unwrap_or(tilde_sub)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_tilde_string(s: &str) -> String {
|
pub fn expand_tilde_string(s: &str) -> String {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
|||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
'\\' => {
|
'\\' => {
|
||||||
|
result.push(ch);
|
||||||
if let Some(next_ch) = chars.next() {
|
if let Some(next_ch) = chars.next() {
|
||||||
result.push(next_ch)
|
result.push(next_ch)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,15 +303,6 @@ impl CmdRedirs {
|
|||||||
}
|
}
|
||||||
Self { open: vec![], targets_fd, targets_file, targets_text }
|
Self { open: vec![], targets_fd, targets_file, targets_text }
|
||||||
}
|
}
|
||||||
pub fn close_all(&mut self) -> ShResult<()> {
|
|
||||||
while let Some(fd) = self.open.pop() {
|
|
||||||
if let Err(e) = close(fd) {
|
|
||||||
self.open.push(fd);
|
|
||||||
return Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn activate(&mut self) -> ShResult<()> {
|
pub fn activate(&mut self) -> ShResult<()> {
|
||||||
self.open_file_tgts()?;
|
self.open_file_tgts()?;
|
||||||
self.open_fd_tgts()?;
|
self.open_fd_tgts()?;
|
||||||
@@ -328,12 +319,12 @@ impl CmdRedirs {
|
|||||||
RedirTarget::HereDoc(body) |
|
RedirTarget::HereDoc(body) |
|
||||||
RedirTarget::HereString(body) => {
|
RedirTarget::HereString(body) => {
|
||||||
write(wpipe_fd, body.as_bytes())?;
|
write(wpipe_fd, body.as_bytes())?;
|
||||||
close(wpipe)?;
|
close(wpipe).ok();
|
||||||
}
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
dup2(rpipe, src.as_raw_fd())?;
|
dup2(rpipe, src.as_raw_fd())?;
|
||||||
close(rpipe)?;
|
close(rpipe).ok();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ pub use crate::{
|
|||||||
expand::{
|
expand::{
|
||||||
expand_argv,
|
expand_argv,
|
||||||
expand_token,
|
expand_token,
|
||||||
|
prompt::expand_prompt,
|
||||||
alias::expand_aliases
|
alias::expand_aliases
|
||||||
},
|
},
|
||||||
shellenv::{
|
shellenv::{
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ fn init_rl<'a>(shenv: &'a mut ShEnv) -> Editor<SynHelper<'a>, DefaultHistory> {
|
|||||||
|
|
||||||
pub fn read_line(shenv: &mut ShEnv) -> ShResult<String> {
|
pub fn read_line(shenv: &mut ShEnv) -> ShResult<String> {
|
||||||
log!(TRACE, "Entering prompt");
|
log!(TRACE, "Entering prompt");
|
||||||
let prompt = "$ ".styled(Style::Green | Style::Bold);
|
let ps1 = std::env::var("PS1").unwrap_or("\\$ ".styled(Style::Green | Style::Bold));
|
||||||
|
let prompt = expand_prompt(&ps1,shenv)?;
|
||||||
let mut editor = init_rl(shenv);
|
let mut editor = init_rl(shenv);
|
||||||
match editor.readline(&prompt) {
|
match editor.readline(&prompt) {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ bitflags! {
|
|||||||
#[derive(Copy,Clone,Debug,PartialEq,PartialOrd)]
|
#[derive(Copy,Clone,Debug,PartialEq,PartialOrd)]
|
||||||
pub struct ExecFlags: u32 {
|
pub struct ExecFlags: u32 {
|
||||||
const NO_FORK = 0x00000001;
|
const NO_FORK = 0x00000001;
|
||||||
|
const IN_FUNC = 0x00000010;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct MetaTab {
|
pub struct MetaTab {
|
||||||
|
timer_start: Option<Instant>,
|
||||||
|
last_runtime: Option<Duration>,
|
||||||
last_status: i32
|
last_status: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaTab {
|
impl MetaTab {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
timer_start: None,
|
||||||
|
last_runtime: None,
|
||||||
last_status: 0
|
last_status: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn start_timer(&mut self) {
|
||||||
|
self.timer_start = Some(Instant::now())
|
||||||
|
}
|
||||||
|
pub fn stop_timer(&mut self) {
|
||||||
|
let timer_start = self.timer_start.take();
|
||||||
|
if let Some(instant) = timer_start {
|
||||||
|
self.last_runtime = Some(instant.elapsed())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_runtime(&self) -> Option<Duration> {
|
||||||
|
self.last_runtime
|
||||||
|
}
|
||||||
pub fn set_status(&mut self, code: i32) {
|
pub fn set_status(&mut self, code: i32) {
|
||||||
self.last_status = code
|
self.last_status = code
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,11 +167,11 @@ impl ShEnv {
|
|||||||
let saved_out = saved.stdout;
|
let saved_out = saved.stdout;
|
||||||
let saved_err = saved.stderr;
|
let saved_err = saved.stderr;
|
||||||
dup2(saved_in,STDIN_FILENO)?;
|
dup2(saved_in,STDIN_FILENO)?;
|
||||||
close(saved_in)?;
|
close(saved_in).ok();
|
||||||
dup2(saved_out,STDOUT_FILENO)?;
|
dup2(saved_out,STDOUT_FILENO)?;
|
||||||
close(saved_out)?;
|
close(saved_out).ok();
|
||||||
dup2(saved_err,STDERR_FILENO)?;
|
dup2(saved_err,STDERR_FILENO)?;
|
||||||
close(saved_err)?;
|
close(saved_err).ok();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user