Implemented command substitution
This commit is contained in:
@@ -7,7 +7,7 @@ pub fn flowctl(node: Node, kind: ShErrKind) -> ShResult<()> {
|
|||||||
};
|
};
|
||||||
let mut code = 0;
|
let mut code = 0;
|
||||||
|
|
||||||
let mut argv = prepare_argv(argv);
|
let mut argv = prepare_argv(argv)?;
|
||||||
let cmd = argv.remove(0).0;
|
let cmd = argv.remove(0).0;
|
||||||
|
|
||||||
if !argv.is_empty() {
|
if !argv.is_empty() {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub fn setup_builtin(
|
|||||||
job: &mut JobBldr,
|
job: &mut JobBldr,
|
||||||
io_mode: Option<(&mut IoStack,Vec<Redir>)>,
|
io_mode: Option<(&mut IoStack,Vec<Redir>)>,
|
||||||
) -> ShResult<(Vec<(String,Span)>, Option<IoFrame>)> {
|
) -> ShResult<(Vec<(String,Span)>, Option<IoFrame>)> {
|
||||||
let mut argv: Vec<(String,Span)> = prepare_argv(argv);
|
let mut argv: Vec<(String,Span)> = prepare_argv(argv)?;
|
||||||
|
|
||||||
let child_pgid = if let Some(pgid) = job.pgid() {
|
let child_pgid = if let Some(pgid) = job.pgid() {
|
||||||
pgid
|
pgid
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
use crate::{libsh::error::ShResult, parse::lex::{is_field_sep, is_hard_sep, is_keyword, LexFlags, LexStream, Span, Tk, TkFlags, TkRule}, prelude::*, state::{read_logic, read_meta, read_vars, write_meta}};
|
use crate::{exec_input, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{lex::{is_field_sep, is_hard_sep, is_keyword, LexFlags, LexStream, Span, Tk, TkFlags, TkRule}, Redir, RedirType}, prelude::*, procio::{IoBuf, IoFrame, IoMode}, state::{read_logic, read_meta, read_vars, write_meta}};
|
||||||
|
|
||||||
/// Variable substitution marker
|
/// Variable substitution marker
|
||||||
pub const VAR_SUB: char = '\u{fdd0}';
|
pub const VAR_SUB: char = '\u{fdd0}';
|
||||||
@@ -16,10 +16,10 @@ impl Tk {
|
|||||||
/// tokens: A vector of raw tokens lexed from the expansion result
|
/// tokens: A vector of raw tokens lexed from the expansion result
|
||||||
/// span: The span of the original token that is being expanded
|
/// span: The span of the original token that is being expanded
|
||||||
/// flags: some TkFlags
|
/// flags: some TkFlags
|
||||||
pub fn expand(self, span: Span, flags: TkFlags) -> Self {
|
pub fn expand(self, span: Span, flags: TkFlags) -> ShResult<Self> {
|
||||||
let exp = Expander::new(self).expand();
|
let exp = Expander::new(self).expand()?;
|
||||||
let class = TkRule::Expanded { exp };
|
let class = TkRule::Expanded { exp };
|
||||||
Self { class, span, flags, }
|
Ok(Self { class, span, flags, })
|
||||||
}
|
}
|
||||||
pub fn get_words(&self) -> Vec<String> {
|
pub fn get_words(&self) -> Vec<String> {
|
||||||
match &self.class {
|
match &self.class {
|
||||||
@@ -38,9 +38,9 @@ impl Expander {
|
|||||||
let unescaped = unescape_str(raw.span.as_str());
|
let unescaped = unescape_str(raw.span.as_str());
|
||||||
Self { raw: unescaped }
|
Self { raw: unescaped }
|
||||||
}
|
}
|
||||||
pub fn expand(&mut self) -> Vec<String> {
|
pub fn expand(&mut self) -> ShResult<Vec<String>> {
|
||||||
self.raw = self.expand_raw();
|
self.raw = self.expand_raw()?;
|
||||||
self.split_words()
|
Ok(self.split_words())
|
||||||
}
|
}
|
||||||
pub fn split_words(&mut self) -> Vec<String> {
|
pub fn split_words(&mut self) -> Vec<String> {
|
||||||
let mut words = vec![];
|
let mut words = vec![];
|
||||||
@@ -68,18 +68,38 @@ impl Expander {
|
|||||||
}
|
}
|
||||||
words
|
words
|
||||||
}
|
}
|
||||||
pub fn expand_raw(&self) -> String {
|
pub fn expand_raw(&self) -> ShResult<String> {
|
||||||
let mut chars = self.raw.chars();
|
let mut chars = self.raw.chars().peekable();
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let mut var_name = String::new();
|
let mut var_name = String::new();
|
||||||
let mut in_brace = false;
|
let mut in_brace = false;
|
||||||
|
|
||||||
// TODO: implement error handling for unclosed braces
|
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
VAR_SUB => {
|
VAR_SUB => {
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
|
'(' if var_name.is_empty() => {
|
||||||
|
let mut paren_stack = vec!['('];
|
||||||
|
let mut subsh_body = String::new();
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
flog!(DEBUG, "looping");
|
||||||
|
flog!(DEBUG, subsh_body);
|
||||||
|
match ch {
|
||||||
|
'(' => {
|
||||||
|
paren_stack.push(ch);
|
||||||
|
subsh_body.push(ch);
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
paren_stack.pop();
|
||||||
|
if paren_stack.is_empty() { break };
|
||||||
|
subsh_body.push(ch);
|
||||||
|
}
|
||||||
|
_ => subsh_body.push(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_str(&expand_cmd_sub(&subsh_body)?);
|
||||||
|
}
|
||||||
'{' => in_brace = true,
|
'{' => in_brace = true,
|
||||||
'}' if in_brace => {
|
'}' if in_brace => {
|
||||||
let var_val = read_vars(|v| v.get_var(&var_name));
|
let var_val = read_vars(|v| v.get_var(&var_name));
|
||||||
@@ -106,7 +126,44 @@ impl Expander {
|
|||||||
_ => result.push(ch)
|
_ => result.push(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the command output of a given command input as a String
|
||||||
|
pub fn expand_cmd_sub(raw: &str) -> ShResult<String> {
|
||||||
|
flog!(DEBUG, "in expand_cmd_sub");
|
||||||
|
let (rpipe,wpipe) = IoMode::get_pipes();
|
||||||
|
let cmd_sub_redir = Redir::new(wpipe, RedirType::Output);
|
||||||
|
let mut cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir);
|
||||||
|
let mut io_buf = IoBuf::new(rpipe);
|
||||||
|
|
||||||
|
match unsafe { fork()? } {
|
||||||
|
ForkResult::Child => {
|
||||||
|
if let Err(e) = cmd_sub_io_frame.redirect() {
|
||||||
|
eprintln!("{e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = exec_input(raw.to_string()) {
|
||||||
|
eprintln!("{e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
ForkResult::Parent { child } => {
|
||||||
|
std::mem::drop(cmd_sub_io_frame); // Closes the write pipe
|
||||||
|
let status = waitpid(child, Some(WtFlag::WSTOPPED))?;
|
||||||
|
match status {
|
||||||
|
WtStat::Exited(_, _) => {
|
||||||
|
flog!(DEBUG, "filling buffer");
|
||||||
|
io_buf.fill_buffer()?;
|
||||||
|
flog!(DEBUG, "done");
|
||||||
|
Ok(io_buf.as_str()?.trim().to_string())
|
||||||
|
}
|
||||||
|
_ => return Err(ShErr::simple(ShErrKind::InternalErr, "Command sub failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ pub trait VecDequeExt<T> {
|
|||||||
fn to_vec(self) -> Vec<T>;
|
fn to_vec(self) -> Vec<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CharDequeUtils {
|
||||||
|
fn to_string(self) -> String;
|
||||||
|
fn ends_with(&self, pat: &str) -> bool;
|
||||||
|
fn starts_with(&self, pat: &str) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait TkVecUtils<Tk> {
|
pub trait TkVecUtils<Tk> {
|
||||||
fn get_span(&self) -> Option<Span>;
|
fn get_span(&self) -> Option<Span>;
|
||||||
fn debug_tokens(&self);
|
fn debug_tokens(&self);
|
||||||
@@ -26,6 +32,42 @@ impl<T> VecDequeExt<T> for VecDeque<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CharDequeUtils for VecDeque<char> {
|
||||||
|
fn to_string(mut self) -> String {
|
||||||
|
let mut result = String::with_capacity(self.len());
|
||||||
|
while let Some(ch) = self.pop_front() {
|
||||||
|
result.push(ch);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ends_with(&self, pat: &str) -> bool {
|
||||||
|
let pat_chars = pat.chars();
|
||||||
|
let self_len = self.len();
|
||||||
|
|
||||||
|
// If pattern is longer than self, return false
|
||||||
|
if pat_chars.clone().count() > self_len {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare from the back
|
||||||
|
self.iter().rev().zip(pat_chars.rev()).all(|(c1, c2)| c1 == &c2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn starts_with(&self, pat: &str) -> bool {
|
||||||
|
let pat_chars = pat.chars();
|
||||||
|
let self_len = self.len();
|
||||||
|
|
||||||
|
// If pattern is longer than self, return false
|
||||||
|
if pat_chars.clone().count() > self_len {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare from the front
|
||||||
|
self.iter().zip(pat_chars).all(|(c1, c2)| c1 == &c2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TkVecUtils<Tk> for Vec<Tk> {
|
impl TkVecUtils<Tk> for Vec<Tk> {
|
||||||
fn get_span(&self) -> Option<Span> {
|
fn get_span(&self) -> Option<Span> {
|
||||||
if let Some(first_tk) = self.first() {
|
if let Some(first_tk) = self.first() {
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ pub struct ExecArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ExecArgs {
|
impl ExecArgs {
|
||||||
pub fn new(argv: Vec<Tk>) -> Self {
|
pub fn new(argv: Vec<Tk>) -> ShResult<Self> {
|
||||||
assert!(!argv.is_empty());
|
assert!(!argv.is_empty());
|
||||||
let argv = prepare_argv(argv);
|
let argv = prepare_argv(argv)?;
|
||||||
let cmd = Self::get_cmd(&argv);
|
let cmd = Self::get_cmd(&argv);
|
||||||
let argv = Self::get_argv(argv);
|
let argv = Self::get_argv(argv);
|
||||||
let envp = Self::get_envp();
|
let envp = Self::get_envp();
|
||||||
Self { cmd, argv, envp }
|
Ok(Self { cmd, argv, envp })
|
||||||
}
|
}
|
||||||
pub fn get_cmd(argv: &[(String,Span)]) -> (CString,Span) {
|
pub fn get_cmd(argv: &[(String,Span)]) -> (CString,Span) {
|
||||||
(CString::new(argv[0].0.as_str()).unwrap(),argv[0].1.clone())
|
(CString::new(argv[0].0.as_str()).unwrap(),argv[0].1.clone())
|
||||||
@@ -197,7 +197,7 @@ impl Dispatcher {
|
|||||||
self.io_stack.append_to_frame(case_stmt.redirs);
|
self.io_stack.append_to_frame(case_stmt.redirs);
|
||||||
|
|
||||||
flog!(DEBUG,pattern.span.as_str());
|
flog!(DEBUG,pattern.span.as_str());
|
||||||
let exp_pattern = pattern.clone().expand(pattern.span.clone(), pattern.flags.clone());
|
let exp_pattern = pattern.clone().expand(pattern.span.clone(), pattern.flags.clone())?;
|
||||||
let pattern_raw = exp_pattern
|
let pattern_raw = exp_pattern
|
||||||
.get_words()
|
.get_words()
|
||||||
.first()
|
.first()
|
||||||
@@ -396,7 +396,7 @@ impl Dispatcher {
|
|||||||
|
|
||||||
self.io_stack.append_to_frame(cmd.redirs);
|
self.io_stack.append_to_frame(cmd.redirs);
|
||||||
|
|
||||||
let exec_args = ExecArgs::new(argv);
|
let exec_args = ExecArgs::new(argv)?;
|
||||||
let io_frame = self.io_stack.pop_frame();
|
let io_frame = self.io_stack.pop_frame();
|
||||||
run_fork(
|
run_fork(
|
||||||
io_frame,
|
io_frame,
|
||||||
@@ -453,18 +453,18 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_argv(argv: Vec<Tk>) -> Vec<(String,Span)> {
|
pub fn prepare_argv(argv: Vec<Tk>) -> ShResult<Vec<(String,Span)>> {
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
|
||||||
for arg in argv {
|
for arg in argv {
|
||||||
let flags = arg.flags;
|
let flags = arg.flags;
|
||||||
let span = arg.span.clone();
|
let span = arg.span.clone();
|
||||||
let expanded = arg.expand(span.clone(), flags);
|
let expanded = arg.expand(span.clone(), flags)?;
|
||||||
for exp in expanded.get_words() {
|
for exp in expanded.get_words() {
|
||||||
args.push((exp,span.clone()))
|
args.push((exp,span.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_fork<'t,C,P>(
|
pub fn run_fork<'t,C,P>(
|
||||||
|
|||||||
116
src/parse/lex.rs
116
src/parse/lex.rs
@@ -1,8 +1,8 @@
|
|||||||
use std::{fmt::Display, ops::{Bound, Deref, Range, RangeBounds}, str::Chars};
|
use std::{collections::VecDeque, fmt::Display, iter::Peekable, ops::{Bound, Deref, Range, RangeBounds}, str::Chars};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use crate::{builtin::BUILTINS, libsh::error::{ShErr, ShErrKind, ShResult}, prelude::*};
|
use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, prelude::*};
|
||||||
|
|
||||||
pub const KEYWORDS: [&'static str;14] = [
|
pub const KEYWORDS: [&'static str;14] = [
|
||||||
"if",
|
"if",
|
||||||
@@ -137,9 +137,10 @@ bitflags! {
|
|||||||
/// This is a keyword that opens a new block statement, like 'if' and 'while'
|
/// This is a keyword that opens a new block statement, like 'if' and 'while'
|
||||||
const OPENER = 0b0000000000000010;
|
const OPENER = 0b0000000000000010;
|
||||||
const IS_CMD = 0b0000000000000100;
|
const IS_CMD = 0b0000000000000100;
|
||||||
const IS_OP = 0b0000000000001000;
|
const IS_SUBSH = 0b0000000000001000;
|
||||||
const ASSIGN = 0b0000000000010000;
|
const IS_OP = 0b0000000000010000;
|
||||||
const BUILTIN = 0b0000000000100000;
|
const ASSIGN = 0b0000000000100000;
|
||||||
|
const BUILTIN = 0b0000000001000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +310,7 @@ impl LexStream {
|
|||||||
assert!(self.cursor <= self.source.len());
|
assert!(self.cursor <= self.source.len());
|
||||||
let slice = self.slice_from_cursor().unwrap().to_string();
|
let slice = self.slice_from_cursor().unwrap().to_string();
|
||||||
let mut pos = self.cursor;
|
let mut pos = self.cursor;
|
||||||
let mut chars = slice.chars();
|
let mut chars = slice.chars().peekable();
|
||||||
let mut quote_pos = None;
|
let mut quote_pos = None;
|
||||||
|
|
||||||
if let Some(count) = case_pat_lookahead(chars.clone()) {
|
if let Some(count) = case_pat_lookahead(chars.clone()) {
|
||||||
@@ -331,10 +332,89 @@ impl LexStream {
|
|||||||
}
|
}
|
||||||
'\\' => {
|
'\\' => {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
if chars.next().is_some() {
|
if let Some(ch) = chars.next() {
|
||||||
pos += 1;
|
pos += ch.len_utf8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
'$' if chars.peek() == Some(&'(') => {
|
||||||
|
pos += 2;
|
||||||
|
chars.next();
|
||||||
|
let mut paren_stack = vec!['('];
|
||||||
|
let paren_pos = pos;
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
pos += 1;
|
||||||
|
if let Some(next_ch) = chars.next() {
|
||||||
|
pos += next_ch.len_utf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
pos += 1;
|
||||||
|
paren_stack.push(ch);
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
pos += 1;
|
||||||
|
paren_stack.pop();
|
||||||
|
if paren_stack.is_empty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => pos += ch.len_utf8()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !paren_stack.is_empty() {
|
||||||
|
return Err(
|
||||||
|
ShErr::full(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
"Unclosed subshell",
|
||||||
|
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' if self.next_is_cmd() => {
|
||||||
|
let mut paren_stack = vec!['('];
|
||||||
|
let paren_pos = pos;
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
pos += ch.len_utf8();
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
if let Some(next_ch) = chars.next() {
|
||||||
|
pos += next_ch.len_utf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
pos += 1;
|
||||||
|
paren_stack.push(ch);
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
pos += 1;
|
||||||
|
paren_stack.pop();
|
||||||
|
if paren_stack.is_empty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !paren_stack.is_empty() {
|
||||||
|
return Err(
|
||||||
|
ShErr::full(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
"Unclosed subshell",
|
||||||
|
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let mut subsh_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||||
|
subsh_tk.flags |= TkFlags::IS_CMD;
|
||||||
|
subsh_tk.flags |= TkFlags::IS_SUBSH;
|
||||||
|
self.cursor = pos;
|
||||||
|
self.set_next_is_cmd(true);
|
||||||
|
flog!(DEBUG, subsh_tk);
|
||||||
|
return Ok(subsh_tk)
|
||||||
|
}
|
||||||
'{' if pos == self.cursor && self.next_is_cmd() => {
|
'{' if pos == self.cursor && self.next_is_cmd() => {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
let mut tk = self.get_token(self.cursor..pos, TkRule::BraceGrpStart);
|
let mut tk = self.get_token(self.cursor..pos, TkRule::BraceGrpStart);
|
||||||
@@ -384,6 +464,7 @@ impl LexStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut new_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
let mut new_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||||
|
flog!(DEBUG,new_tk);
|
||||||
if self.in_quote && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
if self.in_quote && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||||
return Err(
|
return Err(
|
||||||
ShErr::full(
|
ShErr::full(
|
||||||
@@ -428,6 +509,7 @@ impl LexStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cursor = pos;
|
self.cursor = pos;
|
||||||
|
flog!(DEBUG, self.slice_from_cursor());
|
||||||
Ok(new_tk)
|
Ok(new_tk)
|
||||||
}
|
}
|
||||||
pub fn get_token(&self, range: Range<usize>, class: TkRule) -> Tk {
|
pub fn get_token(&self, range: Range<usize>, class: TkRule) -> Tk {
|
||||||
@@ -595,7 +677,23 @@ pub fn is_keyword(slice: &str) -> bool {
|
|||||||
(slice.ends_with("()") && !slice.ends_with("\\()"))
|
(slice.ends_with("()") && !slice.ends_with("\\()"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn case_pat_lookahead(mut chars: Chars) -> Option<usize> {
|
pub fn lookahead(pat: &str, mut chars: Chars) -> Option<usize> {
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut char_deque = VecDeque::new();
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
char_deque.push_back(ch);
|
||||||
|
if char_deque.len() > pat.len() {
|
||||||
|
char_deque.pop_front();
|
||||||
|
}
|
||||||
|
if char_deque.starts_with(pat) {
|
||||||
|
return Some(pos)
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn case_pat_lookahead(mut chars: Peekable<Chars>) -> Option<usize> {
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ impl ParsedSrc {
|
|||||||
for token in LexStream::new(self.src.clone(), LexFlags::empty()) {
|
for token in LexStream::new(self.src.clone(), LexFlags::empty()) {
|
||||||
tokens.push(token?);
|
tokens.push(token?);
|
||||||
}
|
}
|
||||||
|
flog!(DEBUG,tokens);
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
for result in ParseStream::new(tokens) {
|
for result in ParseStream::new(tokens) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{fmt::Debug, ops::{Deref, DerefMut}};
|
use std::{fmt::Debug, ops::{Deref, DerefMut}};
|
||||||
|
|
||||||
use crate::{libsh::{error::ShResult, utils::RedirVecUtils}, parse::{Redir, RedirType}, prelude::*};
|
use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::RedirVecUtils}, parse::{Redir, RedirType}, prelude::*};
|
||||||
|
|
||||||
// Credit to fish-shell for many of the implementation ideas present in this module
|
// Credit to fish-shell for many of the implementation ideas present in this module
|
||||||
// https://fishshell.com/
|
// https://fishshell.com/
|
||||||
@@ -10,6 +10,7 @@ pub enum IoMode {
|
|||||||
Fd { tgt_fd: RawFd, src_fd: Rc<OwnedFd> },
|
Fd { tgt_fd: RawFd, src_fd: Rc<OwnedFd> },
|
||||||
File { tgt_fd: RawFd, file: Rc<File> },
|
File { tgt_fd: RawFd, file: Rc<File> },
|
||||||
Pipe { tgt_fd: RawFd, pipe: Rc<OwnedFd> },
|
Pipe { tgt_fd: RawFd, pipe: Rc<OwnedFd> },
|
||||||
|
Buffer { buf: String, pipe: Rc<OwnedFd> }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoMode {
|
impl IoMode {
|
||||||
@@ -29,14 +30,16 @@ impl IoMode {
|
|||||||
match self {
|
match self {
|
||||||
IoMode::Fd { tgt_fd, src_fd: _ } |
|
IoMode::Fd { tgt_fd, src_fd: _ } |
|
||||||
IoMode::File { tgt_fd, file: _ } |
|
IoMode::File { tgt_fd, file: _ } |
|
||||||
IoMode::Pipe { tgt_fd, pipe: _ } => *tgt_fd
|
IoMode::Pipe { tgt_fd, pipe: _ } => *tgt_fd,
|
||||||
|
_ => panic!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn src_fd(&self) -> RawFd {
|
pub fn src_fd(&self) -> RawFd {
|
||||||
match self {
|
match self {
|
||||||
IoMode::Fd { tgt_fd: _, src_fd } => src_fd.as_raw_fd(),
|
IoMode::Fd { tgt_fd: _, src_fd } => src_fd.as_raw_fd(),
|
||||||
IoMode::File { tgt_fd: _, file } => file.as_raw_fd(),
|
IoMode::File { tgt_fd: _, file } => file.as_raw_fd(),
|
||||||
IoMode::Pipe { tgt_fd: _, pipe } => pipe.as_raw_fd()
|
IoMode::Pipe { tgt_fd: _, pipe } => pipe.as_raw_fd(),
|
||||||
|
_ => panic!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_pipes() -> (Self,Self) {
|
pub fn get_pipes() -> (Self,Self) {
|
||||||
@@ -55,6 +58,50 @@ impl Read for IoMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct IoBuf<R: Read> {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
reader: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> IoBuf<R> {
|
||||||
|
pub fn new(reader: R) -> Self {
|
||||||
|
Self {
|
||||||
|
buf: Vec::new(),
|
||||||
|
reader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads exactly `size` bytes (or fewer if EOF) into the buffer
|
||||||
|
pub fn read_buffer(&mut self, size: usize) -> io::Result<()> {
|
||||||
|
let mut temp_buf = vec![0; size]; // Temporary buffer
|
||||||
|
let bytes_read = self.reader.read(&mut temp_buf)?;
|
||||||
|
self.buf.extend_from_slice(&temp_buf[..bytes_read]); // Append only what was read
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Continuously reads until EOF
|
||||||
|
pub fn fill_buffer(&mut self) -> io::Result<()> {
|
||||||
|
let mut temp_buf = vec![0; 1024]; // Read in chunks
|
||||||
|
loop {
|
||||||
|
flog!(DEBUG, "reading bytes");
|
||||||
|
let bytes_read = self.reader.read(&mut temp_buf)?;
|
||||||
|
flog!(DEBUG, bytes_read);
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break; // EOF reached
|
||||||
|
}
|
||||||
|
self.buf.extend_from_slice(&temp_buf[..bytes_read]);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current buffer contents as a string (if valid UTF-8)
|
||||||
|
pub fn as_str(&self) -> ShResult<&str> {
|
||||||
|
std::str::from_utf8(&self.buf).map_err(|_| {
|
||||||
|
ShErr::simple(ShErrKind::InternalErr, "Invalid utf-8 in IoBuf")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A struct wrapping three fildescs representing `stdin`, `stdout`, and `stderr` respectively
|
/// A struct wrapping three fildescs representing `stdin`, `stdout`, and `stderr` respectively
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
pub struct IoGroup(RawFd,RawFd,RawFd);
|
pub struct IoGroup(RawFd,RawFd,RawFd);
|
||||||
@@ -74,6 +121,9 @@ impl<'e> IoFrame {
|
|||||||
pub fn from_redirs(redirs: Vec<Redir>) -> Self {
|
pub fn from_redirs(redirs: Vec<Redir>) -> Self {
|
||||||
Self { redirs, saved_io: None }
|
Self { redirs, saved_io: None }
|
||||||
}
|
}
|
||||||
|
pub fn from_redir(redir: Redir) -> Self {
|
||||||
|
Self { redirs: vec![redir], saved_io: None }
|
||||||
|
}
|
||||||
|
|
||||||
/// Splits the frame into two frames
|
/// Splits the frame into two frames
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user