1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 20:17:45 +00:00

echo: refactor slightly

This commit is contained in:
Bulat Musin 2018-01-03 18:20:38 +03:00
parent 26ad240572
commit ec543508bc

View file

@ -16,9 +16,9 @@ use std::io::{Write, stdout};
use std::str::from_utf8; use std::str::from_utf8;
#[allow(dead_code)] #[allow(dead_code)]
static SYNTAX: &'static str = "[OPTIONS]... [STRING]..."; static SYNTAX: &str = "[OPTIONS]... [STRING]...";
static SUMMARY: &'static str = "display a line of text"; static SUMMARY: &str = "display a line of text";
static LONG_HELP: &'static str = r#" static HELP: &str = r#"
Echo the STRING(s) to standard output. Echo the STRING(s) to standard output.
If -e is in effect, the following sequences are recognized: If -e is in effect, the following sequences are recognized:
@ -36,26 +36,24 @@ static LONG_HELP: &'static str = r#"
\\xHH byte with hexadecimal value HH (1 to 2 digits) \\xHH byte with hexadecimal value HH (1 to 2 digits)
"#; "#;
#[derive(Clone)] enum Base {
struct EchoOptions { B8 = 8,
B16 = 16,
}
struct Opts {
newline: bool, newline: bool,
escape: bool escape: bool
} }
#[inline(always)] fn convert_str(string: &[u8], index: usize, base: Base) -> (char, usize) {
fn to_char(bytes: &[u8], base: u32) -> char { let (max_digits, is_legal_digit): (usize, fn(u8) -> bool) = match base {
usize::from_str_radix(from_utf8(bytes.as_ref()).unwrap(), base).unwrap() as u8 as char Base::B8 => (3, |c| { (c as char).is_digit(8) }),
} Base::B16 => (2, |c| { (c as char).is_digit(16) }),
fn convert_str(string: &[u8], index: usize, base: u32) -> (char, usize) {
let (max_digits, is_legal_digit) : (usize, fn(u8) -> bool) = match base {
8 => (3, |c| { (c as char).is_digit(8) }),
16 => (2, |c| { (c as char).is_digit(16) }),
_ => panic!(),
}; };
let mut bytes = vec!(); let mut bytes = vec![];
for offset in 0usize .. max_digits { for offset in 0..max_digits {
if string.len() <= index + offset as usize { if string.len() <= index + offset as usize {
break; break;
} }
@ -70,39 +68,39 @@ fn convert_str(string: &[u8], index: usize, base: u32) -> (char, usize) {
if bytes.is_empty() { if bytes.is_empty() {
(' ', 0) (' ', 0)
} else { } else {
(to_char(&bytes, base), bytes.len()) (usize::from_str_radix(
from_utf8(bytes.as_ref()).unwrap(),
base as u32
).unwrap() as u8 as char, bytes.len())
} }
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut options = EchoOptions { let matches = new_coreopts!(SYNTAX, SUMMARY, HELP)
newline: false,
escape: false
};
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("n", "", "do not output the trailing newline") .optflag("n", "", "do not output the trailing newline")
.optflag("e", "", "enable interpretation of backslash escapes") .optflag("e", "", "enable interpretation of backslash escapes")
.optflag("E", "", "disable interpretation of backslash escapes (default)") .optflag("E", "", "disable interpretation of backslash escapes (default)")
.parse(args); .parse(args);
options.newline = matches.opt_present("n"); let options = Opts {
options.escape = matches.opt_present("e"); newline: matches.opt_present("n"),
escape: matches.opt_present("e"),
};
let free = matches.free; let free = matches.free;
if !free.is_empty() { if !free.is_empty() {
let string = free.join(" "); let string = free.join(" ");
if options.escape { if options.escape {
let mut prev_was_slash = false; let mut prev_was_slash = false;
let mut iter = string.chars().enumerate(); let mut iter = string.chars().enumerate();
while let Some((index, c)) = iter.next() { while let Some((mut idx, c)) = iter.next() {
if !prev_was_slash { prev_was_slash = if !prev_was_slash {
if c != '\\' { if c != '\\' {
print!("{}", c); print!("{}", c);
false
} else { } else {
prev_was_slash = true; true
} }
} else { } else {
prev_was_slash = false;
match c { match c {
'\\' => print!("\\"), '\\' => print!("\\"),
'n' => print!("\n"), 'n' => print!("\n"),
@ -114,41 +112,33 @@ pub fn uumain(args: Vec<String>) -> i32 {
'c' => break, 'c' => break,
'e' => print!("\x1B"), 'e' => print!("\x1B"),
'f' => print!("\x0C"), 'f' => print!("\x0C"),
'x' => { ch => { // 'x' or '0' or _
let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 16); idx = if ch == 'x' || ch == '0' {
if num_char_used == 0 { idx + 1
print!("\\x");
} else { } else {
print!("{}", c); idx
for _ in 0 .. num_char_used { };
iter.next(); // consume used characters let base = if ch == 'x' { Base::B16 } else { Base::B8 };
} match convert_str(string.as_bytes(), idx, base) {
(_, 0) => {
match ch {
'x' => print!("\\x"),
'0' => print!("\0"),
_ => print!("\\{}", c),
} }
}, },
'0' => { (c, num_char_used) => {
let (c, num_char_used) = convert_str(string.as_bytes(), index + 1, 8);
if num_char_used == 0 {
print!("\0");
} else {
print!("{}", c); print!("{}", c);
for _ in 0 .. num_char_used { let beg = if ch == 'x' || ch == '0' { 0 } else { 1 };
iter.next(); // consume used characters for _ in beg..num_char_used {
}
}
}
_ => {
let (esc_c, num_char_used) = convert_str(string.as_bytes(), index, 8);
if num_char_used == 0 {
print!("\\{}", c);
} else {
print!("{}", esc_c);
for _ in 1 .. num_char_used {
iter.next(); // consume used characters iter.next(); // consume used characters
} }
} }
} }
} }
} }
false
}
} }
} else { } else {
print!("{}", string); print!("{}", string);