1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-01 13:37:48 +00:00

echo: pass Busybox test suite

This commit is contained in:
Arcterus 2014-06-19 17:38:26 -07:00
parent df6f1ba19c
commit af01114ba8

View file

@ -24,19 +24,18 @@ mod util;
static NAME: &'static str = "echo"; static NAME: &'static str = "echo";
static VERSION: &'static str = "1.0.0"; static VERSION: &'static str = "1.0.0";
#[deriving(Clone)]
struct EchoOptions { struct EchoOptions {
newline: bool, newline: bool,
escape: bool escape: bool
} }
fn print_char(c: char) { #[inline(always)]
print!("{}", c);
}
fn to_char(bytes: &Vec<u8>, base: uint) -> char { fn to_char(bytes: &Vec<u8>, base: uint) -> char {
uint::parse_bytes(bytes.as_slice(), base).unwrap() as u8 as char uint::parse_bytes(bytes.as_slice(), base).unwrap() as u8 as char
} }
#[inline(always)]
fn isxdigit(c: u8) -> bool { fn isxdigit(c: u8) -> bool {
match c as char { match c as char {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
@ -45,6 +44,7 @@ fn isxdigit(c: u8) -> bool {
} }
} }
#[inline(always)]
fn isodigit(c: u8) -> bool { fn isodigit(c: u8) -> bool {
match c as char { match c as char {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true, '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 { let (max_digits, is_legal_digit) = match base {
8u => (3, isodigit), 8u => (3, isodigit),
16u => (2, isxdigit), 16u => (2, isxdigit),
@ -75,31 +75,75 @@ fn convert_str(string: &str, index: uint, base: uint) -> (char, int) {
if bytes.len() == 0 { if bytes.len() == 0 {
(' ', 0) (' ', 0)
} else { } else {
(to_char(&bytes, base), bytes.len() as int) (to_char(&bytes, base), bytes.len())
} }
} }
fn parse_options(args: Vec<String>, options: &mut EchoOptions) -> Option<Vec<String>> { fn parse_options(args: Vec<String>, options: &mut EchoOptions) -> Option<Vec<String>> {
let mut echo_args = vec!(); let mut echo_args = vec!();
let program = args.get(0).clone(); 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() { match arg.as_slice() {
"--help" | "-h" => { "--help" | "-h" => {
let opts = [ print_help(&program);
getopts::optflag("n", "", "do not output the trailing newline"), return None;
getopts::optflag("e", "", "enable interpretation of backslash escapes"), }
getopts::optflag("E", "", "disable interpretation of backslash escapes (default)"), "--version" | "-V" => {
getopts::optflag("h", "help", "display this help and exit"), print_version();
getopts::optflag("V", "version", "output version information and exit"), return None;
]; }
println!("echo {:s} - display a line of text", VERSION); "-n" => options.newline = true,
println!(""); "-e" => options.escape = true,
println!("Usage:"); "-E" => options.escape = false,
println!(" {0:s} [SHORT-OPTION]... [STRING]...", program); _ => {
println!(" {0:s} LONG-OPTION", program); if arg.as_slice().char_at(0) == '-' && arg.len() > 1 {
println!(""); let mut newopts = options.clone();
println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice()); let argptr: *String = &arg; // escape from the borrow checker
println("If -e is in effect, the following sequences are recognized: 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 \\\\ backslash
\\a alert (BEL) \\a alert (BEL)
@ -113,19 +157,10 @@ fn parse_options(args: Vec<String>, options: &mut EchoOptions) -> Option<Vec<Str
\\v vertical tab \\v vertical tab
\\0NNN byte with octal value NNN (1 to 3 digits) \\0NNN byte with octal value NNN (1 to 3 digits)
\\xHH byte with hexadecimal value HH (1 to 2 digits)"); \\xHH byte with hexadecimal value HH (1 to 2 digits)");
return None; }
}
"--version" | "-V" => { fn print_version() {
println!("echo version: {:s}", VERSION); 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)
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -152,30 +187,29 @@ pub fn uumain(args: Vec<String>) -> int {
Some((index, c)) => { Some((index, c)) => {
if !prev_was_slash { if !prev_was_slash {
if c != '\\' { if c != '\\' {
print_char(c); print!("{}", c);
} else { } else {
prev_was_slash = true; prev_was_slash = true;
} }
} else { } else {
prev_was_slash = false; prev_was_slash = false;
match c { match c {
'\\' => print_char('\\'), '\\' => print!("\\"),
'a' => print_char('\x07'), 'a' => print!("\x07"),
'b' => print_char('\x08'), 'b' => print!("\x08"),
'c' => break, 'c' => break,
'e' => print_char('\x1B'), 'e' => print!("\x1B"),
'f' => print_char('\x0C'), 'f' => print!("\x0C"),
'n' => print_char('\n'), 'n' => print!("\n"),
'r' => print_char('\r'), 'r' => print!("\r"),
't' => print_char('\t'), 't' => print!("\t"),
'v' => print_char('\x0B'), 'v' => print!("\x0B"),
'x' => { 'x' => {
let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 16u); let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 16u);
if num_char_used == 0 { if num_char_used == 0 {
print_char('\\'); print!("\\x");
print_char('x');
} else { } else {
print_char(c); print!("{}", c);
for _ in range(0, num_char_used) { for _ in range(0, num_char_used) {
iter.next(); // consume used characters iter.next(); // consume used characters
} }
@ -184,17 +218,24 @@ pub fn uumain(args: Vec<String>) -> int {
'0' => { '0' => {
let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 8u); let (c, num_char_used) = convert_str(string.as_slice(), index + 1, 8u);
if num_char_used == 0 { if num_char_used == 0 {
print_char('\0'); print!("\0");
} else { } else {
print_char(c); print!("{}", c);
for _ in range(0, num_char_used) { for _ in range(0, num_char_used) {
iter.next(); // consume used characters iter.next(); // consume used characters
} }
} }
} }
_ => { _ => {
print_char('\\'); let (esc_c, num_char_used) = convert_str(string.as_slice(), index, 8u);
print_char(c); if num_char_used == 0 {
print!("\\{}", c);
} else {
print!("{}", esc_c);
for _ in range(1, num_char_used) {
iter.next(); // consume used characters
}
}
} }
} }
} }