diff --git a/src/uu/tr/locales/en-US.ftl b/src/uu/tr/locales/en-US.ftl index 0f1fc38a9..087436847 100644 --- a/src/uu/tr/locales/en-US.ftl +++ b/src/uu/tr/locales/en-US.ftl @@ -23,6 +23,7 @@ tr-error-write-error = write error # Warning messages tr-warning-unescaped-backslash = warning: an unescaped backslash at end of string is not portable tr-warning-ambiguous-octal-escape = the ambiguous octal escape \{ $origin_octal } is being interpreted as the 2-byte sequence \0{ $actual_octal_tail }, { $outstand_char } +tr-warning-invalid-utf8 = invalid utf8 sequence # Sequence parsing error messages tr-error-missing-char-class-name = missing character class name '[::]' diff --git a/src/uu/tr/locales/fr-FR.ftl b/src/uu/tr/locales/fr-FR.ftl index e8af47a0d..075b4a447 100644 --- a/src/uu/tr/locales/fr-FR.ftl +++ b/src/uu/tr/locales/fr-FR.ftl @@ -24,6 +24,7 @@ tr-error-write-error = erreur d'écriture tr-warning-unescaped-backslash = avertissement : une barre oblique inverse non échappée à la fin de la chaîne n'est pas portable tr-warning-ambiguous-octal-escape = l'échappement octal ambigu \{ $origin_octal } est en cours d'interprétation comme la séquence de 2 octets \0{ $actual_octal_tail }, { $outstand_char } +tr-warning-invalid-utf8 = séquence UTF-8 non valide # Messages d'erreur d'analyse de séquence tr-error-missing-char-class-name = nom de classe de caractères manquant '[::]' diff --git a/src/uu/tr/src/operation.rs b/src/uu/tr/src/operation.rs index e2ba2bd83..a9d1f5287 100644 --- a/src/uu/tr/src/operation.rs +++ b/src/uu/tr/src/operation.rs @@ -379,13 +379,16 @@ impl Sequence { let str_to_parse = std::str::from_utf8(out).unwrap(); let result = u8::from_str_radix(str_to_parse, 8).ok(); if result.is_none() { - let origin_octal: &str = std::str::from_utf8(input).unwrap(); - let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap(); - let outstand_char: char = char::from_u32(input[2] as u32).unwrap(); - show_warning!( - "{}", - translate!("tr-warning-ambiguous-octal-escape", "origin_octal" => origin_octal, "actual_octal_tail" => actual_octal_tail, "outstand_char" => outstand_char) - ); + if let Ok(origin_octal) = std::str::from_utf8(input) { + let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap(); + let outstand_char: char = char::from_u32(input[2] as u32).unwrap(); + show_warning!( + "{}", + translate!("tr-warning-ambiguous-octal-escape", "origin_octal" => origin_octal, "actual_octal_tail" => actual_octal_tail, "outstand_char" => outstand_char) + ); + } else { + show_warning!("{}", translate!("tr-warning-invalid-utf8")); + } } result }, diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index 491fd64ee..4a7c266b9 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -1544,6 +1544,30 @@ fn test_non_digit_repeat() { .stderr_only("tr: invalid repeat count 'c' in [c*n] construct\n"); } +#[test] +#[cfg(unix)] +fn test_octal_escape_ambiguous_followed_by_non_utf8() { + // This case does not trigger the panic + let set1 = OsStr::from_bytes(b"\\501a"); + new_ucmd!() + .arg("-d") + .arg(set1) + .pipe_in("(1a)") + .succeeds() + .stderr_contains("warning: the ambiguous octal escape") + .stdout_is(")"); + + // An user is not supposed to use this invalid utf8 set but + // we would need to make the command more error-proof still + let set1 = OsStr::from_bytes(b"\\501\xff"); + new_ucmd!() + .arg("-d") + .arg(set1) + .pipe_in([b'(', b'1', 0xff, b')']) + .succeeds() + .stderr_contains("warning: invalid utf8 sequence"); +} + #[cfg(target_os = "linux")] #[test] fn test_failed_write_is_reported() {