Added prompt escape code expansion flag to echo, -p
Added non-formatted runtime to prompt escape codes Added prompt escape code that expands to the output of a shell function Reworked internal logic for termios control
This commit is contained in:
@@ -1,14 +1,7 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::{
|
||||
builtin::setup_builtin,
|
||||
getopt::{get_opts_from_tokens, Opt, OptSet},
|
||||
jobs::JobBldr,
|
||||
libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt},
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
state,
|
||||
builtin::setup_builtin, expand::expand_prompt, getopt::{Opt, OptSet, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt}, parse::{NdRule, Node}, prelude::*, procio::{IoStack, borrow_fd}, state
|
||||
};
|
||||
|
||||
pub static ECHO_OPTS: LazyLock<OptSet> = LazyLock::new(|| {
|
||||
@@ -16,7 +9,7 @@ pub static ECHO_OPTS: LazyLock<OptSet> = LazyLock::new(|| {
|
||||
Opt::Short('n'),
|
||||
Opt::Short('E'),
|
||||
Opt::Short('e'),
|
||||
Opt::Short('r'),
|
||||
Opt::Short('p'),
|
||||
]
|
||||
.into()
|
||||
});
|
||||
@@ -26,6 +19,7 @@ bitflags! {
|
||||
const NO_NEWLINE = 0b000001;
|
||||
const USE_STDERR = 0b000010;
|
||||
const USE_ESCAPE = 0b000100;
|
||||
const USE_PROMPT = 0b001000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +43,13 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
borrow_fd(STDOUT_FILENO)
|
||||
};
|
||||
|
||||
let mut echo_output = argv
|
||||
let mut echo_output = prepare_echo_args(argv
|
||||
.into_iter()
|
||||
.map(|a| a.0) // Extract the String from the tuple of (String,Span)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
.collect::<Vec<_>>(),
|
||||
flags.contains(EchoFlags::USE_ESCAPE),
|
||||
flags.contains(EchoFlags::USE_PROMPT)
|
||||
)?.join(" ");
|
||||
|
||||
if !flags.contains(EchoFlags::NO_NEWLINE) {
|
||||
echo_output.push('\n')
|
||||
@@ -66,6 +62,122 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn prepare_echo_args(argv: Vec<String>, use_escape: bool, use_prompt: bool) -> ShResult<Vec<String>> {
|
||||
if !use_escape {
|
||||
if use_prompt {
|
||||
let expanded: ShResult<Vec<String>> = argv
|
||||
.into_iter()
|
||||
.map(|s| expand_prompt(s.as_str()))
|
||||
.collect();
|
||||
return expanded
|
||||
}
|
||||
return Ok(argv);
|
||||
}
|
||||
|
||||
let mut prepared_args = Vec::with_capacity(argv.len());
|
||||
|
||||
for arg in argv {
|
||||
let mut prepared_arg = String::new();
|
||||
if use_prompt {
|
||||
prepared_arg = expand_prompt(&prepared_arg)?;
|
||||
}
|
||||
|
||||
let mut chars = arg.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '\\' {
|
||||
if let Some(&next_char) = chars.peek() {
|
||||
match next_char {
|
||||
'n' => {
|
||||
prepared_arg.push('\n');
|
||||
chars.next();
|
||||
}
|
||||
't' => {
|
||||
prepared_arg.push('\t');
|
||||
chars.next();
|
||||
}
|
||||
'r' => {
|
||||
prepared_arg.push('\r');
|
||||
chars.next();
|
||||
}
|
||||
'a' => {
|
||||
prepared_arg.push('\x07');
|
||||
chars.next();
|
||||
}
|
||||
'b' => {
|
||||
prepared_arg.push('\x08');
|
||||
chars.next();
|
||||
}
|
||||
'e' | 'E' => {
|
||||
prepared_arg.push('\x1b');
|
||||
chars.next();
|
||||
}
|
||||
'x' => {
|
||||
chars.next(); // consume 'x'
|
||||
let mut hex_digits = String::new();
|
||||
for _ in 0..2 {
|
||||
if let Some(&hex_char) = chars.peek() {
|
||||
if hex_char.is_ascii_hexdigit() {
|
||||
hex_digits.push(hex_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&hex_digits, 16) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('x');
|
||||
prepared_arg.push_str(&hex_digits);
|
||||
}
|
||||
}
|
||||
'0' => {
|
||||
chars.next(); // consume '0'
|
||||
let mut octal_digits = String::new();
|
||||
for _ in 0..3 {
|
||||
if let Some(&octal_char) = chars.peek() {
|
||||
if ('0'..='7').contains(&octal_char) {
|
||||
octal_digits.push(octal_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&octal_digits, 8) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('0');
|
||||
prepared_arg.push_str(&octal_digits);
|
||||
}
|
||||
}
|
||||
'\\' => {
|
||||
prepared_arg.push('\\');
|
||||
chars.next();
|
||||
}
|
||||
_ => prepared_arg.push(c),
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
prepared_args.push(prepared_arg);
|
||||
}
|
||||
|
||||
Ok(prepared_args)
|
||||
}
|
||||
|
||||
pub fn get_echo_flags(mut opts: Vec<Opt>) -> ShResult<EchoFlags> {
|
||||
let mut flags = EchoFlags::empty();
|
||||
|
||||
@@ -82,6 +194,7 @@ pub fn get_echo_flags(mut opts: Vec<Opt>) -> ShResult<EchoFlags> {
|
||||
'n' => flags |= EchoFlags::NO_NEWLINE,
|
||||
'r' => flags |= EchoFlags::USE_STDERR,
|
||||
'e' => flags |= EchoFlags::USE_ESCAPE,
|
||||
'p' => flags |= EchoFlags::USE_PROMPT,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user