From ebf30a2e8a6cb7507c15b71e1fd73c246705225c Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 1 Jul 2026 14:11:09 -0500 Subject: [PATCH 1/2] test: add acceptance test for CFE-2663 (empty trailing region) An insert_lines promise using select_region with select_end_match_eof cannot select the region of the final section when that section header is the last line of the file. The region is empty and sits at the end of the file, SelectRegion() rejects it, and the insertion fails with "could not select an edit region". This test fails today and will pass once SelectRegion() honors select_end_match_eof for an empty region at end of file. Ticket: CFE-2663 Changelog: None Signed-off-by: Nick Anderson --- tests/acceptance/31_tickets/CFE-2663/test.cf | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/acceptance/31_tickets/CFE-2663/test.cf diff --git a/tests/acceptance/31_tickets/CFE-2663/test.cf b/tests/acceptance/31_tickets/CFE-2663/test.cf new file mode 100644 index 0000000000..ffcf713441 --- /dev/null +++ b/tests/acceptance/31_tickets/CFE-2663/test.cf @@ -0,0 +1,81 @@ +body file control +{ + inputs => { "../../default.sub.cf" }; +} + +bundle agent __main__ +{ + methods: + "bundlesequence" usebundle => default("$(this.promise_filename)"); +} + +bundle agent init +{ + vars: + # No trailing newline in the strings, so the "[section2]" header is + # genuinely the last line of the "actual" file. The region selected for + # section2 is therefore empty and sits at the end of the file. + "actual" string => "[section] +keyone=valueone +[section2]"; + + "expected" string => "[section] +keyone=valueone +[section2] +keytwo=valuetwo"; + + "files" slist => { "actual", "expected" }; + + files: + "$(G.testfile).$(files)" + create => "true", + edit_line => init_insert("$(init.$(files))"), + edit_defaults => init_empty; +} + +bundle edit_line init_insert(str) +{ + insert_lines: + "$(str)"; +} + +body edit_defaults init_empty +{ + empty_file_before_editing => "true"; +} + +bundle agent test +{ + meta: + "description" + string => "Insertion into an empty trailing region (the select_start delimiter is the last line of the file) must succeed when select_end_match_eof is true", + meta => { "CFE-2663" }; + + files: + "$(G.testfile).actual" + edit_line => insert_into_trailing_section; +} + +bundle edit_line insert_into_trailing_section +{ + insert_lines: + "keytwo=valuetwo" + select_region => ini_section("section2"); +} + +body select_region ini_section(x) +# @brief Restrict the edit to the lines in section [x], matching to EOF when +# [x] is the final section in the file. +{ + select_start => "\[$(x)\]\s*"; + select_end => "\[.*\]\s*"; + select_end_match_eof => "true"; +} + +bundle agent check +{ + methods: + "any" usebundle => dcs_check_diff("$(G.testfile).actual", + "$(G.testfile).expected", + "$(this.promise_filename)"); +} From c7bcc4452e2d2b98e6fd04819182c2ed798937ee Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 1 Jul 2026 17:05:56 -0500 Subject: [PATCH 2/2] Fixed select_region failing to select an empty region at end of file When select_region matched a start delimiter on the last line of the file and include_start_delimiter was false, SelectRegion() rejected the region as an "empty region at the end of file" and the edit failed with "could not select an edit region". This happened even when select_end_match_eof was true, which is meant to let the region extend to the end of the file. Now the empty region at end of file is accepted when select_end_match_eof is set, so a trailing section (its header being the last line of the file) can still be populated by insert_lines and similar promises. Ticket: CFE-2663 Changelog: Title Signed-off-by: Nick Anderson --- cf-agent/files_editline.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cf-agent/files_editline.c b/cf-agent/files_editline.c index c66fd8ab82..a0a48c4b43 100644 --- a/cf-agent/files_editline.c +++ b/cf-agent/files_editline.c @@ -869,7 +869,13 @@ If no such region matches, begin_ptr and end_ptr should point to NULL { if (!include_start) { - if (ip->next == NULL) + /* The start delimiter is the last line of the file, so the + * region (which excludes the delimiter) is empty and sits + * at end of file. Reject it only when select_end_match_eof + * is not set. Otherwise the empty region at EOF is a valid, + * selectable region so that e.g. a trailing INI section can + * still be populated (CFE-2663). */ + if (ip->next == NULL && !a->region.select_end_match_eof) { Log(LOG_LEVEL_VERBOSE, "The promised start pattern '%s' found an empty region at the end of file '%s'",