Skip to content

Commit 7a15896

Browse files
ChrisDrydensylvestre
authored andcommitted
mv: address review comments - reorder enum, cache mode, safer defaults
1 parent cb34601 commit 7a15896

File tree

1 file changed

+26
-21
lines changed

1 file changed

+26
-21
lines changed

src/uu/mv/src/mv.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ impl Default for Options {
130130
/// specifies behavior of the overwrite flag
131131
#[derive(Clone, Debug, Eq, PartialEq, Default)]
132132
pub enum OverwriteMode {
133+
/// No flag specified - prompt for unwriteable files when stdin is TTY
134+
#[default]
135+
Default,
133136
/// '-n' '--no-clobber' do not overwrite
134137
NoClobber,
135138
/// '-i' '--interactive' prompt before overwrite
136139
Interactive,
137140
///'-f' '--force' overwrite without prompt
138141
Force,
139-
/// No flag specified - prompt for unwriteable files when stdin is TTY
140-
#[default]
141-
Default,
142142
}
143143

144144
static OPT_FORCE: &str = "force";
@@ -427,11 +427,12 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
427427
} else if target.exists() && source_is_dir {
428428
match opts.overwrite {
429429
OverwriteMode::NoClobber => return Ok(()),
430-
OverwriteMode::Interactive => prompt_overwrite(target)?,
430+
OverwriteMode::Interactive => prompt_overwrite(target, None)?,
431431
OverwriteMode::Force => {}
432432
OverwriteMode::Default => {
433-
if std::io::stdin().is_terminal() && !is_writable(target) {
434-
prompt_overwrite(target)?;
433+
let (writable, mode) = is_writable(target);
434+
if !writable && std::io::stdin().is_terminal() {
435+
prompt_overwrite(target, mode)?;
435436
}
436437
}
437438
}
@@ -735,12 +736,13 @@ fn rename(
735736
}
736737
return Ok(());
737738
}
738-
OverwriteMode::Interactive => prompt_overwrite(to)?,
739+
OverwriteMode::Interactive => prompt_overwrite(to, None)?,
739740
OverwriteMode::Force => {}
740741
OverwriteMode::Default => {
741742
// GNU mv prompts when stdin is a TTY and target is not writable
742-
if std::io::stdin().is_terminal() && !is_writable(to) {
743-
prompt_overwrite(to)?;
743+
let (writable, mode) = is_writable(to);
744+
if !writable && std::io::stdin().is_terminal() {
745+
prompt_overwrite(to, mode)?;
744746
}
745747
}
746748
}
@@ -1204,31 +1206,34 @@ fn is_empty_dir(path: &Path) -> bool {
12041206
fs::read_dir(path).is_ok_and(|mut contents| contents.next().is_none())
12051207
}
12061208

1209+
/// Check if file is writable, returning the mode for potential reuse.
12071210
#[cfg(unix)]
1208-
fn is_writable(path: &Path) -> bool {
1211+
fn is_writable(path: &Path) -> (bool, Option<u32>) {
12091212
if let Ok(metadata) = path.metadata() {
12101213
let mode = metadata.permissions().mode();
12111214
// Check if user write bit is set
1212-
(mode & 0o200) != 0
1215+
((mode & 0o200) != 0, Some(mode))
12131216
} else {
1214-
true // If we can't get metadata, assume writable
1217+
(false, None) // If we can't get metadata, prompt user to be safe
12151218
}
12161219
}
12171220

1221+
/// Check if file is writable.
12181222
#[cfg(not(unix))]
1219-
fn is_writable(path: &Path) -> bool {
1223+
fn is_writable(path: &Path) -> (bool, Option<u32>) {
12201224
if let Ok(metadata) = path.metadata() {
1221-
!metadata.permissions().readonly()
1225+
(!metadata.permissions().readonly(), None)
12221226
} else {
1223-
true
1227+
(false, None) // If we can't get metadata, prompt user to be safe
12241228
}
12251229
}
12261230

12271231
#[cfg(unix)]
1228-
fn get_interactive_prompt(to: &Path) -> String {
1232+
fn get_interactive_prompt(to: &Path, cached_mode: Option<u32>) -> String {
12291233
use libc::mode_t;
1230-
if let Ok(metadata) = to.metadata() {
1231-
let mode = metadata.permissions().mode();
1234+
// Use cached mode if available, otherwise fetch it
1235+
let mode = cached_mode.or_else(|| to.metadata().ok().map(|m| m.permissions().mode()));
1236+
if let Some(mode) = mode {
12321237
let file_mode = mode & 0o777;
12331238
// Check if file is not writable by user
12341239
if (mode & 0o200) == 0 {
@@ -1241,13 +1246,13 @@ fn get_interactive_prompt(to: &Path) -> String {
12411246
}
12421247

12431248
#[cfg(not(unix))]
1244-
fn get_interactive_prompt(to: &Path) -> String {
1249+
fn get_interactive_prompt(to: &Path, _cached_mode: Option<u32>) -> String {
12451250
translate!("mv-prompt-overwrite", "target" => to.quote())
12461251
}
12471252

12481253
/// Prompts the user for confirmation and returns an error if declined.
1249-
fn prompt_overwrite(to: &Path) -> io::Result<()> {
1250-
if !prompt_yes!("{}", get_interactive_prompt(to)) {
1254+
fn prompt_overwrite(to: &Path, cached_mode: Option<u32>) -> io::Result<()> {
1255+
if !prompt_yes!("{}", get_interactive_prompt(to, cached_mode)) {
12511256
return Err(io::Error::other(""));
12521257
}
12531258
Ok(())

0 commit comments

Comments
 (0)