diff --git a/src/uu/chmod/locales/en-US.ftl b/src/uu/chmod/locales/en-US.ftl index 65b93775000..c69db78136e 100644 --- a/src/uu/chmod/locales/en-US.ftl +++ b/src/uu/chmod/locales/en-US.ftl @@ -28,7 +28,6 @@ chmod-help-recursive = change files and directories recursively chmod-help-reference = use RFILE's mode instead of MODE values # Verbose messages -chmod-verbose-failed-dangling = failed to change mode of {$file} from 0000 (---------) to 1500 (r-x-----T) chmod-verbose-neither-changed = neither symbolic link {$file} nor referent has been changed chmod-verbose-mode-retained = mode of {$file} retained as {$mode_octal} ({$mode_display}) chmod-verbose-failed-change = failed to change mode of file {$file} from {$old_mode} ({$old_mode_display}) to {$new_mode} ({$new_mode_display}) diff --git a/src/uu/chmod/locales/fr-FR.ftl b/src/uu/chmod/locales/fr-FR.ftl index feb5f47619c..005fb6a6195 100644 --- a/src/uu/chmod/locales/fr-FR.ftl +++ b/src/uu/chmod/locales/fr-FR.ftl @@ -30,7 +30,6 @@ chmod-error-missing-operand = opérande manquant chmod-error-reference-stat = impossible d'obtenir les attributs de { $file } : { $error } # Messages verbeux/de statut -chmod-verbose-failed-dangling = échec du changement de mode de {$file} de 0000 (---------) vers 1500 (r-x-----T) chmod-verbose-neither-changed = ni le lien symbolique {$file} ni la référence n'ont été changés chmod-verbose-mode-retained = mode de {$file} conservé comme {$mode_octal} ({$mode_display}) chmod-verbose-failed-change = échec du changement de mode du fichier {$file} de {$old_mode} ({$old_mode_display}) vers {$new_mode} ({$new_mode_display}) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 98abdfae2e2..b4dded2fa21 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -440,6 +440,12 @@ impl Chmoder { /// subtree was left alone. fn report_unreadable_dir(&self, path: &Path, err: std::io::Error) { self.report_failure(ChmodError::CannotReadDirectory(path.into(), err).into()); + self.report_not_accessed(path); + } + + /// With --verbose, GNU follows a failure to stat or read FILE with + /// "'FILE' could not be accessed" on stdout, even with --quiet. + fn report_not_accessed(&self, path: &Path) { if self.verbose { println!( "{}", @@ -487,16 +493,10 @@ impl Chmoder { show!(ChmodError::DanglingSymlink(filename.into())); set_exit_code(1); } - - if self.verbose { - println!( - "{}", - translate!("chmod-verbose-failed-dangling", "file" => filename.quote()) - ); - } } else if !self.quiet { show!(ChmodError::NoSuchFile(filename.into())); } + self.report_not_accessed(file); // GNU exits with exit code 1 even if -q or --quiet are passed // So we set the exit code, because it hasn't been set yet if `self.quiet` is true. set_exit_code(1); @@ -506,6 +506,7 @@ impl Chmoder { if !self.quiet { show!(ChmodError::PermissionDenied(filename.into())); } + self.report_not_accessed(file); set_exit_code(1); continue; } @@ -854,10 +855,13 @@ impl Chmoder { Self::print_neither_changed(file.into())?; } Ok(()) // Skip dangling symlinks - } else if err.kind() == std::io::ErrorKind::PermissionDenied { - Err(ChmodError::PermissionDenied(file.into()).into()) } else { - Err(ChmodError::CannotStat(file.into(), err).into()) + self.report_not_accessed(file); + if err.kind() == std::io::ErrorKind::PermissionDenied { + Err(ChmodError::PermissionDenied(file.into()).into()) + } else { + Err(ChmodError::CannotStat(file.into(), err).into()) + } }; } }; diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 9e8aa224809..adc14193c5e 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -784,9 +784,7 @@ fn test_chmod_symlink_non_existing_file() { let non_existing = "test_chmod_symlink_non_existing_file"; let test_symlink = "test_chmod_symlink_non_existing_file_symlink"; - let expected_stdout = &format!( - "failed to change mode of '{test_symlink}' from 0000 (---------) to 1500 (r-x-----T)" - ); + let expected_stdout = &format!("'{test_symlink}' could not be accessed"); let expected_stderr = &format!("cannot operate on dangling symlink '{test_symlink}'"); at.symlink_file(non_existing, test_symlink); @@ -963,9 +961,7 @@ fn test_chmod_file_symlink_after_non_existing_file() { let non_existing = "test_chmod_symlink_non_existing_file"; let test_dangling_symlink = "test_chmod_symlink_non_existing_file_symlink"; - let expected_stdout = &format!( - "failed to change mode of '{test_dangling_symlink}' from 0000 (---------) to 1500 (r-x-----T)" - ); + let expected_stdout = &format!("'{test_dangling_symlink}' could not be accessed"); let expected_stderr = &format!("cannot operate on dangling symlink '{test_dangling_symlink}'"); at.touch(existing); @@ -2068,3 +2064,39 @@ fn test_overlong_path_reports_os_error_and_continues() { "chmod: failed to get attributes of '{path}': {error}\n" )); } + +#[test] +fn test_chmod_verbose_reports_inaccessible_operand() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("f"); + at.set_mode("f", 0o644); + ucmd.args(&["-v", "644", "missing", "f/", "f"]) + .fails_with_code(1) + .stdout_is( + "'missing' could not be accessed\n\ + 'f/' could not be accessed\n\ + mode of 'f' retained as 0644 (rw-r--r--)\n", + ) + .stderr_is( + "chmod: cannot access 'missing': No such file or directory\n\ + chmod: cannot access 'f/': Not a directory\n", + ); +} + +#[test] +fn test_chmod_verbose_quiet_still_reports_inaccessible_operand() { + new_ucmd!() + .args(&["-v", "-f", "644", "missing"]) + .fails_with_code(1) + .stdout_is("'missing' could not be accessed\n") + .no_stderr(); +} + +#[test] +fn test_chmod_changes_does_not_report_inaccessible_operand() { + new_ucmd!() + .args(&["-c", "644", "missing"]) + .fails_with_code(1) + .no_stdout() + .stderr_is("chmod: cannot access 'missing': No such file or directory\n"); +}