@@ -10,6 +10,7 @@ use onig::{
1010} ;
1111use onig_sys:: { OnigEncCtype_ONIGENC_CTYPE_WORD , OnigEncodingUTF8 } ;
1212use uucore:: error:: { UResult , USimpleError } ;
13+ use uucore:: show_warning;
1314
1415pub struct Matcher < ' a > {
1516 config : & ' a Config < ' a > ,
@@ -215,6 +216,17 @@ impl CompiledPattern {
215216 // GNU grep supports `{,n}` as an alias for `{0,n}`.
216217 syntax. enable_behavior ( SyntaxBehavior :: SYNTAX_BEHAVIOR_ALLOW_INTERVAL_LOW_ABBREV ) ;
217218 }
219+ let mut normalized_pattern = None ;
220+ let pattern = if config. regex_mode == RegexMode :: Extended {
221+ if let Some ( ( op, rest) ) = strip_leading_repeat_operator ( pattern) {
222+ show_warning ! ( "{op} at start of expression" ) ;
223+ normalized_pattern = Some ( rest. to_string ( ) ) ;
224+ }
225+ normalized_pattern. as_deref ( ) . unwrap_or ( pattern)
226+ } else {
227+ pattern
228+ } ;
229+
218230 if config. regex_mode == RegexMode :: Perl {
219231 // GNU grep supports `(?P<name>...)`.
220232 // Unfortunately, the onig crate defines the OP2 flag without the
@@ -289,3 +301,22 @@ impl CompiledPattern {
289301 . is_some ( )
290302 }
291303}
304+
305+ fn strip_leading_repeat_operator ( pattern : & str ) -> Option < ( & ' static str , & str ) > {
306+ match pattern. as_bytes ( ) . first ( ) ? {
307+ b'?' => Some ( ( "?" , & pattern[ 1 ..] ) ) ,
308+ b'*' => Some ( ( "*" , & pattern[ 1 ..] ) ) ,
309+ b'+' => Some ( ( "+" , & pattern[ 1 ..] ) ) ,
310+ b'{' => strip_leading_interval_repeat ( pattern) . map ( |rest| ( "{...}" , rest) ) ,
311+ _ => None ,
312+ }
313+ }
314+
315+ fn strip_leading_interval_repeat ( pattern : & str ) -> Option < & str > {
316+ let close = pattern. as_bytes ( ) . iter ( ) . position ( |& b| b == b'}' ) ?;
317+ let body = & pattern[ 1 ..close] ;
318+ let is_interval = !body. is_empty ( )
319+ && body. bytes ( ) . all ( |b| b. is_ascii_digit ( ) || b == b',' )
320+ && body. bytes ( ) . any ( |b| b. is_ascii_digit ( ) ) ;
321+ is_interval. then_some ( & pattern[ close + 1 ..] )
322+ }
0 commit comments