mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #1090 from shutefan/chmod-cleanup
chmod: avoid passing all args through function hierarchy
This commit is contained in:
commit
fc0e4cb98b
1 changed files with 262 additions and 240 deletions
|
@ -45,24 +45,7 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
|
||||||
|
|
||||||
// sanitize input for - at beginning (e.g. chmod -x testfile). Remove
|
// sanitize input for - at beginning (e.g. chmod -x testfile). Remove
|
||||||
// the option and save it for later, after parsing is finished.
|
// the option and save it for later, after parsing is finished.
|
||||||
let mut negative_option = None;
|
let negative_option = sanitize_input(&mut args);
|
||||||
for i in 0..args.len() {
|
|
||||||
if let Some(first) = args[i].chars().nth(0) {
|
|
||||||
if first != '-' {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(second) = args[i].chars().nth(1) {
|
|
||||||
match second {
|
|
||||||
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0' ... '7' => {
|
|
||||||
negative_option = Some(args.remove(i));
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut matches = opts.parse(args);
|
let mut matches = opts.parse(args);
|
||||||
if matches.free.is_empty() {
|
if matches.free.is_empty() {
|
||||||
|
@ -93,8 +76,16 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
match chmod(matches.free, changes, quiet, verbose, preserve_root,
|
let chmoder = Chmoder{
|
||||||
recursive, fmode, cmode.as_ref()) {
|
changes: changes,
|
||||||
|
quiet: quiet,
|
||||||
|
verbose: verbose,
|
||||||
|
preserve_root: preserve_root,
|
||||||
|
recursive: recursive,
|
||||||
|
fmode: fmode,
|
||||||
|
cmode: cmode,
|
||||||
|
};
|
||||||
|
match chmoder.chmod(matches.free) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => return e
|
Err(e) => return e
|
||||||
}
|
}
|
||||||
|
@ -103,7 +94,37 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve_root: bool, recursive: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
|
fn sanitize_input(args: &mut Vec<String>) -> Option<String> {
|
||||||
|
for i in 0..args.len() {
|
||||||
|
let first = args[i].chars().nth(0).unwrap();
|
||||||
|
if first != '-' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(second) = args[i].chars().nth(1) {
|
||||||
|
match second {
|
||||||
|
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0' ... '7' => {
|
||||||
|
return Some(args.remove(i));
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Chmoder {
|
||||||
|
changes: bool,
|
||||||
|
quiet: bool,
|
||||||
|
verbose: bool,
|
||||||
|
preserve_root: bool,
|
||||||
|
recursive: bool,
|
||||||
|
fmode: Option<u32>,
|
||||||
|
cmode: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chmoder {
|
||||||
|
|
||||||
|
fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
|
||||||
let mut r = Ok(());
|
let mut r = Ok(());
|
||||||
|
|
||||||
for filename in &files {
|
for filename in &files {
|
||||||
|
@ -111,8 +132,8 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
|
||||||
let file = Path::new(filename);
|
let file = Path::new(filename);
|
||||||
if file.exists() {
|
if file.exists() {
|
||||||
if file.is_dir() {
|
if file.is_dir() {
|
||||||
if !preserve_root || filename != "/" {
|
if !self.preserve_root || filename != "/" {
|
||||||
if recursive {
|
if self.recursive {
|
||||||
let walk_dir = match Walker::new(&file) {
|
let walk_dir = match Walker::new(&file) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(f) => {
|
Err(f) => {
|
||||||
|
@ -124,15 +145,14 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
|
||||||
// on Windows OsStrings cannot be built out of non-UTF-8 chars. One
|
// on Windows OsStrings cannot be built out of non-UTF-8 chars. One
|
||||||
// possible fix is to use CStrings rather than Strings in the args
|
// possible fix is to use CStrings rather than Strings in the args
|
||||||
// to chmod() and chmod_file().
|
// to chmod() and chmod_file().
|
||||||
r = chmod(walk_dir.filter_map(|x| match x {
|
r = self.chmod(walk_dir.filter_map(|x| match x {
|
||||||
Ok(o) => match o.path().into_os_string().to_str() {
|
Ok(o) => match o.path().into_os_string().to_str() {
|
||||||
Some(s) => Some(s.to_owned()),
|
Some(s) => Some(s.to_owned()),
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}).collect(),
|
}).collect()).and(r);
|
||||||
changes, quiet, verbose, preserve_root, recursive, fmode, cmode).and(r);
|
r = self.chmod_file(&file, filename).and(r);
|
||||||
r = chmod_file(&file, filename, changes, quiet, verbose, fmode, cmode).and(r);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
show_error!("could not change permissions of directory '{}'",
|
show_error!("could not change permissions of directory '{}'",
|
||||||
|
@ -140,7 +160,7 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
|
||||||
r = Err(1);
|
r = Err(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r = chmod_file(&file, filename, changes, quiet, verbose, fmode, cmode).and(r);
|
r = self.chmod_file(&file, filename).and(r);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
show_error!("no such file or directory '{}'", filename);
|
show_error!("no such file or directory '{}'", filename);
|
||||||
|
@ -152,41 +172,42 @@ fn chmod(files: Vec<String>, changes: bool, quiet: bool, verbose: bool, preserve
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
|
fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
|
||||||
// chmod is useless on Windows
|
// chmod is useless on Windows
|
||||||
// it doesn't set any permissions at all
|
// it doesn't set any permissions at all
|
||||||
// instead it just sets the readonly attribute on the file
|
// instead it just sets the readonly attribute on the file
|
||||||
Err(0)
|
Err(0)
|
||||||
}
|
}
|
||||||
#[cfg(any(unix, target_os = "redox"))]
|
#[cfg(any(unix, target_os = "redox"))]
|
||||||
fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option<u32>, cmode: Option<&String>) -> Result<(), i32> {
|
fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
|
||||||
let mut fperm = match fs::metadata(name) {
|
let mut fperm = match fs::metadata(name) {
|
||||||
Ok(meta) => meta.mode() & 0o7777,
|
Ok(meta) => meta.mode() & 0o7777,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !quiet {
|
if !self.quiet {
|
||||||
show_error!("{}", err);
|
show_error!("{}", err);
|
||||||
}
|
}
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match fmode {
|
match self.fmode {
|
||||||
Some(mode) => try!(change_file(fperm, mode, file, name, verbose, changes, quiet)),
|
Some(mode) => try!(self.change_file(fperm, mode, file, name)),
|
||||||
None => {
|
None => {
|
||||||
for mode in cmode.unwrap().split(',') { // cmode is guaranteed to be Some in this case
|
let cmode_unwrapped = self.cmode.clone().unwrap();
|
||||||
|
for mode in cmode_unwrapped.split(',') { // cmode is guaranteed to be Some in this case
|
||||||
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||||
let result =
|
let result =
|
||||||
if mode.contains(arr) {
|
if mode.contains(arr) {
|
||||||
parse_numeric(fperm, mode)
|
self.parse_numeric(fperm, mode)
|
||||||
} else {
|
} else {
|
||||||
parse_symbolic(fperm, mode, file)
|
self.parse_symbolic(fperm, mode, file)
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
Ok(mode) => {
|
Ok(mode) => {
|
||||||
try!(change_file(fperm, mode, file, name, verbose, changes, quiet));
|
try!(self.change_file(fperm, mode, file, name));
|
||||||
fperm = mode;
|
fperm = mode;
|
||||||
}
|
}
|
||||||
Err(f) => {
|
Err(f) => {
|
||||||
if !quiet {
|
if !self.quiet {
|
||||||
show_error!("{}", f);
|
show_error!("{}", f);
|
||||||
}
|
}
|
||||||
return Err(1);
|
return Err(1);
|
||||||
|
@ -199,8 +220,8 @@ fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
|
fn parse_numeric(&self, fperm: u32, mut mode: &str) -> Result<u32, String> {
|
||||||
let (op, pos) = try!(parse_op(mode, Some('=')));
|
let (op, pos) = try!(self.parse_op(mode, Some('=')));
|
||||||
mode = mode[pos..].trim_left_matches('0');
|
mode = mode[pos..].trim_left_matches('0');
|
||||||
if mode.len() > 4 {
|
if mode.len() > 4 {
|
||||||
Err(format!("mode is too large ({} > 7777)", mode))
|
Err(format!("mode is too large ({} > 7777)", mode))
|
||||||
|
@ -219,7 +240,7 @@ fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, String> {
|
fn parse_symbolic(&self, mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, String> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc::umask;
|
use libc::umask;
|
||||||
|
|
||||||
|
@ -229,7 +250,7 @@ fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, St
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mask, pos) = parse_levels(mode);
|
let (mask, pos) = self.parse_levels(mode);
|
||||||
if pos == mode.len() {
|
if pos == mode.len() {
|
||||||
return Err(format!("invalid mode ({})", mode));
|
return Err(format!("invalid mode ({})", mode));
|
||||||
}
|
}
|
||||||
|
@ -239,9 +260,9 @@ fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, St
|
||||||
};
|
};
|
||||||
mode = &mode[pos..];
|
mode = &mode[pos..];
|
||||||
while mode.len() > 0 {
|
while mode.len() > 0 {
|
||||||
let (op, pos) = try!(parse_op(mode, None));
|
let (op, pos) = try!(self.parse_op(mode, None));
|
||||||
mode = &mode[pos..];
|
mode = &mode[pos..];
|
||||||
let (mut srwx, pos) = parse_change(mode, fperm, file);
|
let (mut srwx, pos) = self.parse_change(mode, fperm, file);
|
||||||
if respect_umask {
|
if respect_umask {
|
||||||
srwx &= !(last_umask as u32);
|
srwx &= !(last_umask as u32);
|
||||||
}
|
}
|
||||||
|
@ -259,7 +280,7 @@ fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result<u32, St
|
||||||
Ok(fperm)
|
Ok(fperm)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_levels(mode: &str) -> (u32, usize) {
|
fn parse_levels(&self, mode: &str) -> (u32, usize) {
|
||||||
let mut mask = 0;
|
let mut mask = 0;
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
for ch in mode.chars() {
|
for ch in mode.chars() {
|
||||||
|
@ -278,7 +299,7 @@ fn parse_levels(mode: &str) -> (u32, usize) {
|
||||||
(mask, pos)
|
(mask, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
|
fn parse_op(&self, mode: &str, default: Option<char>) -> Result<(char, usize), String> {
|
||||||
match mode.chars().next() {
|
match mode.chars().next() {
|
||||||
Some(ch) => match ch {
|
Some(ch) => match ch {
|
||||||
'+' | '-' | '=' => Ok((ch, 1)),
|
'+' | '-' | '=' => Ok((ch, 1)),
|
||||||
|
@ -291,7 +312,7 @@ fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_change(mode: &str, fperm: u32, file: &Path) -> (u32, usize) {
|
fn parse_change(&self, mode: &str, fperm: u32, file: &Path) -> (u32, usize) {
|
||||||
let mut srwx = fperm & 0o7000;
|
let mut srwx = fperm & 0o7000;
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
for ch in mode.chars() {
|
for ch in mode.chars() {
|
||||||
|
@ -319,24 +340,25 @@ fn parse_change(mode: &str, fperm: u32, file: &Path) -> (u32, usize) {
|
||||||
(srwx, pos)
|
(srwx, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_file(fperm: u32, mode: u32, file: &Path, path: &str, verbose: bool, changes: bool, quiet: bool) -> Result<(), i32> {
|
fn change_file(&self, fperm: u32, mode: u32, file: &Path, path: &str) -> Result<(), i32> {
|
||||||
if fperm == mode {
|
if fperm == mode {
|
||||||
if verbose && !changes {
|
if self.verbose && !self.changes {
|
||||||
show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
|
show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) {
|
} else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) {
|
||||||
if !quiet {
|
if !self.quiet {
|
||||||
show_error!("{}", err);
|
show_error!("{}", err);
|
||||||
}
|
}
|
||||||
if verbose {
|
if self.verbose {
|
||||||
show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode);
|
show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode);
|
||||||
}
|
}
|
||||||
Err(1)
|
Err(1)
|
||||||
} else {
|
} else {
|
||||||
if verbose || changes {
|
if self.verbose || self.changes {
|
||||||
show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode);
|
show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue