This is a backport of the fix itself. From 4c75f64068a7e1446c9aa6ae8f764e0ebddd67ef Mon Sep 17 00:00:00 2001 From: Norihiro Tanaka Date: Fri, 20 Feb 2015 01:54:35 +0900 Subject: [PATCH 81/92] sed: fix mishandling of overlapping address ranges When the line number ranges of two or more editing commands overlap, sed applies all commands but the first to a line that is just beyond the union of all ranges. E.g., with this command, seq 9|sed '2,7d;3,6d', sed would mistakenly delete line 8. * sed/execute.c (match_an_address_p) [ADDR_IS_NUM]: Make this function work also in this case. (match_address_p): Move the ADDR_IS_NUM + ...->line_number == ...->addr_number comparison "up" into match_an_address_p, so we can hoist two similar if/return blocks out of "if" and "else" branches. Change so that this function returns false when the current line number is outside the specified range. * testsuite/range-overlap.sh: New file, to test for this. * testsuite/Makefile.am (T): Add it to the list. * NEWS (Bug fixes): Mention it. Reported as http://bugs.gnu.org/19899 --- NEWS | 15 +++++++++++++++ sed/execute.c | 16 ++++++++-------- testsuite/Makefile.am | 3 ++- testsuite/range-overlap.sh | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100755 testsuite/range-overlap.sh Index: sed-4.2.2/sed/execute.c =================================================================== --- sed-4.2.2.orig/sed/execute.c +++ sed-4.2.2/sed/execute.c @@ -860,8 +860,10 @@ match_an_address_p(addr, input) case ADDR_IS_LAST: return test_eof(input); - /* ADDR_IS_NUM is handled in match_address_p. */ case ADDR_IS_NUM: + /* reminder: these are only meaningful for a1 addresses */ + return (addr->addr_number == input->line_number); + default: panic("INTERNAL ERROR: bad address type"); } @@ -881,23 +883,20 @@ match_address_p(cmd, input) if (cmd->range_state != RANGE_ACTIVE) { + if (!cmd->a2) + return match_an_address_p(cmd->a1, input); + /* Find if we are going to activate a range. Handle ADDR_IS_NUM specially: it represent an "absolute" state, it should not be computed like regexes. */ if (cmd->a1->addr_type == ADDR_IS_NUM) { - if (!cmd->a2) - return (input->line_number == cmd->a1->addr_number); - if (cmd->range_state == RANGE_CLOSED || input->line_number < cmd->a1->addr_number) return false; } else { - if (!cmd->a2) - return match_an_address_p(cmd->a1, input); - if (!match_an_address_p(cmd->a1, input)) return false; } @@ -913,7 +912,8 @@ match_address_p(cmd, input) /* Same handling as below, but always include at least one line. */ if (input->line_number >= cmd->a2->addr_number) cmd->range_state = RANGE_CLOSED; - return true; + return (input->line_number <= cmd->a2->addr_number + || match_an_address_p(cmd->a1, input)); case ADDR_IS_STEP: cmd->a2->addr_number = input->line_number + cmd->a2->addr_step; return true;