diff --git a/functions-common b/functions-common index 9f4acfec1e..333f31da02 100644 --- a/functions-common +++ b/functions-common @@ -119,6 +119,33 @@ function ini_has_option { [ -n "$line" ] } +# Add another config line for a multi-line option. +# It's normally called after iniset of the same option and assumes +# that the section already exists. +# +# Note that iniset_multiline requires all the 'lines' to be supplied +# in the argument list. Doing that will cause incorrect configuration +# if spaces are used in the config values. +# +# iniadd_literal config-file section option value +function iniadd_literal { + local xtrace=$(set +o | grep xtrace) + set +o xtrace + local file=$1 + local section=$2 + local option=$3 + local value=$4 + + [[ -z $section || -z $option ]] && return + + # Add it + sed -i -e "/^\[$section\]/ a\\ +$option = $value +" "$file" + + $xtrace +} + # Set an option in an INI file # iniset config-file section option value function iniset { diff --git a/lib/config b/lib/config index b44e79aa3d..a4d59a31c9 100644 --- a/lib/config +++ b/lib/config @@ -86,7 +86,11 @@ function merge_config_file { # having to do nasty quoting games get_meta_section $file $matchgroup $configfile | \ $CONFIG_AWK_CMD -v configfile=$configfile ' - BEGIN { section = "" } + BEGIN { + section = "" + last_section = "" + section_count = 0 + } /^\[.+\]/ { gsub("[][]", "", $1); section=$1 @@ -106,10 +110,48 @@ function merge_config_file { # need to strip leading & trailing whitespace from value sub(/^[ \t]*/, "", value) sub(/[ \t]*$/, "", value) - print "iniset " configfile " " section " " attr " \x27" value "\x27" + + # cfg_attr_count: number of config lines per [section, attr] + # cfg_attr: three dimensional array to keep all the config lines per [section, attr] + # cfg_section: keep the section names in the same order as they appear in local.conf + # cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf + if (! (section, attr) in cfg_attr_count) { + if (section != last_section) { + cfg_section[section_count++] = section + last_section = section + } + attr_count = cfg_sec_attr_count[section_count - 1]++ + cfg_sec_attr_name[section_count - 1, attr_count] = attr + + cfg_attr[section, attr, 0] = value + cfg_attr_count[section, attr] = 1 + } else { + lno = cfg_attr_count[section, attr]++ + cfg_attr[section, attr, lno] = value + } + } + END { + # Process each section in order + for (sno = 0; sno < section_count; sno++) { + section = cfg_section[sno] + # The ini routines simply append a config item immediately + # after the section header. To keep the same order as defined + # in local.conf, invoke the ini routines in the reverse order + for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) { + attr = cfg_sec_attr_name[sno, attr_no] + if (cfg_attr_count[section, attr] == 1) + print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, 0] "\x27" + else { + # For multiline, invoke the ini routines in the reverse order + count = cfg_attr_count[section, attr] + print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, count - 1] "\x27" + for (l = count -2; l >= 0; l--) + print "iniadd_literal " configfile " " section " " attr " \x27" cfg_attr[section, attr, l] "\x27" + } + } + } } ' | while read a; do eval "$a"; done - } diff --git a/tests/test_config.sh b/tests/test_config.sh index 50e8d7b2e6..cd74cee6f0 100755 --- a/tests/test_config.sh +++ b/tests/test_config.sh @@ -108,6 +108,27 @@ attr = strip_trailing_space [[test7|test-colon.conf]] [DEFAULT] servers=10.11.12.13:80 + +[[test-multi-sections|test-multi-sections.conf]] +[sec-1] +cfg_item1 = abcd +cfg_item2 = efgh + +[sec-2] +cfg_item1 = abcd +cfg_item3 = /1/2/3/4:5 +cfg_item4 = end + +[sec-3] +cfg_item5 = 5555 +cfg_item6 = 6666 +cfg_item5 = 5555another + +[[test-multiline|test-multiline.conf]] +[multi] +cfg_item1 = "ab":"cd", "ef": "gh" +cfg_item1 = abcd +cfg_item2 = efgh EOF echo -n "get_meta_section_files: test0 doesn't exist: " @@ -189,8 +210,39 @@ VAL=$(cat test2a.conf) # iniset adds a blank line if it creates the file... EXPECT_VAL=" [ddd] -additional = true -type = new" +type = new +additional = true" +check_result "$VAL" "$EXPECT_VAL" + +echo -n "merge_config_file test-multi-sections: " +rm -f test-multi-sections.conf +merge_config_file test.conf test-multi-sections test-multi-sections.conf +VAL=$(cat test-multi-sections.conf) +EXPECT_VAL=' +[sec-1] +cfg_item1 = abcd +cfg_item2 = efgh + +[sec-2] +cfg_item1 = abcd +cfg_item3 = /1/2/3/4:5 +cfg_item4 = end + +[sec-3] +cfg_item5 = 5555 +cfg_item5 = 5555another +cfg_item6 = 6666' +check_result "$VAL" "$EXPECT_VAL" + +echo -n "merge_config_file test-multiline: " +rm -f test-multiline.conf +merge_config_file test.conf test-multiline test-multiline.conf +VAL=$(cat test-multiline.conf) +EXPECT_VAL=' +[multi] +cfg_item1 = "ab":"cd", "ef": "gh" +cfg_item1 = abcd +cfg_item2 = efgh' check_result "$VAL" "$EXPECT_VAL" echo -n "merge_config_group test2: " @@ -200,8 +252,8 @@ VAL=$(cat test2a.conf) # iniset adds a blank line if it creates the file... EXPECT_VAL=" [ddd] -additional = true -type = new" +type = new +additional = true" check_result "$VAL" "$EXPECT_VAL" echo -n "merge_config_group test2 no conf file: " @@ -281,4 +333,5 @@ servers = 10.11.12.13:80" check_result "$VAL" "$EXPECT_VAL" rm -f test.conf test1c.conf test2a.conf test-quote.conf test-space.conf test-equals.conf test-strip.conf test-colon.conf +rm -f test-multiline.conf test-multi-sections.conf rm -rf test-etc