diff --git a/cdist/conf/type/__key_value/explorer/state b/cdist/conf/type/__key_value/explorer/state
index 94a5ea7f..b990733d 100755
--- a/cdist/conf/type/__key_value/explorer/state
+++ b/cdist/conf/type/__key_value/explorer/state
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2014 Daniel Heule (hda at sfs.biz)
#
# This file is part of cdist.
#
@@ -18,36 +19,85 @@
# along with cdist. If not, see .
#
-key="$(cat "$__object/parameter/key" 2>/dev/null \
+export key="$(cat "$__object/parameter/key" 2>/dev/null \
|| echo "$__object_id")"
-state="$(cat "$__object/parameter/state" 2>/dev/null \
- || echo "present")"
-file="$(cat "$__object/parameter/file")"
-delimiter="$(cat "$__object/parameter/delimiter")"
-value="$(cat "$__object/parameter/value" 2>/dev/null \
- || echo "__CDIST_NOTSET__")"
+export state="$(cat "$__object/parameter/state")"
-case "$state" in
- absent)
- if grep -q -E "^$key$delimiter+" "$file"; then
- # if the key exists, with whatever value, we will have to remove it
- # so report it as present
- echo present
- else
- # key does not exist
- echo absent
- fi
- ;;
- present)
- if grep -q -E "^$key$delimiter+$value$" "$file"; then
- # key exists and value is same
- echo present
- elif grep -q -E "^$key$delimiter+" "$file"; then
- # key exists, but value is empty or different
- echo wrongvalue
- else
- # key does not exist
- echo absent
- fi
- ;;
-esac
+file="$(cat "$__object/parameter/file")"
+
+if [ ! -f "$file" ]; then
+ echo "nosuchfile"
+ exit
+fi
+
+export delimiter="$(cat "$__object/parameter/delimiter")"
+export value="$(cat "$__object/parameter/value" 2>/dev/null \
+ || echo "__CDIST_NOTSET__")"
+if [ -f "$__object/parameter/exact_delimiter" ]; then
+ export exact_delimiter=1
+else
+ export exact_delimiter=0
+fi
+
+awk -f - "$file" <<"AWK_EOF"
+BEGIN {
+ state=ENVIRON["state"]
+ key=ENVIRON["key"]
+ delimiter=ENVIRON["delimiter"]
+ value=ENVIRON["value"]
+ exact_delimiter=ENVIRON["exact_delimiter"]
+ found=0
+}
+# enter the main loop
+{
+ i = index($0,key)
+ if(i == 1) {
+ delval = substr($0,length(key)+1)
+ delpos = index(delval,delimiter)
+ if(delpos == 0) {
+ # in this case, the delimiter was not found
+ next
+ }
+ if(delpos > 1) {
+ spaces = substr(delval,1,delpos-1)
+ sub(/[ \t]*/,"",spaces)
+ if( length(spaces) > 0 ) {
+ # if there are not only spaces between key and delimiter,
+ # continue since we we are on the wrong line
+ next
+ }
+ if( exact_delimiter == 1) {
+ # we have key and delimiter, but since additional spaces are not alowed
+ # return wrongformat
+ found=1
+ print "wrongformat"
+ exit
+ }
+ }
+ found=1
+ if(state == "absent") {
+ # on state absent, only the ocurance is relevant, so exit here
+ print "present"
+ exit
+ }
+ linevalue=substr(delval,delpos + length(delimiter))
+ if(exact_delimiter == 0){
+ #ok, now strip tabs and whitespaces at the beginning of the value
+ sub(/[ \t]*/,"",linevalue)
+ }
+ # Key with separator found
+ if(linevalue == value) {
+ # exact match found, so state is present
+ print "present"
+ }
+ else {
+ print "wrongvalue"
+ }
+ exit
+ }
+}
+END {
+ if(found == 0)
+ print "absent"
+}
+AWK_EOF
diff --git a/cdist/conf/type/__key_value/files/remote_script.sh b/cdist/conf/type/__key_value/files/remote_script.sh
new file mode 100644
index 00000000..282ba531
--- /dev/null
+++ b/cdist/conf/type/__key_value/files/remote_script.sh
@@ -0,0 +1,102 @@
+export key="$(cat "$__object/parameter/key" 2>/dev/null \
+ || echo "$__object_id")"
+export state="$(cat "$__object/parameter/state")"
+
+file="$(cat "$__object/parameter/file")"
+
+export delimiter="$(cat "$__object/parameter/delimiter")"
+export value="$(cat "$__object/parameter/value" 2>/dev/null \
+ || echo "__CDIST_NOTSET__")"
+if [ -f "$__object/parameter/exact_delimiter" ]; then
+ export exact_delimiter=1
+else
+ export exact_delimiter=0
+fi
+
+tmpfile=$(mktemp "${file}.cdist.XXXXXXXXXX")
+# preserve ownership and permissions by copying existing file over tmpfile
+if [ -f "$file" ]; then
+ cp -p "$file" "$tmpfile"
+else
+ touch "$file"
+fi
+awk -f - "$file" >"$tmpfile" <<"AWK_EOF"
+BEGIN {
+ # import variables in a secure way ..
+ state=ENVIRON["state"]
+ key=ENVIRON["key"]
+ delimiter=ENVIRON["delimiter"]
+ value=ENVIRON["value"]
+ comment=ENVIRON["comment"]
+ exact_delimiter=ENVIRON["exact_delimiter"]
+ inserted=0
+ lastline=""
+ lastlinepopulated=0
+ line=key delimiter value
+}
+# enter the main loop
+{
+ # I dont use regex, this is by design, so we can match against every value without special meanings of chars ...
+ i = index($0,key)
+ if(i == 1) {
+ delval = substr($0,length(key)+1)
+ delpos = index(delval,delimiter)
+ if(delpos > 1) {
+ spaces = substr(delval,1,delpos-1)
+ sub(/[ \t]*/,"",spaces)
+ if( length(spaces) > 0 ) {
+ # if there are not only spaces between key and delimiter,
+ # continue since we we are on the wrong line
+ if(lastlinepopulated == 1) {
+ print lastline
+ }
+ lastline=$0
+ lastlinepopulated=1
+ next
+ }
+ }
+ if(state == "absent") {
+ if(lastline == comment) {
+ # if comment is present, clear lastlinepopulated flag
+ lastlinepopulated=0
+ }
+ # if absent, simple yump over this line
+ next
+ }
+ else {
+ # if comment is present and not present in last line
+ if (lastlinepopulated == 1) {
+ print lastline
+ if( comment != "" && lastline != comment) {
+ print comment
+ }
+ lastlinepopulated=0
+ }
+ inserted=1
+ # state is present, so insert correct line here
+ print line
+ lastline=line
+ next
+ }
+ }
+ else {
+ if(lastlinepopulated == 1) {
+ print lastline
+ }
+ lastline=$0
+ lastlinepopulated=1
+ }
+}
+END {
+ if(lastlinepopulated == 1) {
+ print lastline
+ }
+ if(inserted == 0 && state == "present" ) {
+ if(comment != "" && lastline != comment){
+ print comment
+ }
+ print line
+ }
+}
+AWK_EOF
+mv -f "$tmpfile" "$file"
diff --git a/cdist/conf/type/__key_value/gencode-remote b/cdist/conf/type/__key_value/gencode-remote
index e1041a02..e6815cb6 100755
--- a/cdist/conf/type/__key_value/gencode-remote
+++ b/cdist/conf/type/__key_value/gencode-remote
@@ -2,6 +2,7 @@
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2012-2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2014 Daniel Heule (hda at sfs.biz)
#
# This file is part of cdist.
#
@@ -19,55 +20,56 @@
# along with cdist. If not, see .
#
-key="$__object_id"
-[ -f "$__object/parameter/key" ] && key="$(cat "$__object/parameter/key")"
state_should="$(cat "$__object/parameter/state")"
-
-file="$(cat "$__object/parameter/file")"
-delimiter="$(cat "$__object/parameter/delimiter")"
-# escape double quotes, as that is what we use ourself below
-value_escaped="$(cat "$__object/parameter/value" | sed -e "s/\([\"]\)/\\\\\1/g")"
state_is="$(cat "$__object/explorer/state")"
-[ "$state_is" = "$state_should" ] && exit 0
+if [ "$state_is" = "$state_should" ]; then
+ exit 0
+fi
+# here we check only if the states are valid,
+# emmit messages and
+# let awk do the work ...
case "$state_should" in
absent)
- # remove lines starting with key
- cat << DONE
-tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
-# preserve ownership and permissions by copying existing file over tmpfile
-cp -p "$file" "\$tmpfile"
-sed '/^$key\($delimiter\+\)/d' "$file" > "\$tmpfile"
-mv -f "\$tmpfile" "$file"
-DONE
- echo "remove" >> "$__messages_out"
- ;;
- present)
case "$state_is" in
- absent)
- # add new key and value
- printf 'echo "%s%s%s" >> "%s"' "$key" "$delimiter" "$value_escaped" "$file"
- echo "add" >> "$__messages_out"
+ absent|nosuchfile)
+ # nothing to do
;;
- wrongvalue)
- # change exisiting value
- cat << DONE
-tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
-# preserve ownership and permissions by copying existing file over tmpfile
-cp -p "$file" "\$tmpfile"
-sed "s|^$key\($delimiter\+\).*|$key\\1$value_escaped|" "$file" > "\$tmpfile"
-mv -f "\$tmpfile" "$file"
-DONE
- echo "changevalue" >> "$__messages_out"
+ wrongformat|wrongvalue|present)
+ echo "remove" >> "$__messages_out"
;;
*)
echo "Unknown explorer state: $state_is" >&2
exit 1
+ ;;
+ esac
+ ;;
+ present)
+ case "$state_is" in
+ nosuchfile)
+ echo "create" >> "$__messages_out"
+ ;;
+ absent)
+ echo "insert" >> "$__messages_out"
+ ;;
+ wrongformated|wrongvalue)
+ echo "change" >> "$__messages_out"
+ ;;
+ present)
+ # nothing to do
+ ;;
+ *)
+ echo "Unknown explorer state: $state_is" >&2
+ exit 1
+ ;;
esac
;;
*)
echo "Unknown state: $state_should" >&2
exit 1
+ ;;
esac
+
+cat "$__type/files/remote_script.sh"
diff --git a/cdist/conf/type/__key_value/man.text b/cdist/conf/type/__key_value/man.text
index 7def7139..d4c8e2cc 100644
--- a/cdist/conf/type/__key_value/man.text
+++ b/cdist/conf/type/__key_value/man.text
@@ -25,21 +25,36 @@ delimiter::
OPTIONAL PARAMETERS
-------------------
state::
- present or absent, defaults to present. If present, sets the key to value,
- if absent, removes the key from the file.
+ present or absent, defaults to present. If present, sets the key to value,
+ if absent, removes the key from the file.
key::
- The key to change. Defaults to object_id.
+ The key to change. Defaults to object_id.
value::
- The value for the key. Optional if state=absent, required otherwise.
+ The value for the key. Optional if state=absent, required otherwise.
+comment::
+ If supplied, the value will be inserted before the line with the key,
+ but only if the key or value must be changed.
+ You need to ensure yourself that the line is prefixed with the correct
+ comment sign. (for example # or ; or wathever ..)
+
+
+BOOLEAN PARAMETERS
+------------------
+exact_delimiter::
+ If supplied, treat additional whitespaces between key, delimiter and value
+ as wrong value.
+
MESSAGES
--------
-create::
+remove::
+ Removed existing key and value
+insert::
Added key and value
change::
Changed value of existing key
-remove::
- Removed existing key and value
+create::
+ A new line was inserted in a new file
EXAMPLES
@@ -55,13 +70,19 @@ __key_value my-fancy-id --file /etc/login.defs --key SYS_UID_MAX --value 666 \
# Enable packet forwarding
__key_value net.ipv4.ip_forward --file /etc/sysctl.conf --value 1 \
- --delimiter '='
+ --delimiter ' = ' --comment '# my linux kernel should act as a router'
# Remove existing key/value
__key_value LEGACY_KEY --file /etc/somefile --state absent --delimiter '='
--------------------------------------------------------------------------------
+MORE INFORMATION
+----------------
+This type try to handle as many values as possible, so it doesn't use regexes.
+So you need to exactly specify the key and delimiter. Delimiter can be of any lenght.
+
+
SEE ALSO
--------
- cdist-type(7)
diff --git a/cdist/conf/type/__key_value/parameter/boolean b/cdist/conf/type/__key_value/parameter/boolean
new file mode 100644
index 00000000..190831c1
--- /dev/null
+++ b/cdist/conf/type/__key_value/parameter/boolean
@@ -0,0 +1 @@
+exact_delimiter
diff --git a/cdist/conf/type/__key_value/parameter/default/comment b/cdist/conf/type/__key_value/parameter/default/comment
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/cdist/conf/type/__key_value/parameter/default/comment
@@ -0,0 +1 @@
+
diff --git a/cdist/conf/type/__key_value/parameter/optional b/cdist/conf/type/__key_value/parameter/optional
index 483e3192..666be2ae 100644
--- a/cdist/conf/type/__key_value/parameter/optional
+++ b/cdist/conf/type/__key_value/parameter/optional
@@ -1,3 +1,4 @@
key
value
state
+comment