mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 04:27:45 +00:00
Merge pull request #280 from Arcterus/echo-fix
Make echo pass the Busybox test suite
This commit is contained in:
commit
5c501cc815
2 changed files with 131 additions and 67 deletions
176
echo/echo.rs
176
echo/echo.rs
|
@ -20,17 +20,22 @@ use std::uint;
|
||||||
#[path = "../common/util.rs"]
|
#[path = "../common/util.rs"]
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
static NAME: &'static str = "echo";
|
static NAME: &'static str = "echo";
|
||||||
static VERSION: &'static str = "1.0.0";
|
static VERSION: &'static str = "1.0.0";
|
||||||
|
|
||||||
fn print_char(c: char) {
|
#[deriving(Clone)]
|
||||||
print!("{}", c);
|
struct EchoOptions {
|
||||||
|
newline: bool,
|
||||||
|
escape: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
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' |
|
||||||
|
@ -39,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,
|
||||||
|
@ -46,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),
|
||||||
|
@ -55,26 +61,74 @@ fn convert_str(string: &str, index: uint, base: uint) -> (char, int) {
|
||||||
|
|
||||||
let mut bytes = vec!();
|
let mut bytes = vec!();
|
||||||
for offset in range(0, max_digits) {
|
for offset in range(0, max_digits) {
|
||||||
|
if string.len() <= index + offset as uint {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let c = string[index + offset as uint];
|
let c = string[index + offset as uint];
|
||||||
if is_legal_digit(c) {
|
if is_legal_digit(c) {
|
||||||
bytes.push(c as u8);
|
bytes.push(c as u8);
|
||||||
} else {
|
} else {
|
||||||
if bytes.len() > 0 {
|
break;
|
||||||
return (to_char(&bytes, base), offset);
|
|
||||||
} else {
|
|
||||||
return (' ', offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(to_char(&bytes, base), max_digits)
|
if bytes.len() == 0 {
|
||||||
|
(' ', 0)
|
||||||
|
} else {
|
||||||
|
(to_char(&bytes, base), bytes.len())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn parse_options(args: Vec<String>, options: &mut EchoOptions) -> Option<Vec<String>> {
|
||||||
fn main() { os::set_exit_status(uumain(os::args())); }
|
let mut echo_args = vec!();
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> int {
|
|
||||||
let program = args.get(0).clone();
|
let program = args.get(0).clone();
|
||||||
|
'argloop: for arg in args.move_iter().skip(1) {
|
||||||
|
match arg.as_slice() {
|
||||||
|
"--help" | "-h" => {
|
||||||
|
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 = [
|
let opts = [
|
||||||
getopts::optflag("n", "", "do not output the trailing newline"),
|
getopts::optflag("n", "", "do not output the trailing newline"),
|
||||||
getopts::optflag("e", "", "enable interpretation of backslash escapes"),
|
getopts::optflag("e", "", "enable interpretation of backslash escapes"),
|
||||||
|
@ -82,21 +136,14 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
getopts::optflag("h", "help", "display this help and exit"),
|
getopts::optflag("h", "help", "display this help and exit"),
|
||||||
getopts::optflag("V", "version", "output version information and exit"),
|
getopts::optflag("V", "version", "output version information and exit"),
|
||||||
];
|
];
|
||||||
|
println!("echo {:s} - display a line of text", VERSION);
|
||||||
let matches = match getopts::getopts(args.tail(), opts) {
|
println!("");
|
||||||
Ok(m) => m,
|
println!("Usage:");
|
||||||
Err(f) => crash!(1, "Invalid options\n{}", f)
|
println!(" {0:s} [SHORT-OPTION]... [STRING]...", *program);
|
||||||
};
|
println!(" {0:s} LONG-OPTION", *program);
|
||||||
|
println!("");
|
||||||
if matches.opt_present("help") {
|
println(getopts::usage("Echo the STRING(s) to standard output.", opts).as_slice());
|
||||||
println!("echo {:s} - display a line of text", VERSION);
|
println("If -e is in effect, the following sequences are recognized:
|
||||||
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)
|
||||||
|
@ -110,17 +157,29 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
\\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 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if matches.opt_present("version") {
|
fn print_version() {
|
||||||
println!("echo version: {:s}", VERSION);
|
println!("echo version: {:s}", VERSION);
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !matches.free.is_empty() {
|
#[allow(dead_code)]
|
||||||
let string = matches.free.connect(" ");
|
fn main() { os::set_exit_status(uumain(os::args())); }
|
||||||
if matches.opt_present("e") {
|
|
||||||
|
pub fn uumain(args: Vec<String>) -> 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 prev_was_slash = false;
|
||||||
let mut iter = string.as_slice().chars().enumerate();
|
let mut iter = string.as_slice().chars().enumerate();
|
||||||
loop {
|
loop {
|
||||||
|
@ -128,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
|
||||||
}
|
}
|
||||||
|
@ -160,18 +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('\\');
|
print!("\0");
|
||||||
print_char('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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +248,7 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches.opt_present("n") {
|
if !options.newline {
|
||||||
println!("")
|
println!("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,17 +121,17 @@ fn util_map() -> HashMap<&str, fn(Vec<String>) -> int> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(cmap: &HashMap<&str, fn(Vec<String>) -> int>) {
|
fn usage(cmap: &HashMap<&str, fn(Vec<String>) -> int>) {
|
||||||
println!("{} {}", NAME, VERSION);
|
println!("{} {}", NAME, VERSION);
|
||||||
println!("");
|
println!("");
|
||||||
println!("Usage:");
|
println!("Usage:");
|
||||||
println!(" {} [util [arguments...]", NAME);
|
println!(" {} [util [arguments...]", NAME);
|
||||||
println!("Currently defined functions:");
|
println!("Currently defined functions:");
|
||||||
let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
|
let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
|
||||||
utils.sort();
|
utils.sort();
|
||||||
for util in utils.iter() {
|
for util in utils.iter() {
|
||||||
println!("\t{}", util);
|
println!("\t{}", util);
|
||||||
}
|
}
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue