From 9bea8a39f8c9230882bce5dcf8e2a5144d0a3a96 Mon Sep 17 00:00:00 2001 From: Rostyslav Toch Date: Wed, 24 Dec 2025 17:30:02 +0000 Subject: [PATCH 1/2] ptx: handle invalid regex arguments gracefully instead of panicking --- src/uu/ptx/src/ptx.rs | 18 ++++++++++++------ tests/by-util/test_ptx.rs | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 28d19cdbdfd..61a32c63571 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -19,7 +19,7 @@ use clap::{Arg, ArgAction, Command}; use regex::Regex; use thiserror::Error; use uucore::display::Quotable; -use uucore::error::{FromIo, UError, UResult, UUsageError}; +use uucore::error::{FromIo, UError, UResult, USimpleError, UUsageError}; use uucore::format_usage; use uucore::translate; @@ -301,9 +301,15 @@ fn read_input(input_files: &[OsString]) -> std::io::Result { } /// Go through every lines in the input files and record each match occurrence as a `WordRef`. -fn create_word_set(config: &Config, filter: &WordFilter, file_map: &FileMap) -> BTreeSet { - let reg = Regex::new(&filter.word_regex).unwrap(); - let ref_reg = Regex::new(&config.context_regex).unwrap(); +fn create_word_set( + config: &Config, + filter: &WordFilter, + file_map: &FileMap, +) -> UResult> { + let reg = Regex::new(&filter.word_regex) + .map_err(|e| USimpleError::new(1, format!("invalid regular expression: {}", e)))?; + let ref_reg = Regex::new(&config.context_regex) + .map_err(|e| USimpleError::new(1, format!("invalid regular expression: {}", e)))?; let mut word_set: BTreeSet = BTreeSet::new(); for (file, lines) in file_map { let mut count: usize = 0; @@ -342,7 +348,7 @@ fn create_word_set(config: &Config, filter: &WordFilter, file_map: &FileMap) -> count += 1; } } - word_set + Ok(word_set) } fn get_reference(config: &Config, word_ref: &WordRef, line: &str, context_reg: &Regex) -> String { @@ -884,7 +890,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let word_filter = WordFilter::new(&matches, &config)?; let file_map = read_input(&input_files).map_err_context(String::new)?; - let word_set = create_word_set(&config, &word_filter, &file_map); + let word_set = create_word_set(&config, &word_filter, &file_map)?; write_traditional_output(&mut config, &file_map, &word_set, &output_file) } diff --git a/tests/by-util/test_ptx.rs b/tests/by-util/test_ptx.rs index c9ecb5c22e2..7859f9bcc79 100644 --- a/tests/by-util/test_ptx.rs +++ b/tests/by-util/test_ptx.rs @@ -301,3 +301,19 @@ fn test_unicode_truncation_alignment() { .succeeds() .stdout_only(" / bar\n föö/\n"); } + +#[test] +fn test_invalid_regex_word_trailing_backslash() { + new_ucmd!() + .args(&["-W", "bar\\"]) + .fails_with_code(1) + .stderr_contains("ptx: invalid regular expression"); +} + +#[test] +fn test_invalid_regex_word_unclosed_group() { + new_ucmd!() + .args(&["-W", "(wrong"]) + .fails_with_code(1) + .stderr_contains("ptx: invalid regular expression"); +} \ No newline at end of file From c385d4f0422edfd12eda3df58b6e7c4a84edd249 Mon Sep 17 00:00:00 2001 From: Rostyslav Toch Date: Wed, 24 Dec 2025 22:05:31 +0000 Subject: [PATCH 2/2] ptx: use translate! macro for regex error messages --- src/uu/ptx/locales/en-US.ftl | 1 + src/uu/ptx/src/ptx.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/uu/ptx/locales/en-US.ftl b/src/uu/ptx/locales/en-US.ftl index 402b2702b47..c7362122447 100644 --- a/src/uu/ptx/locales/en-US.ftl +++ b/src/uu/ptx/locales/en-US.ftl @@ -28,3 +28,4 @@ ptx-error-dumb-format = There is no dumb format with GNU extensions disabled ptx-error-not-implemented = { $feature } not implemented yet ptx-error-write-failed = write failed ptx-error-extra-operand = extra operand { $operand } +ptx-invalid-regular-expression = invalid regular expression: { $error } diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 61a32c63571..0a96dc6c6f8 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -307,9 +307,9 @@ fn create_word_set( file_map: &FileMap, ) -> UResult> { let reg = Regex::new(&filter.word_regex) - .map_err(|e| USimpleError::new(1, format!("invalid regular expression: {}", e)))?; + .map_err(|e| USimpleError::new(1, translate!("ptx-invalid-regular-expression", "error" => e)))?; let ref_reg = Regex::new(&config.context_regex) - .map_err(|e| USimpleError::new(1, format!("invalid regular expression: {}", e)))?; + .map_err(|e| USimpleError::new(1, translate!("ptx-invalid-regular-expression", "error" => e)))?; let mut word_set: BTreeSet = BTreeSet::new(); for (file, lines) in file_map { let mut count: usize = 0;