From 76cf0e47ed02ff515e0766753231689063ad7bee Mon Sep 17 00:00:00 2001 From: Arcterus Date: Thu, 19 Jun 2014 13:05:43 -0700 Subject: [PATCH 1/3] echo: handle -- correctly --- echo/echo.rs | 91 +++++++++++++++++++++++++++++------------------- uutils/uutils.rs | 22 ++++++------ 2 files changed, 67 insertions(+), 46 deletions(-) diff --git a/echo/echo.rs b/echo/echo.rs index a32008127..febdc77d3 100644 --- a/echo/echo.rs +++ b/echo/echo.rs @@ -20,9 +20,15 @@ use std::uint; #[path = "../common/util.rs"] mod util; +#[allow(dead_code)] static NAME: &'static str = "echo"; static VERSION: &'static str = "1.0.0"; +struct EchoOptions { + newline: bool, + escape: bool +} + fn print_char(c: char) { print!("{}", c); } @@ -70,33 +76,27 @@ fn convert_str(string: &str, index: uint, base: uint) -> (char, int) { (to_char(&bytes, base), max_digits) } -#[allow(dead_code)] -fn main() { os::set_exit_status(uumain(os::args())); } - -pub fn uumain(args: Vec) -> int { +fn parse_options(args: Vec, options: &mut EchoOptions) -> Option> { + let mut echo_args = vec!(); let program = args.get(0).clone(); - let opts = [ - getopts::optflag("n", "", "do not output the trailing newline"), - getopts::optflag("e", "", "enable interpretation of backslash escapes"), - getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"), - getopts::optflag("h", "help", "display this help and exit"), - getopts::optflag("V", "version", "output version information and exit"), - ]; - - let matches = match getopts::getopts(args.tail(), opts) { - Ok(m) => m, - Err(f) => crash!(1, "Invalid options\n{}", f) - }; - - if matches.opt_present("help") { - println!("echo {:s} - display a line of text", VERSION); - println!(""); - println!("Usage:"); - println!(" {0:s} [SHORT-OPTION]... [STRING]...", program); - println!(" {0:s} LONG-OPTION", program); - println!(""); - println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice()); - println("If -e is in effect, the following sequences are recognized: + for arg in args.move_iter().skip(1) { + match arg.as_slice() { + "--help" | "-h" => { + let opts = [ + getopts::optflag("n", "", "do not output the trailing newline"), + getopts::optflag("e", "", "enable interpretation of backslash escapes"), + getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"), + getopts::optflag("h", "help", "display this help and exit"), + getopts::optflag("V", "version", "output version information and exit"), + ]; + println!("echo {:s} - display a line of text", VERSION); + println!(""); + println!("Usage:"); + println!(" {0:s} [SHORT-OPTION]... [STRING]...", program); + println!(" {0:s} LONG-OPTION", program); + println!(""); + println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice()); + println("If -e is in effect, the following sequences are recognized: \\\\ backslash \\a alert (BEL) @@ -110,17 +110,38 @@ pub fn uumain(args: Vec) -> int { \\v vertical tab \\0NNN byte with octal value NNN (1 to 3 digits) \\xHH byte with hexadecimal value HH (1 to 2 digits)"); - return 0; + return None; + } + "--version" | "-V" => { + println!("echo version: {:s}", VERSION); + return None; + } + "-n" => options.newline = true, + "-e" => options.escape = true, + "-E" => options.escape = false, + _ => echo_args.push(arg) + } } + Some(echo_args) +} - if matches.opt_present("version") { - println!("echo version: {:s}", VERSION); - return 0; - } +#[allow(dead_code)] +fn main() { os::set_exit_status(uumain(os::args())); } - if !matches.free.is_empty() { - let string = matches.free.connect(" "); - if matches.opt_present("e") { +pub fn uumain(args: Vec) -> int { + let mut options = EchoOptions { + newline: false, + escape: false + }; + + let free = match parse_options(args, &mut options) { + Some(vec) => vec, + None => return 0 + }; + + if !free.is_empty() { + let string = free.connect(" "); + if options.escape { let mut prev_was_slash = false; let mut iter = string.as_slice().chars().enumerate(); loop { @@ -184,7 +205,7 @@ pub fn uumain(args: Vec) -> int { } } - if !matches.opt_present("n") { + if !options.newline { println!("") } diff --git a/uutils/uutils.rs b/uutils/uutils.rs index 2d8c8f46c..8b83f16ce 100644 --- a/uutils/uutils.rs +++ b/uutils/uutils.rs @@ -117,17 +117,17 @@ fn util_map() -> HashMap<&str, fn(Vec) -> int> { } fn usage(cmap: &HashMap<&str, fn(Vec) -> int>) { - println!("{} {}", NAME, VERSION); - println!(""); - println!("Usage:"); - println!(" {} [util [arguments...]", NAME); - println!("Currently defined functions:"); - let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect(); - utils.sort(); - for util in utils.iter() { - println!("\t{}", util); - } - println!(""); + println!("{} {}", NAME, VERSION); + println!(""); + println!("Usage:"); + println!(" {} [util [arguments...]", NAME); + println!("Currently defined functions:"); + let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect(); + utils.sort(); + for util in utils.iter() { + println!("\t{}", util); + } + println!(""); } fn main() { From df6f1ba19ccb836939b4dae65b62254702ca345d Mon Sep 17 00:00:00 2001 From: Arcterus Date: Thu, 19 Jun 2014 16:26:49 -0700 Subject: [PATCH 2/3] echo: fix handling of \0 --- echo/echo.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/echo/echo.rs b/echo/echo.rs index febdc77d3..bddbf4e47 100644 --- a/echo/echo.rs +++ b/echo/echo.rs @@ -61,19 +61,22 @@ fn convert_str(string: &str, index: uint, base: uint) -> (char, int) { let mut bytes = vec!(); for offset in range(0, max_digits) { + if string.len() <= index + offset as uint { + break; + } let c = string[index + offset as uint]; if is_legal_digit(c) { bytes.push(c as u8); } else { - if bytes.len() > 0 { - return (to_char(&bytes, base), offset); - } else { - return (' ', offset); - } + break; } } - (to_char(&bytes, base), max_digits) + if bytes.len() == 0 { + (' ', 0) + } else { + (to_char(&bytes, base), bytes.len() as int) + } } fn parse_options(args: Vec, options: &mut EchoOptions) -> Option> { @@ -181,8 +184,7 @@ pub fn uumain(args: Vec) -> int { '0' => { let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 8u); if num_char_used == 0 { - print_char('\\'); - print_char('0'); + print_char('\0'); } else { print_char(c); for _ in range(0, num_char_used) { From af01114ba856e6ad0c254cf7cdcfaa9d71fca67c Mon Sep 17 00:00:00 2001 From: Arcterus Date: Thu, 19 Jun 2014 17:38:26 -0700 Subject: [PATCH 3/3] echo: pass Busybox test suite --- echo/echo.rs | 145 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 52 deletions(-) diff --git a/echo/echo.rs b/echo/echo.rs index bddbf4e47..aa815ec35 100644 --- a/echo/echo.rs +++ b/echo/echo.rs @@ -24,19 +24,18 @@ mod util; static NAME: &'static str = "echo"; static VERSION: &'static str = "1.0.0"; +#[deriving(Clone)] struct EchoOptions { newline: bool, escape: bool } -fn print_char(c: char) { - print!("{}", c); -} - +#[inline(always)] fn to_char(bytes: &Vec, base: uint) -> char { uint::parse_bytes(bytes.as_slice(), base).unwrap() as u8 as char } +#[inline(always)] fn isxdigit(c: u8) -> bool { match c as char { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | @@ -45,6 +44,7 @@ fn isxdigit(c: u8) -> bool { } } +#[inline(always)] fn isodigit(c: u8) -> bool { match c as char { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true, @@ -52,7 +52,7 @@ fn isodigit(c: u8) -> bool { } } -fn convert_str(string: &str, index: uint, base: uint) -> (char, int) { +fn convert_str(string: &str, index: uint, base: uint) -> (char, uint) { let (max_digits, is_legal_digit) = match base { 8u => (3, isodigit), 16u => (2, isxdigit), @@ -75,31 +75,75 @@ fn convert_str(string: &str, index: uint, base: uint) -> (char, int) { if bytes.len() == 0 { (' ', 0) } else { - (to_char(&bytes, base), bytes.len() as int) + (to_char(&bytes, base), bytes.len()) } } fn parse_options(args: Vec, options: &mut EchoOptions) -> Option> { let mut echo_args = vec!(); let program = args.get(0).clone(); - for arg in args.move_iter().skip(1) { + 'argloop: for arg in args.move_iter().skip(1) { match arg.as_slice() { "--help" | "-h" => { - let opts = [ - getopts::optflag("n", "", "do not output the trailing newline"), - getopts::optflag("e", "", "enable interpretation of backslash escapes"), - getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"), - getopts::optflag("h", "help", "display this help and exit"), - getopts::optflag("V", "version", "output version information and exit"), - ]; - println!("echo {:s} - display a line of text", VERSION); - println!(""); - println!("Usage:"); - println!(" {0:s} [SHORT-OPTION]... [STRING]...", program); - println!(" {0:s} LONG-OPTION", program); - println!(""); - println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice()); - println("If -e is in effect, the following sequences are recognized: + print_help(&program); + return None; + } + "--version" | "-V" => { + print_version(); + return None; + } + "-n" => options.newline = true, + "-e" => options.escape = true, + "-E" => options.escape = false, + _ => { + if arg.as_slice().char_at(0) == '-' && arg.len() > 1 { + let mut newopts = options.clone(); + let argptr: *String = &arg; // escape from the borrow checker + for ch in unsafe { (*argptr).as_slice() }.chars().skip(1) { + match ch { + 'h' => { + print_help(&program); + return None; + } + 'V' => { + print_version(); + return None; + } + 'n' => newopts.newline = true, + 'e' => newopts.escape = true, + 'E' => newopts.escape = false, + _ => { + echo_args.push(arg); + continue 'argloop; + } + } + } + *options = newopts; + } else { + echo_args.push(arg); + } + } + } + } + Some(echo_args) +} + +fn print_help(program: &String) { + let opts = [ + getopts::optflag("n", "", "do not output the trailing newline"), + getopts::optflag("e", "", "enable interpretation of backslash escapes"), + getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"), + getopts::optflag("h", "help", "display this help and exit"), + getopts::optflag("V", "version", "output version information and exit"), + ]; + println!("echo {:s} - display a line of text", VERSION); + println!(""); + println!("Usage:"); + println!(" {0:s} [SHORT-OPTION]... [STRING]...", *program); + println!(" {0:s} LONG-OPTION", *program); + println!(""); + println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice()); + println("If -e is in effect, the following sequences are recognized: \\\\ backslash \\a alert (BEL) @@ -113,19 +157,10 @@ fn parse_options(args: Vec, options: &mut EchoOptions) -> Option { - println!("echo version: {:s}", VERSION); - return None; - } - "-n" => options.newline = true, - "-e" => options.escape = true, - "-E" => options.escape = false, - _ => echo_args.push(arg) - } - } - Some(echo_args) +} + +fn print_version() { + println!("echo version: {:s}", VERSION); } #[allow(dead_code)] @@ -152,30 +187,29 @@ pub fn uumain(args: Vec) -> int { Some((index, c)) => { if !prev_was_slash { if c != '\\' { - print_char(c); + print!("{}", c); } else { prev_was_slash = true; } } else { prev_was_slash = false; match c { - '\\' => print_char('\\'), - 'a' => print_char('\x07'), - 'b' => print_char('\x08'), + '\\' => print!("\\"), + 'a' => print!("\x07"), + 'b' => print!("\x08"), 'c' => break, - 'e' => print_char('\x1B'), - 'f' => print_char('\x0C'), - 'n' => print_char('\n'), - 'r' => print_char('\r'), - 't' => print_char('\t'), - 'v' => print_char('\x0B'), + 'e' => print!("\x1B"), + 'f' => print!("\x0C"), + 'n' => print!("\n"), + 'r' => print!("\r"), + 't' => print!("\t"), + 'v' => print!("\x0B"), 'x' => { let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 16u); if num_char_used == 0 { - print_char('\\'); - print_char('x'); + print!("\\x"); } else { - print_char(c); + print!("{}", c); for _ in range(0, num_char_used) { iter.next(); // consume used characters } @@ -184,17 +218,24 @@ pub fn uumain(args: Vec) -> int { '0' => { let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 8u); if num_char_used == 0 { - print_char('\0'); + print!("\0"); } else { - print_char(c); + print!("{}", c); for _ in range(0, num_char_used) { iter.next(); // consume used characters } } } _ => { - print_char('\\'); - print_char(c); + let (esc_c, num_char_used) = convert_str(string.as_slice(), index, 8u); + if num_char_used == 0 { + print!("\\{}", c); + } else { + print!("{}", esc_c); + for _ in range(1, num_char_used) { + iter.next(); // consume used characters + } + } } } }