diff --git a/src/uu/tr/src/operation.rs b/src/uu/tr/src/operation.rs index 035f09972..6a1bf9391 100644 --- a/src/uu/tr/src/operation.rs +++ b/src/uu/tr/src/operation.rs @@ -23,7 +23,7 @@ use std::{ io::{BufRead, Write}, ops::Not, }; -use uucore::error::UError; +use uucore::error::{UError, UResult, USimpleError}; use uucore::show_warning; #[derive(Debug, Clone)] @@ -608,7 +608,7 @@ impl SymbolTranslator for SqueezeOperation { } } -pub fn translate_input(input: &mut R, output: &mut W, mut translator: T) +pub fn translate_input(input: &mut R, output: &mut W, mut translator: T) -> UResult<()> where T: SymbolTranslator, R: BufRead, @@ -616,15 +616,25 @@ where { let mut buf = Vec::new(); let mut output_buf = Vec::new(); + while let Ok(length) = input.read_until(b'\n', &mut buf) { if length == 0 { - break; - } else { - let filtered = buf.iter().filter_map(|c| translator.translate(*c)); - output_buf.extend(filtered); - output.write_all(&output_buf).unwrap(); + break; // EOF reached } + + let filtered = buf.iter().filter_map(|&c| translator.translate(c)); + output_buf.extend(filtered); + + if let Err(e) = output.write_all(&output_buf) { + return Err(USimpleError::new( + 1, + format!("{}: write error: {}", uucore::util_name(), e), + )); + } + buf.clear(); output_buf.clear(); } + + Ok(()) } diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index 67998d26d..ff85002e7 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -132,24 +132,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let delete_op = DeleteOperation::new(set1); let squeeze_op = SqueezeOperation::new(set2); let op = delete_op.chain(squeeze_op); - translate_input(&mut locked_stdin, &mut buffered_stdout, op); + translate_input(&mut locked_stdin, &mut buffered_stdout, op)?; } else { let op = DeleteOperation::new(set1); - translate_input(&mut locked_stdin, &mut buffered_stdout, op); + translate_input(&mut locked_stdin, &mut buffered_stdout, op)?; } } else if squeeze_flag { if sets_len < 2 { let op = SqueezeOperation::new(set1); - translate_input(&mut locked_stdin, &mut buffered_stdout, op); + translate_input(&mut locked_stdin, &mut buffered_stdout, op)?; } else { let translate_op = TranslateOperation::new(set1, set2.clone())?; let squeeze_op = SqueezeOperation::new(set2); let op = translate_op.chain(squeeze_op); - translate_input(&mut locked_stdin, &mut buffered_stdout, op); + translate_input(&mut locked_stdin, &mut buffered_stdout, op)?; } } else { let op = TranslateOperation::new(set1, set2)?; - translate_input(&mut locked_stdin, &mut buffered_stdout, op); + translate_input(&mut locked_stdin, &mut buffered_stdout, op)?; } Ok(()) } diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index 705f40834..f8fcafce3 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -13,6 +13,15 @@ fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } +#[test] +fn test_invalid_input() { + new_ucmd!() + .args(&["1", "1", "<", "."]) + .fails() + .code_is(1) + .stderr_contains("tr: extra operand '<'"); +} + #[test] fn test_to_upper() { new_ucmd!()