@@ -339,27 +339,66 @@ fn compile_address_range(
339339 cmd. addr1 = Some ( addr1) ;
340340 if is_line0 && context. posix {
341341 // 0 starting address is a GNU extension.
342- return compilation_error ( lines, line, "address 0 invalid in POSIX mode" ) ;
342+ return compilation_error ( lines, line, "address 0 is invalid in POSIX mode" ) ;
343343 }
344344 n_addr += 1 ;
345345 }
346346
347347 line. eat_spaces ( ) ;
348- if n_addr == 1 && !line. eol ( ) && line. current ( ) == ',' {
348+ if n_addr == 1 && !line. eol ( ) && matches ! ( line. current( ) , ',' | '~' ) {
349+ let is_step_match = line. current ( ) == '~' ; // E.g. 0~2: Pick even-numbered lines
349350 line. advance ( ) ;
350351 line. eat_spaces ( ) ;
352+ let is_step_end = if line. current ( ) == '~' {
353+ // E.g. /foo/,~10: Start at foo, include all lines until multiple of 10 is reached.
354+ line. advance ( ) ;
355+ line. eat_spaces ( ) ;
356+ true
357+ } else {
358+ false
359+ } ;
360+
361+ if ( is_step_match || is_step_end) && context. posix {
362+ // ~ steps are a GNU extension.
363+ return compilation_error ( lines, line, "~step is invalid in POSIX mode" ) ;
364+ }
365+
366+ // Look for second address.
351367 if !line. eol ( )
352368 && let Ok ( addr2) = compile_address ( lines, line, context)
353369 {
354- let addr2_is_re = matches ! ( addr2, Address :: Re ( _) ) ;
355- cmd. addr2 = Some ( addr2) ;
356- if is_line0 && !addr2_is_re {
370+ // Set step_n to the number specified in the (required numeric) address.
371+ let step_n = if is_step_match || is_step_end {
372+ match addr2 {
373+ Address :: Line ( n) => n,
374+ _ => {
375+ return compilation_error (
376+ lines,
377+ line,
378+ "~step can only be specified on numeric addresses" ,
379+ ) ;
380+ }
381+ }
382+ } else {
383+ 0 // dummy, not used
384+ } ;
385+
386+ if is_line0 && !matches ! ( addr2, Address :: Re ( _) ) && !is_step_match {
357387 return compilation_error (
358388 lines,
359389 line,
360- "address 0 can only be used with a regular expression second address " ,
390+ "address 0 can only be used with a regular expression or ~step " ,
361391 ) ;
362392 }
393+
394+ // If needed, transform Address::Line into Address::Step*.
395+ cmd. addr2 = if is_step_match {
396+ Some ( Address :: StepMatch ( step_n) )
397+ } else if is_step_end {
398+ Some ( Address :: StepEnd ( step_n) )
399+ } else {
400+ Some ( addr2)
401+ } ;
363402 n_addr += 1 ;
364403 }
365404 }
@@ -390,6 +429,8 @@ fn read_file_path(lines: &ScriptLineProvider, line: &mut ScriptCharProvider) ->
390429}
391430
392431/// Compile and return a single range address specification.
432+ // Due to their irregular syntax ~ addresses are returned as Line() and adjusted
433+ // in compile_address_range().
393434fn compile_address (
394435 lines : & ScriptLineProvider ,
395436 line : & mut ScriptCharProvider ,
@@ -1625,6 +1666,28 @@ mod tests {
16251666 assert ! ( matches!( cmd. borrow( ) . addr2, Some ( Address :: RelLine ( 3 ) ) ) ) ;
16261667 }
16271668
1669+ #[ test]
1670+ fn test_compile_step_match_address ( ) {
1671+ let ( lines, mut chars) = make_providers ( "0~2" ) ;
1672+ let mut cmd = Rc :: new ( RefCell :: new ( Command :: default ( ) ) ) ;
1673+ let n_addr = compile_address_range ( & lines, & mut chars, & mut cmd, & ctx ( ) ) . unwrap ( ) ;
1674+
1675+ assert_eq ! ( n_addr, 2 ) ;
1676+ assert ! ( matches!( cmd. borrow( ) . addr1, Some ( Address :: Line ( 0 ) ) ) ) ;
1677+ assert ! ( matches!( cmd. borrow( ) . addr2, Some ( Address :: StepMatch ( 2 ) ) ) ) ;
1678+ }
1679+
1680+ #[ test]
1681+ fn test_compile_step_end_address ( ) {
1682+ let ( lines, mut chars) = make_providers ( "1,~10" ) ;
1683+ let mut cmd = Rc :: new ( RefCell :: new ( Command :: default ( ) ) ) ;
1684+ let n_addr = compile_address_range ( & lines, & mut chars, & mut cmd, & ctx ( ) ) . unwrap ( ) ;
1685+
1686+ assert_eq ! ( n_addr, 2 ) ;
1687+ assert ! ( matches!( cmd. borrow( ) . addr1, Some ( Address :: Line ( 1 ) ) ) ) ;
1688+ assert ! ( matches!( cmd. borrow( ) . addr2, Some ( Address :: StepEnd ( 10 ) ) ) ) ;
1689+ }
1690+
16281691 #[ test]
16291692 fn test_compile_last_address ( ) {
16301693 let ( lines, mut chars) = make_providers ( "$" ) ;
0 commit comments