@@ -130,15 +130,15 @@ impl Default for Options {
130130/// specifies behavior of the overwrite flag
131131#[ derive( Clone , Debug , Eq , PartialEq , Default ) ]
132132pub 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
144144static 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