From 2686ea75d70e08ad9d04a82ac5cb90a5bfa7f85a Mon Sep 17 00:00:00 2001 From: Luca Ottaviano Date: Tue, 15 Mar 2016 14:18:39 +0100 Subject: [PATCH] chmod: handle -octal and -[rwx] The main issue is that -octal or -[rwx] is interpreted as an option by getopts. Search the args for such a pattern, remove it before parsing and manually handle it afterwards. Fixes #788. --- src/chmod/chmod.rs | 32 +++++++++++++++++++++++++++++--- tests/chmod.rs | 10 ++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/chmod/chmod.rs b/src/chmod/chmod.rs index 105021fea..651cb2f75 100644 --- a/src/chmod/chmod.rs +++ b/src/chmod/chmod.rs @@ -29,7 +29,7 @@ use walker::Walker; const NAME: &'static str = "chmod"; const VERSION: &'static str = env!("CARGO_PKG_VERSION"); -pub fn uumain(args: Vec) -> i32 { +pub fn uumain(mut args: Vec) -> i32 { let mut opts = Options::new(); opts.optflag("c", "changes", "like verbose but report only when a change is made (unimplemented)"); opts.optflag("f", "quiet", "suppress most error messages (unimplemented)"); // TODO: support --silent @@ -40,7 +40,27 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("R", "recursive", "change files and directories recursively"); opts.optflag("h", "help", "display this help and exit"); opts.optflag("V", "version", "output version information and exit"); - // TODO: sanitize input for - at beginning (e.g. chmod -x testfile). Solution is to add a to -x, making a-x + // sanitize input for - at beginning (e.g. chmod -x testfile). Remove + // the option and save it for later, after parsing is finished. + let mut negative_option = None; + 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 = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { crash!(1, "{}", f) } @@ -86,7 +106,13 @@ Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.", }); let cmode = if fmode.is_none() { - Some(matches.free.remove(0)) + // If there was a negative option, now it's a good time to + // use it. + if negative_option.is_some() { + negative_option + } else { + Some(matches.free.remove(0)) + } } else { None }; diff --git a/tests/chmod.rs b/tests/chmod.rs index 5d4f893c0..f406c7727 100644 --- a/tests/chmod.rs +++ b/tests/chmod.rs @@ -59,10 +59,9 @@ fn test_chmod_octal() { TestCase{args: vec!{"0700", TEST_FILE}, before: 0o000, after: 0o700}, TestCase{args: vec!{"0070", TEST_FILE}, before: 0o000, after: 0o070}, TestCase{args: vec!{"0007", TEST_FILE}, before: 0o000, after: 0o007}, - // Known failues: #788 - // TestCase{args: vec!{"-0700", TEST_FILE}, before: 0o700, after: 0o000}, - // TestCase{args: vec!{"-0070", TEST_FILE}, before: 0o060, after: 0o000}, - // TestCase{args: vec!{"-0007", TEST_FILE}, before: 0o001, after: 0o000}, + TestCase{args: vec!{"-0700", TEST_FILE}, before: 0o700, after: 0o000}, + TestCase{args: vec!{"-0070", TEST_FILE}, before: 0o060, after: 0o000}, + TestCase{args: vec!{"-0007", TEST_FILE}, before: 0o001, after: 0o000}, TestCase{args: vec!{"+0100", TEST_FILE}, before: 0o600, after: 0o700}, TestCase{args: vec!{"+0020", TEST_FILE}, before: 0o050, after: 0o070}, TestCase{args: vec!{"+0004", TEST_FILE}, before: 0o003, after: 0o007}, @@ -77,6 +76,9 @@ fn test_chmod_ugoa() { TestCase{args: vec!{"g=rwx", TEST_FILE}, before: 0o000, after: 0o070}, TestCase{args: vec!{"o=rwx", TEST_FILE}, before: 0o000, after: 0o007}, TestCase{args: vec!{"a=rwx", TEST_FILE}, before: 0o000, after: 0o777}, + TestCase{args: vec!{"-r", TEST_FILE}, before: 0o777, after: 0o333}, + TestCase{args: vec!{"-w", TEST_FILE}, before: 0o777, after: 0o555}, + TestCase{args: vec!{"-x", TEST_FILE}, before: 0o777, after: 0o666}, }; run_tests(tests); }