Skip to content

Commit

Permalink
feat: implement cd - (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
39555 authored Oct 13, 2024
1 parent d6fa500 commit 2f02e01
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 11 deletions.
40 changes: 31 additions & 9 deletions brush-core/src/builtins/cd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use clap::Parser;

use crate::{builtins, commands};

/// Change the current working directory.
/// Change the current shell working directory.
#[derive(Parser)]
pub(crate) struct CdCommand {
/// Force following symlinks.
Expand All @@ -16,7 +16,7 @@ pub(crate) struct CdCommand {
#[arg(short = 'P')]
use_physical_dir: bool,

/// Exit if current working dir resolution fails.
/// Exit with non zero exit status if current working directory resolution fails.
#[arg(short = 'e')]
exit_on_failed_cwd_resolution: bool,

Expand All @@ -25,6 +25,8 @@ pub(crate) struct CdCommand {
#[arg(short = '@')]
file_with_xattr_as_dir: bool,

/// By default it is the value of the HOME shell variable. If `TARGET_DIR` is "-", it is
/// converted to $OLDPWD.
target_dir: Option<PathBuf>,
}

Expand All @@ -43,8 +45,22 @@ impl builtins::Command for CdCommand {
return crate::error::unimp("options to cd");
}

let mut should_print = false;
let target_dir = if let Some(target_dir) = &self.target_dir {
target_dir.clone()
// `cd -', equivalent to `cd $OLDPWD'
if target_dir.as_os_str() == "-" {
should_print = true;
if let Some(oldpwd) = context.shell.env.get_str("OLDPWD") {
PathBuf::from(oldpwd.to_string())
} else {
writeln!(context.stderr(), "OLDPWD not set")?;
return Ok(builtins::ExitCode::Custom(1));
}
} else {
// TODO: remove clone, and use temporary lifetime extension after rust 1.75
target_dir.clone()
}
// `cd' without arguments is equivalent to `cd $HOME'
} else {
if let Some(home_var) = context.shell.env.get_str("HOME") {
PathBuf::from(home_var.to_string())
Expand All @@ -54,12 +70,18 @@ impl builtins::Command for CdCommand {
}
};

match context.shell.set_working_dir(&target_dir) {
Ok(()) => {}
Err(e) => {
writeln!(context.stderr(), "cd: {e}")?;
return Ok(builtins::ExitCode::Custom(1));
}
if let Err(e) = context.shell.set_working_dir(&target_dir) {
writeln!(context.stderr(), "cd: {e}")?;
return Ok(builtins::ExitCode::Custom(1));
}

// Bash compatibility
// https://www.gnu.org/software/bash/manual/bash.html#index-cd
// If a non-empty directory name from CDPATH is used, or if '-' is the first argument, and
// the directory change is successful, the absolute pathname of the new working
// directory is written to the standard output.
if should_print {
writeln!(context.stdout(), "{}", target_dir.display())?;
}

Ok(builtins::ExitCode::Success)
Expand Down
14 changes: 12 additions & 2 deletions brush-core/src/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,8 +969,6 @@ impl Shell {

let pwd = cleaned_path.to_string_lossy().to_string();

// TODO: handle updating PWD
self.working_dir = cleaned_path;
self.env.update_or_add(
"PWD",
variables::ShellValueLiteral::Scalar(pwd),
Expand All @@ -981,6 +979,18 @@ impl Shell {
EnvironmentLookup::Anywhere,
EnvironmentScope::Global,
)?;
let oldpwd = std::mem::replace(&mut self.working_dir, cleaned_path);

self.env.update_or_add(
"OLDPWD",
variables::ShellValueLiteral::Scalar(oldpwd.to_string_lossy().to_string()),
|var| {
var.export();
Ok(())
},
EnvironmentLookup::Anywhere,
EnvironmentScope::Global,
)?;

Ok(())
}
Expand Down
13 changes: 13 additions & 0 deletions brush-shell/tests/cases/builtins/cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,16 @@ cases:
cd file
echo $?
ls -1
- name: "cd -"
ignore_stderr: true
stdin: |
cd /
echo "pwd: $PWD"
cd usr
echo "pwd: $PWD"
echo "oldpwd: $OLDPWD"
cd -
echo $?
echo "pwd: $PWD"

0 comments on commit 2f02e01

Please sign in to comment.