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:
commit
01966e8aab
2 changed files with 56 additions and 19 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue