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'", 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)"); +}