1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

Merge pull request #1356 from Arcterus/echo-locked-stdout

echo: write using locked stdout
This commit is contained in:
Alex Lyon 2019-04-08 15:57:02 -07:00 committed by GitHub
commit 01966e8aab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 19 deletions

View file

@ -13,6 +13,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::io::{self, Write};
use std::iter::Peekable; use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
@ -36,7 +37,12 @@ const HELP: &str = r#"
\\xHH byte with hexadecimal value HH (1 to 2 digits) \\xHH byte with hexadecimal value HH (1 to 2 digits)
"#; "#;
fn parse_code(input: &mut Peekable<Chars>, base: u32, max_digits: u32, bits_per_digit: u32) -> Option<char> { fn parse_code(
input: &mut Peekable<Chars>,
base: u32,
max_digits: u32,
bits_per_digit: u32,
) -> Option<char> {
let mut ret = 0x80000000; let mut ret = 0x80000000;
for _ in 0..max_digits { for _ in 0..max_digits {
match input.peek().and_then(|c| c.to_digit(base)) { match input.peek().and_then(|c| c.to_digit(base)) {
@ -48,9 +54,15 @@ fn parse_code(input: &mut Peekable<Chars>, base: u32, max_digits: u32, bits_per_
std::char::from_u32(ret) 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<bool> {
let mut should_stop = false;
let mut buffer = ['\\'; 2];
let mut iter = input.chars().peekable(); let mut iter = input.chars().peekable();
while let Some(mut c) = iter.next() { while let Some(mut c) = iter.next() {
let mut start = 1;
if c == '\\' { if c == '\\' {
if let Some(next) = iter.next() { if let Some(next) = iter.next() {
c = match next { c = match next {
@ -58,7 +70,7 @@ fn print_escaped(input: &str, should_stop: &mut bool) {
'a' => '\x07', 'a' => '\x07',
'b' => '\x08', 'b' => '\x08',
'c' => { 'c' => {
*should_stop = true; should_stop = true;
break break
}, },
'e' => '\x1b', 'e' => '\x1b',
@ -68,22 +80,30 @@ fn print_escaped(input: &str, should_stop: &mut bool) {
't' => '\t', 't' => '\t',
'v' => '\x0b', 'v' => '\x0b',
'x' => parse_code(&mut iter, 16, 2, 4).unwrap_or_else(|| { 'x' => parse_code(&mut iter, 16, 2, 4).unwrap_or_else(|| {
print!("\\"); start = 0;
next next
}), }),
'0' => parse_code(&mut iter, 8, 3, 3).unwrap_or_else(|| { '0' => parse_code(&mut iter, 8, 3, 3).unwrap_or_else(|| {
print!("\\"); start = 0;
next next
}), }),
_ => { _ => {
print!("\\"); start = 0;
next 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<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
@ -96,24 +116,36 @@ pub fn uumain(args: Vec<String>) -> i32 {
let no_newline = matches.opt_present("n"); let no_newline = matches.opt_present("n");
let escaped = matches.opt_present("e"); 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<String>) -> io::Result<()> {
let stdout = io::stdout();
let mut output = stdout.lock();
for (i, input) in free.iter().enumerate() {
if i > 0 { if i > 0 {
print!(" "); write!(output, " ")?;
} }
if escaped { if escaped {
let mut should_stop = false; let should_stop = print_escaped(&input, &mut output)?;
print_escaped(&input, &mut should_stop);
if should_stop { if should_stop {
break; break;
} }
} else { } else {
print!("{}", input); write!(output, "{}", input)?;
} }
} }
if !no_newline { if !no_newline {
println!(); writeln!(output)?;
} }
0 Ok(())
} }

View file

@ -50,17 +50,22 @@ fn test_escape_hex() {
#[test] #[test]
fn test_escape_short_hex() { fn test_escape_short_hex() {
new_ucmd!().args(&["-e", "foo\\xa bar"]).succeeds().stdout_only("foo\n bar"); new_ucmd!().args(&["-e", "foo\\xa bar"]).succeeds().stdout_only("foo\n bar\n");
} }
#[test] #[test]
fn test_escape_no_hex() { fn test_escape_no_hex() {
new_ucmd!().args(&["-e", "foo\\x bar"]).succeeds().stdout_only("foo\\x bar"); new_ucmd!().args(&["-e", "foo\\x bar"]).succeeds().stdout_only("foo\\x bar\n");
} }
#[test] #[test]
fn test_escape_one_slash() { fn test_escape_one_slash() {
new_ucmd!().args(&["-e", "foo\\ bar"]).succeeds().stdout_only("foo\\ bar"); new_ucmd!().args(&["-e", "foo\\ bar"]).succeeds().stdout_only("foo\\ bar\n");
}
#[test]
fn test_escape_one_slash_multi() {
new_ucmd!().args(&["-e", "foo\\", "bar"]).succeeds().stdout_only("foo\\ bar\n");
} }
#[test] #[test]
@ -80,12 +85,12 @@ fn test_escape_octal() {
#[test] #[test]
fn test_escape_short_octal() { fn test_escape_short_octal() {
new_ucmd!().args(&["-e", "foo\\040bar"]).succeeds().stdout_only("foo bar"); new_ucmd!().args(&["-e", "foo\\040bar"]).succeeds().stdout_only("foo bar\n");
} }
#[test] #[test]
fn test_escape_no_octal() { fn test_escape_no_octal() {
new_ucmd!().args(&["-e", "foo\\0 bar"]).succeeds().stdout_only("foo\\0 bar"); new_ucmd!().args(&["-e", "foo\\0 bar"]).succeeds().stdout_only("foo\\0 bar\n");
} }
#[test] #[test]