diff --git a/cdist/conf/type/__uci_section/explorer/options b/cdist/conf/type/__uci_section/explorer/options index 67b4f83f..e1e60668 100644 --- a/cdist/conf/type/__uci_section/explorer/options +++ b/cdist/conf/type/__uci_section/explorer/options @@ -25,4 +25,24 @@ section=$("${__type_explorer:?}/match") test -n "${section}" || exit 0 uci -s -N -d "${RS}" show "${section}" 2>/dev/null \ -| grep -v -e "^${section}=" || true +| awk -v VSEP="${RS}" ' + { + # Strip off the config and section parts + is_opt = sub(/^([^.]*\.){2}/, "") + + if (!is_opt) { + # this line represents the section -> skip + next + } + + if (index($0, VSEP)) { + # Put values each on a line, like --option and --list parameters + opt = substr($0, 1, index($0, "=") - 1) + split(substr($0, length(opt) + 2), values, VSEP) + for (i in values) { + printf "%s=%s\n", opt, values[i] + } + } else { + print + } + }' diff --git a/cdist/conf/type/__uci_section/files/functions.sh b/cdist/conf/type/__uci_section/files/functions.sh index 17c64bbc..60cb9148 100644 --- a/cdist/conf/type/__uci_section/files/functions.sh +++ b/cdist/conf/type/__uci_section/files/functions.sh @@ -6,14 +6,6 @@ grep_line() { { shift; printf '%s\n' "$@"; } | grep -qxF "$1" } -prefix_lines() { - while test $# -gt 0 - do - echo "$2" | awk -v prefix="$1" '$0 { printf "%s %s\n", prefix, $0 }' - shift; shift - done -} - print_errors() { awk -v prefix="${1:-Found errors:}" -v suffix="${2-}" ' BEGIN { diff --git a/cdist/conf/type/__uci_section/files/option_state.awk b/cdist/conf/type/__uci_section/files/option_state.awk new file mode 100644 index 00000000..97cd94fb --- /dev/null +++ b/cdist/conf/type/__uci_section/files/option_state.awk @@ -0,0 +1,91 @@ +# -*- mode: awk; indent-tabs-mode:t -*- +# Usage: awk -f option_state.awk option_type option_name +# e.g. awk -f option_state.awk option title +# awk -f option_state.awk list entry + +function unquote(s) { + # simplified dequoting of single quoted strings + if (s ~ /^'.*'$/) { + s = substr(s, 2, length(s) - 2) + sub(/'\\''/, "'", s) + } + return s +} + +function valueof(line) { + if (line !~ /^[[:alpha:]_]+=/) return 0 + return unquote(substr(line, index(line, "=") + 1)) +} + +BEGIN { + __object = ENVIRON["__object"] + if (!__object) exit 1 + + opttype = ARGV[1] + optname = ARGV[2] + + if (opttype !~ /^(option|list)/ || !optname) { + print "invalid" + exit (e=1) + } + + ARGV[1] = __object "/parameter/" opttype + ARGV[2] = __object "/explorer/options" + + state = "present" +} + +NR == FNR { + # memoize "should" state + if (index($0, optname "=") == 1) { + should[++should_count] = valueof($0) + } + + # go to next line (important!) + next +} + +{ + # compare "is" state + if (index($0, optname "=") != 1) + next + ++is_count + + v = valueof($0) + + if (v == should[is_count]) { + # looks good, but can't say definitely just from this line + } else if (is_count > should_count) { + # there are more "is" records than "should" -> definitely different + state = "different" + exit + } else { + # see if we can find the "is" value somewhere in "should" + for (i in should) { + if (v == should[i]) { + # value found -> could be rearranged + # FIXME: Duplicate values are not properly handled here. Do they matter? + state = "rearranged" + next + } + } + + # "is" value could not be found in "should" -> definitely different + state = "different" + exit + } +} + +END { + if (e) exit + + if (!is_count) { + # no "is" values -> absent + state = "absent" + } else if (is_count < should_count) { + # "is" was shorter than "should" -> different + state = "different" + } + + print state +} diff --git a/cdist/conf/type/__uci_section/gencode-remote b/cdist/conf/type/__uci_section/gencode-remote index 05bca0a7..285659b0 100755 --- a/cdist/conf/type/__uci_section/gencode-remote +++ b/cdist/conf/type/__uci_section/gencode-remote @@ -70,7 +70,8 @@ in fi # Make sure the section itself is present - if test "${state_is}" = absent + if test "${state_is}" = absent \ + || test "${type_is}" != "${type_should#*.}" then printf 'set %s\n' "${section}" >>"${__messages_out:?}" # shellcheck disable=SC2140 @@ -78,7 +79,7 @@ in fi # Delete options/lists not in "should" - sed -e 's/=.*$//;s/^.*\.//' "${__object:?}/explorer/options" \ + sed -e 's/=.*$//' "${__object:?}/explorer/options" \ | while read -r _optname do grep_line "${_optname}" "${listnames_should}" "${optnames_should}" || { @@ -87,18 +88,55 @@ in } &2 + exit 1 + } - case $_type + # Set "should" options + echo "${optnames_should}" \ + | grep -e . \ + | while read -r _optname + do + _opt_state=$(awk -f "${__type:?}/files/option_state.awk" option "${_optname}") \ + || opt_proc_error "${_optname}" + case ${_opt_state} in - (list) + (invalid) + opt_proc_error "${_optname}" + ;; + (present) + ;; + (*) + printf 'set %s\n' "${section}.${_optname}" >>"${__messages_out:?}" + + # shellcheck disable=SC2140 + uci_cmd set "${section}.${_optname}"="$( + grep -e "^${_optname}=" "${__object:?}/parameter/option" \ + | sed -e 's/^.*=//' \ + | unquote_lines \ + | head -n 1)" + ;; + esac + done + + echo "${listnames_should}" \ + | grep -e . \ + | while read -r _optname + do + _list_state=$(awk -f "${__type:?}/files/option_state.awk" list "${_optname}") \ + || opt_proc_error "${_optname}" + case ${_list_state} + in + (invalid) + opt_proc_error "${_optname}" + ;; + (present) + ;; + (*) printf 'set_list %s\n' "${section}.${_optname}" >>"${__messages_out:?}" - if grep -q -e "^${_optname}=" "${__object:?}/explorer/options" + if test "${_list_state}" != absent then uci_cmd delete "${section}.${_optname}" fi @@ -112,16 +150,6 @@ in uci_cmd add_list "${section}.${_optname}"="${_value}" done ;; - (option) - printf 'set %s\n' "${section}.${_optname}" >>"${__messages_out:?}" - - # shellcheck disable=SC2140 - uci_cmd set "${section}.${_optname}"="$( - grep "^${_optname}=" "${__object:?}/parameter/option" \ - | sed -e 's/^.*=//' \ - | unquote_lines \ - | head -n 1)" - ;; esac done ;;