From 42794e9f11c8897e7a086d7d1bf00475e4a71da3 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Fri, 5 Apr 2019 20:44:33 -0700 Subject: [PATCH] echo: write using locked stdout --- src/echo/echo.rs | 60 +++++++++++++++++++++++++++++++++++----------- tests/test_echo.rs | 5 ++++ 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/echo/echo.rs b/src/echo/echo.rs index 8d6e49257..1178652e1 100644 --- a/src/echo/echo.rs +++ b/src/echo/echo.rs @@ -13,6 +13,7 @@ #[macro_use] extern crate uucore; +use std::io::{self, Write}; use std::iter::Peekable; use std::str::Chars; @@ -36,7 +37,12 @@ const HELP: &str = r#" \\xHH byte with hexadecimal value HH (1 to 2 digits) "#; -fn parse_code(input: &mut Peekable, base: u32, max_digits: u32, bits_per_digit: u32) -> Option { +fn parse_code( + input: &mut Peekable, + base: u32, + max_digits: u32, + bits_per_digit: u32, +) -> Option { let mut ret = 0x80000000; for _ in 0..max_digits { match input.peek().and_then(|c| c.to_digit(base)) { @@ -48,9 +54,15 @@ fn parse_code(input: &mut Peekable, base: u32, max_digits: u32, bits_per_ std::char::from_u32(ret) } -fn print_escaped(input: &str, should_stop: &mut bool) { +fn print_escaped(input: &str, mut output: impl Write) -> io::Result { + let mut should_stop = false; + + let mut buffer = ['\\'; 2]; + let mut iter = input.chars().peekable(); while let Some(mut c) = iter.next() { + let mut start = 1; + if c == '\\' { if let Some(next) = iter.next() { c = match next { @@ -58,7 +70,7 @@ fn print_escaped(input: &str, should_stop: &mut bool) { 'a' => '\x07', 'b' => '\x08', 'c' => { - *should_stop = true; + should_stop = true; break }, 'e' => '\x1b', @@ -68,22 +80,30 @@ fn print_escaped(input: &str, should_stop: &mut bool) { 't' => '\t', 'v' => '\x0b', 'x' => parse_code(&mut iter, 16, 2, 4).unwrap_or_else(|| { - print!("\\"); + start = 0; next }), '0' => parse_code(&mut iter, 8, 3, 3).unwrap_or_else(|| { - print!("\\"); + start = 0; next }), _ => { - print!("\\"); + start = 0; next }, }; } } - print!("{}", c); + + buffer[1] = c; + + // because printing char slices is apparently not available in the standard library + for ch in &buffer[start..] { + write!(output, "{}", ch)?; + } } + + Ok(should_stop) } pub fn uumain(args: Vec) -> i32 { @@ -96,24 +116,36 @@ pub fn uumain(args: Vec) -> i32 { let no_newline = matches.opt_present("n"); let escaped = matches.opt_present("e"); - for (i, input) in matches.free.iter().enumerate() { + match execute(no_newline, escaped, matches.free) { + Ok(_) => 0, + Err(f) => { + show_error!("{}", f); + 1 + } + } +} + +fn execute(no_newline: bool, escaped: bool, free: Vec) -> io::Result<()> { + let stdout = io::stdout(); + let mut output = stdout.lock(); + + for (i, input) in free.iter().enumerate() { if i > 0 { - print!(" "); + write!(output, " ")?; } if escaped { - let mut should_stop = false; - print_escaped(&input, &mut should_stop); + let should_stop = print_escaped(&input, &mut output)?; if should_stop { break; } } else { - print!("{}", input); + write!(output, "{}", input)?; } } if !no_newline { - println!(); + writeln!(output)?; } - 0 + Ok(()) } diff --git a/tests/test_echo.rs b/tests/test_echo.rs index 64633b488..4cf31a9aa 100644 --- a/tests/test_echo.rs +++ b/tests/test_echo.rs @@ -63,6 +63,11 @@ fn test_escape_one_slash() { new_ucmd!().args(&["-e", "foo\\ bar"]).succeeds().stdout_only("foo\\ bar"); } +#[test] +fn test_escape_one_slash_multi() { + new_ucmd!().args(&["-e", "foo\\", "bar"]).succeeds().stdout_only("foo\\ bar"); +} + #[test] fn test_escape_newline() { new_ucmd!().args(&["-e", "\\na"]).succeeds().stdout_only("\na\n");