implemented read command for ex mode
This commit is contained in:
@@ -4,7 +4,18 @@ use ariadne::Span as ASpan;
|
||||
use nix::libc::STDIN_FILENO;
|
||||
|
||||
use crate::{
|
||||
libsh::{error::{ShErr, ShErrKind, ShResult}, guards::RawModeGuard}, parse::{NdRule, Node, Redir, RedirType, execute::{exec_input, prepare_argv}, lex::{QuoteState, Span}}, procio::{IoFrame, IoMode}, readline::{complete::ScoredCandidate, markers}, state
|
||||
libsh::{
|
||||
error::{ShErr, ShErrKind, ShResult},
|
||||
guards::RawModeGuard,
|
||||
},
|
||||
parse::{
|
||||
NdRule, Node, Redir, RedirType,
|
||||
execute::{exec_input, prepare_argv},
|
||||
lex::{QuoteState, Span},
|
||||
},
|
||||
procio::{IoFrame, IoMode},
|
||||
readline::{complete::ScoredCandidate, markers},
|
||||
state,
|
||||
};
|
||||
|
||||
const TAG_SEQ: &str = "\x1b[1;33m"; // bold yellow — searchable tags
|
||||
@@ -58,10 +69,7 @@ pub fn help(node: Node) -> ShResult<()> {
|
||||
let Ok(contents) = std::fs::read_to_string(&path) else {
|
||||
continue;
|
||||
};
|
||||
let filename = path.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let filename = path.file_stem().unwrap().to_string_lossy().to_string();
|
||||
|
||||
let unescaped = unescape_help(&contents);
|
||||
let expanded = expand_help(&unescaped);
|
||||
@@ -78,10 +86,7 @@ pub fn help(node: Node) -> ShResult<()> {
|
||||
for entry in entries {
|
||||
let Ok(entry) = entry else { continue };
|
||||
let path = entry.path();
|
||||
let filename = path.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let filename = path.file_stem().unwrap().to_string_lossy().to_string();
|
||||
|
||||
if !path.is_file() {
|
||||
continue;
|
||||
@@ -95,8 +100,7 @@ pub fn help(node: Node) -> ShResult<()> {
|
||||
let expanded = expand_help(&unescaped);
|
||||
let tags = read_tags(&expanded);
|
||||
|
||||
for (tag, line) in &tags {
|
||||
}
|
||||
for (tag, line) in &tags {}
|
||||
|
||||
if let Some((matched_tag, line)) = get_best_match(&topic, &tags) {
|
||||
open_help(&expanded, Some(line), Some(filename))?;
|
||||
@@ -119,7 +123,9 @@ pub fn help(node: Node) -> ShResult<()> {
|
||||
pub fn open_help(content: &str, line: Option<usize>, file_name: Option<String>) -> ShResult<()> {
|
||||
let pager = env::var("PAGER").unwrap_or("less -R".into());
|
||||
let line_arg = line.map(|ln| format!("+{ln}")).unwrap_or_default();
|
||||
let prompt_arg = file_name.map(|name| format!("-Ps'{name}'")).unwrap_or_default();
|
||||
let prompt_arg = file_name
|
||||
.map(|name| format!("-Ps'{name}'"))
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut tmp = tempfile::NamedTempFile::new()?;
|
||||
let tmp_path = tmp.path().to_string_lossy().to_string();
|
||||
@@ -137,7 +143,8 @@ pub fn open_help(content: &str, line: Option<usize>, file_name: Option<String>)
|
||||
}
|
||||
|
||||
pub fn get_best_match(topic: &str, tags: &[(String, usize)]) -> Option<(String, usize)> {
|
||||
let mut candidates: Vec<_> = tags.iter()
|
||||
let mut candidates: Vec<_> = tags
|
||||
.iter()
|
||||
.map(|(tag, line)| (ScoredCandidate::new(tag.to_string()), *line))
|
||||
.collect();
|
||||
|
||||
@@ -148,7 +155,9 @@ pub fn get_best_match(topic: &str, tags: &[(String, usize)]) -> Option<(String,
|
||||
candidates.retain(|(c, _)| c.score.unwrap_or(i32::MIN) > i32::MIN);
|
||||
candidates.sort_by_key(|(c, _)| c.score.unwrap_or(i32::MIN));
|
||||
|
||||
candidates.first().map(|(c,line)| (c.content.clone(), *line))
|
||||
candidates
|
||||
.first()
|
||||
.map(|(c, line)| (c.content.clone(), *line))
|
||||
}
|
||||
|
||||
pub fn read_tags(raw: &str) -> Vec<(String, usize)> {
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod eval;
|
||||
pub mod exec;
|
||||
pub mod flowctl;
|
||||
pub mod getopts;
|
||||
pub mod help;
|
||||
pub mod intro;
|
||||
pub mod jobctl;
|
||||
pub mod keymap;
|
||||
@@ -25,7 +26,6 @@ pub mod source;
|
||||
pub mod test; // [[ ]] thing
|
||||
pub mod trap;
|
||||
pub mod varcmds;
|
||||
pub mod help;
|
||||
|
||||
pub const BUILTINS: [&str; 51] = [
|
||||
"echo", "cd", "read", "export", "local", "pwd", "source", ".", "shift", "jobs", "fg", "bg",
|
||||
|
||||
@@ -483,20 +483,19 @@ pub fn expand_raw(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
||||
let home = if username.is_empty() {
|
||||
// standard '~' expansion
|
||||
env::var("HOME").unwrap_or_default()
|
||||
}
|
||||
else if let Ok(result) = User::from_name(&username)
|
||||
&& let Some(user) = result {
|
||||
} else if let Ok(result) = User::from_name(&username)
|
||||
&& let Some(user) = result
|
||||
{
|
||||
// username expansion like '~user'
|
||||
user.dir.to_string_lossy().to_string()
|
||||
}
|
||||
else if let Ok(id) = username.parse::<u32>()
|
||||
} else if let Ok(id) = username.parse::<u32>()
|
||||
&& let Ok(result) = User::from_uid(Uid::from_raw(id))
|
||||
&& let Some(user) = result {
|
||||
&& let Some(user) = result
|
||||
{
|
||||
// uid expansion like '~1000'
|
||||
// shed only feature btw B)
|
||||
user.dir.to_string_lossy().to_string()
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// no match, use literal
|
||||
format!("~{username}")
|
||||
};
|
||||
@@ -1623,10 +1622,18 @@ impl FromStr for ParamExp {
|
||||
))
|
||||
};
|
||||
|
||||
if s == "^^" { return Ok(ToUpperAll) }
|
||||
if s == "^" { return Ok(ToUpperFirst) }
|
||||
if s == ",," { return Ok(ToLowerAll) }
|
||||
if s == "," { return Ok(ToLowerFirst) }
|
||||
if s == "^^" {
|
||||
return Ok(ToUpperAll);
|
||||
}
|
||||
if s == "^" {
|
||||
return Ok(ToUpperFirst);
|
||||
}
|
||||
if s == ",," {
|
||||
return Ok(ToLowerAll);
|
||||
}
|
||||
if s == "," {
|
||||
return Ok(ToLowerFirst);
|
||||
}
|
||||
|
||||
// Handle indirect var expansion: ${!var}
|
||||
if let Some(var) = s.strip_prefix('!') {
|
||||
@@ -1752,12 +1759,11 @@ pub fn perform_param_expansion(raw: &str) -> ShResult<String> {
|
||||
ParamExp::ToUpperFirst => {
|
||||
let value = vars.get_var(&var_name);
|
||||
let mut chars = value.chars();
|
||||
let first = chars.next()
|
||||
.map(|c| c.to_uppercase()
|
||||
.to_string())
|
||||
let first = chars
|
||||
.next()
|
||||
.map(|c| c.to_uppercase().to_string())
|
||||
.unwrap_or_default();
|
||||
Ok(first + chars.as_str())
|
||||
|
||||
}
|
||||
ParamExp::ToLowerAll => {
|
||||
let value = vars.get_var(&var_name);
|
||||
@@ -1766,7 +1772,8 @@ pub fn perform_param_expansion(raw: &str) -> ShResult<String> {
|
||||
ParamExp::ToLowerFirst => {
|
||||
let value = vars.get_var(&var_name);
|
||||
let mut chars = value.chars();
|
||||
let first = chars.next()
|
||||
let first = chars
|
||||
.next()
|
||||
.map(|c| c.to_lowercase().to_string())
|
||||
.unwrap_or_default();
|
||||
Ok(first + chars.as_str())
|
||||
|
||||
25
src/jobs.rs
25
src/jobs.rs
@@ -599,24 +599,27 @@ impl Job {
|
||||
.collect::<Vec<WtStat>>()
|
||||
}
|
||||
pub fn pipe_status(stats: &[WtStat]) -> Option<Vec<i32>> {
|
||||
if stats.iter()
|
||||
.any(|stat| matches!(stat, WtStat::StillAlive | WtStat::Continued(_) | WtStat::PtraceSyscall(_)))
|
||||
|| stats.len() <= 1 {
|
||||
if stats.iter().any(|stat| {
|
||||
matches!(
|
||||
stat,
|
||||
WtStat::StillAlive | WtStat::Continued(_) | WtStat::PtraceSyscall(_)
|
||||
)
|
||||
}) || stats.len() <= 1
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(stats.iter()
|
||||
.map(|stat| {
|
||||
match stat {
|
||||
Some(
|
||||
stats
|
||||
.iter()
|
||||
.map(|stat| match stat {
|
||||
WtStat::Exited(_, code) => *code,
|
||||
WtStat::Signaled(_, signal, _) => SIG_EXIT_OFFSET + *signal as i32,
|
||||
WtStat::Stopped(_, signal) => SIG_EXIT_OFFSET + *signal as i32,
|
||||
WtStat::PtraceEvent(_, signal, _) => SIG_EXIT_OFFSET + *signal as i32,
|
||||
WtStat::PtraceSyscall(_) |
|
||||
WtStat::Continued(_) |
|
||||
WtStat::StillAlive => unreachable!()
|
||||
}
|
||||
WtStat::PtraceSyscall(_) | WtStat::Continued(_) | WtStat::StillAlive => unreachable!(),
|
||||
})
|
||||
.collect())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
pub fn get_pids(&self) -> Vec<Pid> {
|
||||
self
|
||||
|
||||
@@ -40,7 +40,8 @@ use crate::readline::term::{LineWriter, RawModeGuard, raw_mode};
|
||||
use crate::readline::{Prompt, ReadlineEvent, ShedVi};
|
||||
use crate::signal::{GOT_SIGWINCH, JOB_DONE, QUIT_CODE, check_signals, sig_setup, signals_pending};
|
||||
use crate::state::{
|
||||
AutoCmdKind, read_logic, read_shopts, source_env, source_login, source_rc, write_jobs, write_meta, write_shopts
|
||||
AutoCmdKind, read_logic, read_shopts, source_env, source_login, source_rc, write_jobs,
|
||||
write_meta, write_shopts,
|
||||
};
|
||||
use clap::Parser;
|
||||
use state::write_vars;
|
||||
@@ -222,7 +223,8 @@ fn shed_interactive(args: ShedArgs) -> ShResult<()> {
|
||||
sig_setup(args.login_shell);
|
||||
|
||||
if args.login_shell
|
||||
&& let Err(e) = source_login() {
|
||||
&& let Err(e) = source_login()
|
||||
{
|
||||
e.print_error();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,30 @@ use ariadne::Fmt;
|
||||
|
||||
use crate::{
|
||||
builtin::{
|
||||
alias::{alias, unalias}, arrops::{arr_fpop, arr_fpush, arr_pop, arr_push, arr_rotate}, autocmd::autocmd, cd::cd, complete::{compgen_builtin, complete_builtin}, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, flowctl::flowctl, getopts::getopts, help::help, intro, jobctl::{self, JobBehavior, continue_job, disown, jobs}, keymap, map, pwd::pwd, read::{self, read_builtin}, resource::{ulimit, umask_builtin}, seek::seek, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, varcmds::{export, local, readonly, unset}
|
||||
alias::{alias, unalias},
|
||||
arrops::{arr_fpop, arr_fpush, arr_pop, arr_push, arr_rotate},
|
||||
autocmd::autocmd,
|
||||
cd::cd,
|
||||
complete::{compgen_builtin, complete_builtin},
|
||||
dirstack::{dirs, popd, pushd},
|
||||
echo::echo,
|
||||
eval, exec,
|
||||
flowctl::flowctl,
|
||||
getopts::getopts,
|
||||
help::help,
|
||||
intro,
|
||||
jobctl::{self, JobBehavior, continue_job, disown, jobs},
|
||||
keymap, map,
|
||||
pwd::pwd,
|
||||
read::{self, read_builtin},
|
||||
resource::{ulimit, umask_builtin},
|
||||
seek::seek,
|
||||
shift::shift,
|
||||
shopt::shopt,
|
||||
source::source,
|
||||
test::double_bracket_test,
|
||||
trap::{TrapTarget, trap},
|
||||
varcmds::{export, local, readonly, unset},
|
||||
},
|
||||
expand::{expand_aliases, expand_case_pattern, glob_to_regex},
|
||||
jobs::{ChildProc, JobStack, attach_tty, dispatch_job},
|
||||
|
||||
@@ -1084,12 +1084,10 @@ impl ParseStream {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !from_func_def {
|
||||
self.parse_redir(&mut redirs, &mut node_tks)?;
|
||||
}
|
||||
|
||||
|
||||
let node = Node {
|
||||
class: NdRule::BraceGrp { body },
|
||||
flags: NdFlags::empty(),
|
||||
|
||||
@@ -118,7 +118,10 @@ impl IoMode {
|
||||
pub fn loaded_pipe(tgt_fd: RawFd, buf: &[u8]) -> ShResult<Self> {
|
||||
let (rpipe, wpipe) = nix::unistd::pipe()?;
|
||||
write(wpipe, buf)?;
|
||||
Ok(Self::Pipe { tgt_fd, pipe: rpipe.into() })
|
||||
Ok(Self::Pipe {
|
||||
tgt_fd,
|
||||
pipe: rpipe.into(),
|
||||
})
|
||||
}
|
||||
pub fn get_pipes() -> (Self, Self) {
|
||||
let (rpipe, wpipe) = nix::unistd::pipe2(OFlag::O_CLOEXEC).unwrap();
|
||||
|
||||
@@ -215,7 +215,7 @@ pub struct History {
|
||||
//search_direction: Direction,
|
||||
ignore_dups: bool,
|
||||
max_size: Option<u32>,
|
||||
stateless: bool
|
||||
stateless: bool,
|
||||
}
|
||||
|
||||
impl History {
|
||||
|
||||
@@ -12,6 +12,7 @@ use super::vicmd::{
|
||||
ViCmd, Word,
|
||||
};
|
||||
use crate::{
|
||||
expand::expand_cmd_sub,
|
||||
libsh::{error::ShResult, guards::var_ctx_guard},
|
||||
parse::{
|
||||
execute::exec_input,
|
||||
@@ -23,6 +24,7 @@ use crate::{
|
||||
markers,
|
||||
register::{RegisterContent, write_register},
|
||||
term::RawModeGuard,
|
||||
vicmd::ReadSrc,
|
||||
},
|
||||
state::{VarFlags, VarKind, read_shopts, write_meta, write_vars},
|
||||
};
|
||||
@@ -464,10 +466,7 @@ impl Default for LineBuf {
|
||||
|
||||
impl LineBuf {
|
||||
pub fn new() -> Self {
|
||||
let mut new = Self {
|
||||
grapheme_indices: Some(vec![]), // We know the buffer is empty, so this keeps us safe from unwrapping None
|
||||
..Default::default()
|
||||
};
|
||||
let mut new = Self::default();
|
||||
new.update_graphemes();
|
||||
new
|
||||
}
|
||||
@@ -3329,12 +3328,39 @@ impl LineBuf {
|
||||
| Verb::CompleteBackward
|
||||
| Verb::VisualModeSelectLast => self.apply_motion(motion),
|
||||
Verb::ShellCmd(cmd) => self.verb_shell_cmd(cmd)?,
|
||||
Verb::Normal(_)
|
||||
| Verb::Read(_)
|
||||
| Verb::Write(_)
|
||||
| Verb::Substitute(..)
|
||||
| Verb::RepeatSubstitute
|
||||
| Verb::RepeatGlobal => {}
|
||||
Verb::Read(src) => match src {
|
||||
ReadSrc::File(path_buf) => {
|
||||
if !path_buf.is_file() {
|
||||
write_meta(|m| m.post_system_message(format!("{} is not a file", path_buf.display())));
|
||||
return Ok(());
|
||||
}
|
||||
let Ok(contents) = std::fs::read_to_string(&path_buf) else {
|
||||
write_meta(|m| {
|
||||
m.post_system_message(format!("Failed to read file {}", path_buf.display()))
|
||||
});
|
||||
return Ok(());
|
||||
};
|
||||
let grapheme_count = contents.graphemes(true).count();
|
||||
self.insert_str_at_cursor(&contents);
|
||||
self.cursor.add(grapheme_count);
|
||||
}
|
||||
ReadSrc::Cmd(cmd) => {
|
||||
let output = match expand_cmd_sub(&cmd) {
|
||||
Ok(out) => out,
|
||||
Err(e) => {
|
||||
e.print_error();
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let grapheme_count = output.graphemes(true).count();
|
||||
self.insert_str_at_cursor(&output);
|
||||
self.cursor.add(grapheme_count);
|
||||
}
|
||||
},
|
||||
Verb::Write(dest) => {}
|
||||
Verb::Edit(path) => {}
|
||||
Verb::Normal(_) | Verb::Substitute(..) | Verb::RepeatSubstitute | Verb::RepeatGlobal => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::readline::vimode::ex::SubFlags;
|
||||
|
||||
use super::register::{RegisterContent, append_register, read_register, write_register};
|
||||
|
||||
//TODO: write tests that take edit results and cursor positions from actual
|
||||
@@ -256,7 +260,8 @@ pub enum Verb {
|
||||
Normal(String),
|
||||
Read(ReadSrc),
|
||||
Write(WriteDest),
|
||||
Substitute(String, String, super::vimode::ex::SubFlags),
|
||||
Edit(PathBuf),
|
||||
Substitute(String, String, SubFlags),
|
||||
RepeatSubstitute,
|
||||
RepeatGlobal,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::str::Chars;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::bitflags;
|
||||
use crate::expand::{Expander, expand_raw};
|
||||
use crate::libsh::error::{ShErr, ShErrKind, ShResult};
|
||||
use crate::readline::history::History;
|
||||
use crate::readline::keys::KeyEvent;
|
||||
@@ -14,7 +15,7 @@ use crate::readline::vicmd::{
|
||||
WriteDest,
|
||||
};
|
||||
use crate::readline::vimode::{ModeReport, ViInsert, ViMode};
|
||||
use crate::state::write_meta;
|
||||
use crate::state::{get_home, write_meta};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
|
||||
@@ -34,7 +35,7 @@ bitflags! {
|
||||
struct ExEditor {
|
||||
buf: LineBuf,
|
||||
mode: ViInsert,
|
||||
history: History
|
||||
history: History,
|
||||
}
|
||||
|
||||
impl ExEditor {
|
||||
@@ -90,7 +91,7 @@ impl ExEditor {
|
||||
if self.should_grab_history(&cmd) {
|
||||
log::debug!("Grabbing history for cmd: {:?}", cmd);
|
||||
self.scroll_history(cmd);
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
self.buf.exec_cmd(cmd)
|
||||
}
|
||||
@@ -103,7 +104,9 @@ pub struct ViEx {
|
||||
|
||||
impl ViEx {
|
||||
pub fn new(history: History) -> Self {
|
||||
Self { pending_cmd: ExEditor::new(history) }
|
||||
Self {
|
||||
pending_cmd: ExEditor::new(history),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +118,7 @@ impl ViMode for ViEx {
|
||||
E(C::Char('\r'), M::NONE) | E(C::Enter, M::NONE) => {
|
||||
let input = self.pending_cmd.buf.as_str();
|
||||
match parse_ex_cmd(input) {
|
||||
Ok(cmd) => {
|
||||
Ok(cmd)
|
||||
}
|
||||
Ok(cmd) => Ok(cmd),
|
||||
Err(e) => {
|
||||
let msg = e.unwrap_or(format!("Not an editor command: {}", input));
|
||||
write_meta(|m| m.post_system_message(msg.clone()));
|
||||
@@ -129,18 +130,14 @@ impl ViMode for ViEx {
|
||||
self.pending_cmd.clear();
|
||||
Ok(None)
|
||||
}
|
||||
E(C::Esc, M::NONE) => {
|
||||
Ok(Some(ViCmd {
|
||||
E(C::Esc, M::NONE) => Ok(Some(ViCmd {
|
||||
register: RegisterName::default(),
|
||||
verb: Some(VerbCmd(1, Verb::NormalMode)),
|
||||
motion: None,
|
||||
flags: CmdFlags::empty(),
|
||||
raw_seq: "".into(),
|
||||
}))
|
||||
}
|
||||
_ => {
|
||||
self.pending_cmd.handle_key(key).map(|_| None)
|
||||
}
|
||||
})),
|
||||
_ => self.pending_cmd.handle_key(key).map(|_| None),
|
||||
}
|
||||
}
|
||||
fn handle_key(&mut self, key: KeyEvent) -> Option<ViCmd> {
|
||||
@@ -248,7 +245,7 @@ fn parse_ex_command(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Opt
|
||||
let mut cmd_name = String::new();
|
||||
|
||||
while let Some(ch) = chars.peek() {
|
||||
if ch == &'!' {
|
||||
if cmd_name.is_empty() && ch == &'!' {
|
||||
cmd_name.push(*ch);
|
||||
chars.next();
|
||||
break;
|
||||
@@ -275,6 +272,7 @@ fn parse_ex_command(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Opt
|
||||
_ if "put".starts_with(&cmd_name) => Ok(Some(Verb::Put(Anchor::After))),
|
||||
_ if "read".starts_with(&cmd_name) => parse_read(chars),
|
||||
_ if "write".starts_with(&cmd_name) => parse_write(chars),
|
||||
_ if "edit".starts_with(&cmd_name) => parse_edit(chars),
|
||||
_ if "substitute".starts_with(&cmd_name) => parse_substitute(chars),
|
||||
_ => Err(None),
|
||||
}
|
||||
@@ -289,6 +287,19 @@ fn parse_normal(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<
|
||||
Ok(Some(Verb::Normal(seq)))
|
||||
}
|
||||
|
||||
fn parse_edit(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<String>> {
|
||||
chars
|
||||
.peeking_take_while(|c| c.is_whitespace())
|
||||
.for_each(drop);
|
||||
|
||||
let arg: String = chars.collect();
|
||||
if arg.trim().is_empty() {
|
||||
return Err(Some("Expected file path after ':edit'".into()));
|
||||
}
|
||||
let arg_path = get_path(arg.trim())?;
|
||||
Ok(Some(Verb::Edit(arg_path)))
|
||||
}
|
||||
|
||||
fn parse_read(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<String>> {
|
||||
chars
|
||||
.peeking_take_while(|c| c.is_whitespace())
|
||||
@@ -311,23 +322,15 @@ fn parse_read(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<St
|
||||
if is_shell_read {
|
||||
Ok(Some(Verb::Read(ReadSrc::Cmd(arg))))
|
||||
} else {
|
||||
let arg_path = get_path(arg.trim());
|
||||
let arg_path = get_path(arg.trim())?;
|
||||
Ok(Some(Verb::Read(ReadSrc::File(arg_path))))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path(path: &str) -> PathBuf {
|
||||
if let Some(stripped) = path.strip_prefix("~/")
|
||||
&& let Some(home) = std::env::var_os("HOME")
|
||||
{
|
||||
return PathBuf::from(home).join(stripped);
|
||||
}
|
||||
if path == "~"
|
||||
&& let Some(home) = std::env::var_os("HOME")
|
||||
{
|
||||
return PathBuf::from(home);
|
||||
}
|
||||
PathBuf::from(path)
|
||||
fn get_path(path: &str) -> Result<PathBuf, Option<String>> {
|
||||
let expanded = expand_raw(&mut path.chars().peekable())
|
||||
.map_err(|e| Some(format!("Error expanding path: {}", e)))?;
|
||||
Ok(PathBuf::from(&expanded))
|
||||
}
|
||||
|
||||
fn parse_write(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<String>> {
|
||||
@@ -350,7 +353,7 @@ fn parse_write(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<S
|
||||
}
|
||||
|
||||
let arg: String = chars.collect();
|
||||
let arg_path = get_path(arg.trim());
|
||||
let arg_path = get_path(arg.trim())?;
|
||||
|
||||
let dest = if is_file_append {
|
||||
WriteDest::FileAppend(arg_path)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::{collections::VecDeque, sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use nix::sys::signal::{SaFlags, SigAction, sigaction};
|
||||
|
||||
@@ -8,7 +11,10 @@ use crate::{
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::execute::exec_input,
|
||||
prelude::*,
|
||||
state::{AutoCmd, AutoCmdKind, VarFlags, VarKind, read_jobs, read_logic, write_jobs, write_meta, write_vars},
|
||||
state::{
|
||||
AutoCmd, AutoCmdKind, VarFlags, VarKind, read_jobs, read_logic, write_jobs, write_meta,
|
||||
write_vars,
|
||||
},
|
||||
};
|
||||
|
||||
static SIGNALS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
32
src/state.rs
32
src/state.rs
@@ -1875,20 +1875,25 @@ pub fn set_status(code: i32) {
|
||||
pub fn source_runtime_file(name: &str, env_var_name: Option<&str>) -> ShResult<()> {
|
||||
let etc_path = PathBuf::from(format!("/etc/shed/{name}"));
|
||||
if etc_path.is_file()
|
||||
&& let Err(e) = source_file(etc_path) {
|
||||
&& let Err(e) = source_file(etc_path)
|
||||
{
|
||||
e.print_error();
|
||||
}
|
||||
|
||||
let path = if let Some(name) = env_var_name
|
||||
&& let Ok(path) = env::var(name) {
|
||||
&& let Ok(path) = env::var(name)
|
||||
{
|
||||
PathBuf::from(&path)
|
||||
} else if let Some(home) = get_home() {
|
||||
home.join(format!(".{name}"))
|
||||
} else {
|
||||
return Err(ShErr::simple(ShErrKind::InternalErr, "could not determine home path"));
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::InternalErr,
|
||||
"could not determine home path",
|
||||
));
|
||||
};
|
||||
if !path.is_file() {
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
source_file(path)
|
||||
}
|
||||
@@ -1921,7 +1926,11 @@ pub fn get_home_unchecked() -> PathBuf {
|
||||
home
|
||||
} else {
|
||||
let caller = std::panic::Location::caller();
|
||||
panic!("get_home_unchecked: could not determine home directory (called from {}:{})", caller.file(), caller.line())
|
||||
panic!(
|
||||
"get_home_unchecked: could not determine home directory (called from {}:{})",
|
||||
caller.file(),
|
||||
caller.line()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1931,14 +1940,19 @@ pub fn get_home_str_unchecked() -> String {
|
||||
home.to_string_lossy().to_string()
|
||||
} else {
|
||||
let caller = std::panic::Location::caller();
|
||||
panic!("get_home_str_unchecked: could not determine home directory (called from {}:{})", caller.file(), caller.line())
|
||||
panic!(
|
||||
"get_home_str_unchecked: could not determine home directory (called from {}:{})",
|
||||
caller.file(),
|
||||
caller.line()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_home() -> Option<PathBuf> {
|
||||
env::var("HOME").ok().map(PathBuf::from).or_else(|| {
|
||||
User::from_uid(getuid()).ok().flatten().map(|u| u.dir)
|
||||
})
|
||||
env::var("HOME")
|
||||
.ok()
|
||||
.map(PathBuf::from)
|
||||
.or_else(|| User::from_uid(getuid()).ok().flatten().map(|u| u.dir))
|
||||
}
|
||||
|
||||
pub fn get_home_str() -> Option<String> {
|
||||
|
||||
Reference in New Issue
Block a user