#! /bin/bash
#
# Johannes Meixner <jsmeix@suse.de>, 2007, 2008, 2009, 2010, 2011

#set -x

# Make sure to have a clean environment:
export PATH="/sbin:/usr/sbin:/usr/bin:/bin"
export LC_ALL="POSIX"
export LANG="POSIX"
umask 022
# Disable bash file name globbing:
set -f

MY_NAME=${0##*/}
CUPSDCONF="/etc/cups/cupsd.conf"

KEY="$1"
if test -z "$KEY"
then echo "Read and write $CUPSDCONF (but does not restart cupsd)." 1>&2
     echo "Usage:" 1>&2
     echo "$MY_NAME keyword [ new value ]" 1>&2
     echo "Without a new value, the current value is reported." 1>&2
     echo "Supported keywords and possible new values are:" 1>&2
     echo "Browsing [ On | Off ]" 1>&2
     echo "      (Browsing On should be set when BrowseAllow or BrowseAddress is not 'none')" 1>&2
     echo "BrowseAllow [ all | @LOCAL | host-address | network-address/netmask ] | BrowseAllow none" 1>&2
     echo "e.g.: BrowseAllow '@LOCAL 192.168.100.1 192.168.200.0/255.255.255.0'" 1>&2
     echo "      BrowseAllow none (to deny all incoming browse packets)" 1>&2
     echo "BrowsePoll [ host-address ] | BrowsePoll none" 1>&2
     echo "e.g.: BrowsePoll '192.168.100.1 192.168.200.1'" 1>&2
     echo "      BrowsePoll none (to poll not at all, i.e. no BrowsePoll line)" 1>&2
     echo "Listen [ all | network-address ] | Listen localhost" 1>&2
     echo "e.g.: Listen '192.168.100.0 192.168.200.0' (localhost is added automatically)" 1>&2
     echo "      Listen localhost (to have only 'Listen 127.0.0.1:631')" 1>&2
     echo "Allow [ all | @LOCAL | @IF(name) | host-address | network-address/netmask ] | Allow none" 1>&2
     echo "e.g.: Allow '@LOCAL @IF(eth1) 192.168.100.1 192.168.200.0/255.255.255.0'" 1>&2
     echo "      (to allow access via the root location '<Location />')" 1>&2
     echo "      Allow none (to have only 'Allow 127.0.0.2', localhost is allowed in any case)" 1>&2
     echo "BrowseAddress [ @LOCAL | @IF(name) | host-address | broadcast-address ] | BrowseAddress none" 1>&2
     echo "e.g.: BrowseAddress '@LOCAL @IF(eth1) 192.168.100.1 192.168.200.255'" 1>&2
     echo "      BrowseAddress none (to send no browse packets, i.e. no BrowseAddress line)" 1>&2
     echo "Policies" 1>&2
     echo "      (reports the existing policy names in '<Policy policy-name>' sections)" 1>&2
     echo "DefaultPolicy [ policy-name ] | DefaultPolicy default" 1>&2
     echo "      (The policy-name must exist as a '<Policy policy-name>' section)" 1>&2
     echo "ErrorPolicy [ stop-printer | retry-job | abort-job ]" 1>&2
     echo "      (if ErrorPolicy is not set, stop-printer is the CUPS default)" 1>&2
     echo "DirtyCleanInterval [ seconds ] (since CUPS 1.4)" 1>&2
     echo "e.g.: DirtyCleanInterval 30 (default 30 seconds delay until cupsd writes config files)" 1>&2
     echo "      DirtyCleanInterval 0 (update config files like printers.conf almost immediately)" 1>&2
     echo "For the syntax for keywords and values see 'man cupsd.conf'" 1>&2
     echo "and http://www.cups.org/documentation.php/ref-cupsd-conf.html" 1>&2
     echo "In case of ambiguity use the syntax which is described above." 1>&2
     echo "Usually case matters, in particular for special keywords like On Off @LOCAL @IF none all." 1>&2
     echo "Multiple values for a keyword must be separated by space." 1>&2
     exit 1
fi

if ! test -r $CUPSDCONF -a -w $CUPSDCONF
then echo "Cannot read or write $CUPSDCONF." 1>&2
     exit 2
fi

# Remove duplicates (ignore case) and remove duplicate, leading and trailing spaces.
# Case is only ignored if there are duplicates (e.g. 'host.domain.com' and 'Host.Domain.com')
# but if there is e.g. only 'Host.Domain.com' it is written exactly this way to cupsd.conf
# because the user may like to have it exactly in cupsd.conf (even if actually case may not matter):
VALUE="$( for V in $2 ; do echo $V ; done | sort -b -f -u | tr -s '[:space:]' ' ' | sed -e 's/ *$//' )"

# Make a backup of /etc/cups/cupsd.conf if it would be changed:
if test -n "$VALUE"
then if rpm -V -f $CUPSDCONF | grep -q "^..5.*$CUPSDCONF\$"
     then # The /etc/cups/cupsd.conf was already changed (RPM tells that the MD5 sum differs):
          if ! cp -p $CUPSDCONF $CUPSDCONF.yast2save
          then echo "Failed to backup $CUPSDCONF as $CUPSDCONF.yast2save" 1>&2
          exit 3
          fi
     else # The /etc/cups/cupsd.conf content is the original from the RPM package:
          if ! cp -p $CUPSDCONF $CUPSDCONF.yast2orig
          then echo "Failed to backup $CUPSDCONF as $CUPSDCONF.yast2orig" 1>&2
          exit 3
          fi
     fi
     # Log the current change:
     echo "# $(date '+%F,%T') $MY_NAME $KEY $VALUE" >>$CUPSDCONF
fi

# Function to deal with 'Browsing [ On | Off ]'
Browsing()
{ if test -n "$VALUE"
  then # Try to substitute an existing active Browsing line (ignore case).
       # (If there is more than one active Browsing line it is a broken config file):
       sed -i -e "s/^[[:space:]]*$KEY.*/$KEY $VALUE/i" $CUPSDCONF
       # There may exist no Browsing line or the above substitute may have failed
       # (therefore it tests not only for "^$KEY" but for "^$KEY $VALUE$"):
       if ! grep -q "^$KEY $VALUE$" $CUPSDCONF
       then # Append a Browsing line below the last active 'Brows' line (ignore case)
            # to have the new Browsing line at the matching place
            # but if no 'Brows' line exists, append below the first empty line
            # which is usually the line after the initial comment block
            # and if even no empty line exists, append at the end of the file:
            LAST_BROWSE_LINE_NUMBER="$( sed -n -e '/^[^#]*Brows/I=' $CUPSDCONF | tail -n 1 )"
            if test -n "$LAST_BROWSE_LINE_NUMBER"
            then sed -i -e "${LAST_BROWSE_LINE_NUMBER}a$KEY $VALUE" $CUPSDCONF
            else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
                 if test -n "$FIRST_EMPTY_LINE_NUMBER"
                 then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}a$KEY $VALUE\n" $CUPSDCONF
                 else echo -en "\n$KEY $VALUE\n\n" >>$CUPSDCONF
                 fi
            fi
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active Browsing entries
  # are found if there is more than one which is a broken config.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE" -a "$RESULT" != "$VALUE"
  then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
       exit 5
  fi
}

# Function to deal with 'BrowseAllow [ all | @LOCAL | host-address | network-address/netmask ] | BrowseAllow none'
BrowseAllow()
{ if test -n "$VALUE"
  then # Set 'BrowseOrder allow,deny' to deny browse packets by default
       # and then allow them from certain sources via BrowseAllow entries
       # and finally deny from certain sources via BrowseDeny entries.
       # All existing active BrowseOrder lines are removed (ignore case).
       sed -i -e '/^[[:space:]]*BrowseOrder.*/Id' $CUPSDCONF
       # Append the new BrowseOrder line below the last active 'Brows' line (ignore case)
       # to have the new BrowseOrder line at the matching place
       # but if no 'Brows' line exists, append below the first empty line
       # which is usually the line after the initial comment block
       # and if even no empty line exists, append at the end of the file:
       LAST_BROWSE_LINE_NUMBER="$( sed -n -e '/^[^#]*Brows/I=' $CUPSDCONF | tail -n 1 )"
       if test -n "$LAST_BROWSE_LINE_NUMBER" 
       then sed -i -e "${LAST_BROWSE_LINE_NUMBER}aBrowseOrder allow,deny" $CUPSDCONF
       else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            if test -n "$FIRST_EMPTY_LINE_NUMBER"
            then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}aBrowseOrder allow,deny\n" $CUPSDCONF
            else echo -en "\nBrowseOrder allow,deny\n\n" >>$CUPSDCONF
            fi
       fi
       # All existing active BrowseAllow lines are removed (ignore case).
       # Those lines can be removed because YaST supports BrowseAllow lines
       # (YaST shows the BrowseAllow entries in the "printing via network" dialog):
       sed -i -e '/^[[:space:]]*BrowseAllow.*/Id' $CUPSDCONF
       # All active 'BrowseDeny all' lines are removed (ignore case).
       # Such a line was added when the VALUE was "none", see below:
       sed -i -e '/^[[:space:]]*BrowseDeny[[:space:]]*all/Id' $CUPSDCONF
       # All remaining active BrowseDeny lines are deactivated (ignore case).
       # Those lines are not removed because YaST does not support BrowseDeny lines
       # so that a remove would silently delete BrowseDeny information:
       sed -i -e 's/^[[:space:]]*\(BrowseDeny.*\)$/#\1/i' $CUPSDCONF
       # Insert BrowseAllow lines before the BrowseOrder line
       # to have the new BrowseAllow lines at the matching place
       # and in the ordering of the values (needed for the test below):
       if ! test "none" = "$VALUE"
       then for V in $VALUE
            do test -n "$V" && sed -i -e "/^BrowseOrder /i$KEY $V" $CUPSDCONF
            done
       else # The 'BrowseOrder allow,deny' line denies browse packets by default
            # to be 100% on the safe side have explicite 'BrowseAllow none' and 'BrowseDeny all' lines too:
            sed -i -e "/^BrowseOrder /aBrowseAllow none\nBrowseDeny all" $CUPSDCONF
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active BrowseAllow entries
  # are found if there is more than one which is allowed.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | sed -e 's/from//I' | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE" -a "$RESULT" != "$VALUE"
  then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
       exit 5
  fi
}

# Function to deal with 'BrowsePoll [ host-address ] | BrowsePoll none'
BrowsePoll()
{ if test -n "$VALUE"
  then # All existing active BrowsePoll lines are removed (ignore case).
       # Those lines can be removed because YaST supports BrowsePoll lines
       # (YaST shows BrowsePoll entries in the "printing via network" dialog):
       sed -i -e '/^[[:space:]]*BrowsePoll.*/Id' $CUPSDCONF
       if ! test "none" = "$VALUE"
       then # Append the new BrowsePoll lines below the last active 'Brows' line (ignore case)
            # to have the new BrowsePoll lines at the matching place
            # but if no 'Brows' line exists, append below the first empty line
            # which is usually the line after the initial comment block
            # and if even no empty line exists, append at the end of the file.
            # Invert the ordering of the values to get the lines
            # in the original ordering of the values (needed for the test below):
            LAST_BROWSE_LINE_NUMBER="$( sed -n -e '/^[^#]*Brows/I=' $CUPSDCONF | tail -n 1 )"
            FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            APPEND_LINE_NUMBER=""
            if test -n "$LAST_BROWSE_LINE_NUMBER"
            then APPEND_LINE_NUMBER="$LAST_BROWSE_LINE_NUMBER"
            else if test -n "$FIRST_EMPTY_LINE_NUMBER"
                 then APPEND_LINE_NUMBER="$FIRST_EMPTY_LINE_NUMBER"
                 fi
            fi
            for V in $( echo "$VALUE" | tac -s ' ' )
            do if test -n "$APPEND_LINE_NUMBER"
               then test -n "$V" && sed -i -e "${APPEND_LINE_NUMBER}a$KEY $V" $CUPSDCONF
               else test -n "$V" && echo -en "\n$KEY $V\n" >>$CUPSDCONF
               fi
            done
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active BrowsePoll entries
  # are found if there is more than one which is allowed.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE"
  then if test "none" = "$VALUE"
       then RESULT="$( grep -q -i '^[[:space:]]BrowsePoll' $CUPSDCONF || echo -n 'none' )"
       fi
       if test "$RESULT" != "$VALUE"
       then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
            exit 5
       fi
  fi
}

# Function to deal with 'Listen [ all | network-address ] | Listen localhost'
Listen()
{ if test -n "$VALUE"
  then # Determine if it listens on the domain socket:
       LISTEN_DOMAIN_SOCKET_LINE_CONTENT="$( grep -i '^[[:space:]]*Listen.*/cups.sock' $CUPSDCONF | head -n 1 )"
       # All existing active Port lines are deactivated (ignore case).
       # Those lines are not removed because YaST does not support Port lines
       # so that a remove would silently delete Port information:
       sed -i -e 's/^[[:space:]]*\(Port.*\)$/#\1/i' $CUPSDCONF
       # Determine the first active 'Listen' line (ignore case):
       FIRST_LISTEN_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*Listen/I=' $CUPSDCONF | head -n 1 )"
       # All existing active Listen lines are removed (ignore case).
       # Those lines can be removed because YaST supports Listen lines
       # (YaST shows the Listen entries in the "share printers" dialog):
       sed -i -e '/^[[:space:]]*Listen.*/Id' $CUPSDCONF
       # Insert the mandatory 'Listen localhost' line (see http://www.cups.org/str.php?L2834
       # "What is *not* supported is a configuration where only a domain socket is enabled")
       # to have the new Listen line where the first active Listen line was
       # but if no 'Listen' line exists, append below the first empty line
       # which is usually the line after the initial comment block
       # and if even no empty line exists, append at the end of the file:
       if test -n "$FIRST_LISTEN_LINE_NUMBER"
       then sed -i -e "${FIRST_LISTEN_LINE_NUMBER}iListen localhost:631" $CUPSDCONF
       else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            if test -n "$FIRST_EMPTY_LINE_NUMBER"
            then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}iListen localhost:631\n" $CUPSDCONF
            else echo -en "\nListen localhost:631\n\n" >>$CUPSDCONF
            fi
       fi
       # Append a Listen line for the domain socket (if such a line was there):
       if test -n "$LISTEN_DOMAIN_SOCKET_LINE_CONTENT"
       then sed -i -e "/^Listen localhost/a$LISTEN_DOMAIN_SOCKET_LINE_CONTENT" $CUPSDCONF
       fi
       # Insert Listen lines before the 'Listen localhost' line
       # to have the new Listen lines at the matching place
       # and in the ordering of the values (needed for the test below):
       for V in $VALUE
       do if test "all" = "$V"
          then V="*"
          fi
          if ! test "localhost" = "$V"
          then test -n "$V" && sed -i -e "/^Listen localhost/i$KEY $V:631" $CUPSDCONF
          fi
       done
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active Listen entries
  # are found if there is more than one which is allowed.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  # Remove '/var/run/cups/cups.sock' because it is only
  # an optional default (i.e. not really of interest).
  # Unify a localhost (ignore case) or 127.0.0.1 value to "localhost".
  # Replace the actual value '*' by the more meaningful word "all".
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | sed -e 's/localhost/localhost/i' -e 's/127\.0*0\.0*0\.0*1/localhost/' -e 's/[^ ]*\/cups\.sock//' -e 's/:631//' -e 's/\*/all/' | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE"
  then if ! test "localhost" = "$VALUE"
       then SPECIFIC_RESULT="$( echo "$RESULT" | sed -e 's/localhost.*//' -e 's/ *$//' )"
       else SPECIFIC_RESULT="$RESULT"
       fi
       if test "$SPECIFIC_RESULT" != "$VALUE"
       then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
            exit 5
       fi
  fi
}

# Function to deal with 'Allow [ all | @LOCAL | @IF(name) | host-address | network-address/netmask ] | Allow none'
Allow()
{ if test -n "$VALUE"
  then # Remove '127.0.0.2' from the value because it is set by default in any case
       # and it is therefore also removed from the output of the resulting setting.
       VALUE="$( echo $VALUE | sed -e 's/127\.00*\.00*\.0*2//g' | tr -s '[:space:]' ' ' | sed -e 's/^ *//' -e 's/ *$//' )"
       # Determine where the (first) root location starts (ignore case and ignore spaces):
       ROOT_LOCATION_LINE_NUMBER="$( sed -n -e '/[[:space:]]*<[[:space:]]*Location[[:space:]]*\/[[:space:]]*>/I=' $CUPSDCONF | head -n 1 )"
       # Remove all root locations (ignore case and ignore spaces).
       # (If there is more than one root location it is a broken config file).
       # This deletion would remove Deny information and whatever Limit sub-sections
       # but on the other hand it makes sure to have a clean root location:
       sed -i -e '/[[:space:]]*<[[:space:]]*Location[[:space:]]*\/[[:space:]]*>/I,/[[:space:]]*<[[:space:]]*\/[[:space:]]*Location[[:space:]]*>/Id' $CUPSDCONF
       # Insert a new default root location (what we have in our cups RPM by default).
       # Set 'Order allow,deny' in the root location to deny packets by default
       # and then allow them from certain sources via Allow entries
       # and finally deny from certain sources via Deny entries.
       # If no root location was there, append it below the first empty line
       # which is usually the line after the initial comment block
       # and if even no empty line exists, append at the end of the file:
       if test -n "$ROOT_LOCATION_LINE_NUMBER"
       then sed -i -e "${ROOT_LOCATION_LINE_NUMBER}i<Location />\nOrder allow,deny\nAllow 127.0.0.2\n</Location>" $CUPSDCONF
       else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            if test -n "$FIRST_EMPTY_LINE_NUMBER"
            then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}a<Location />\nOrder allow,deny\nAllow 127.0.0.2\n</Location>\n" $CUPSDCONF
            else echo -en "\n<Location />\nOrder allow,deny\nAllow 127.0.0.2\n</Location>\n\n" >>$CUPSDCONF
            fi
       fi
       # Determine (again) where the new root location starts (exact match):
       ROOT_LOCATION_LINE_NUMBER="$( sed -n -e '/<Location \/>/=' $CUPSDCONF | head -n 1 )"
       # Append Allow lines after the '<Location />' line
       # to have the new Allow lines at the matching place
       # but invert the ordering of the values to get the lines
       # in the original ordering of the values (needed for the test below):
       if ! test "none" = "$VALUE"
       then for V in $( echo "$VALUE" | tac -s ' ' )
            do test -n "$V" && sed -i -e "${ROOT_LOCATION_LINE_NUMBER}a$KEY $V" $CUPSDCONF
            done
       else # The 'Order allow,deny' line denies packets by default
            # to be on the safe side have en explicite 'Deny all' line
            # but do not have an additional 'Allow none' line too
            # because an explicite 'Allow none' line results in YaST
            # a "none" entry for the experts settings in the Shareing dialog
            # and such a "none" expert setting overrules any other setting
            # so that all user would have to remove this expert setting
            # manually to allow any kind of remote access.
            sed -i -e "${ROOT_LOCATION_LINE_NUMBER}aDeny all" $CUPSDCONF
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active Allow entries
  # in the root location are found if there is more than one which is allowed.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( sed -n -e '/[[:space:]]*<[[:space:]]*Location[[:space:]]*\/[[:space:]]*>/,/[[:space:]]*<[[:space:]]*\/[[:space:]]*Location[[:space:]]*>/p' $CUPSDCONF | grep -i "^[[:space:]]*$KEY[[:space:]]" | egrep -v '127.0.0.2' | sed -e 's/from//I' | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE"
  then if test "none" = "$VALUE"
       then EXPECTED_RESULT=""
       else EXPECTED_RESULT="$VALUE"
       fi
       if test "$RESULT" != "$EXPECTED_RESULT"
       then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
            exit 5
       fi
  fi
}

# Function to deal with 'BrowseAddress [ @LOCAL | @IF(name) | host-address | broadcast-address ] | BrowseAddress none'
BrowseAddress()
{ if test -n "$VALUE"
  then # All existing active BrowseAddress lines are removed (ignore case).
       # Those lines can be removed because YaST supports BrowseAddress lines
       # (YaST shows BrowseAddress entries in the "share printers" dialog):
       sed -i -e '/^[[:space:]]*BrowseAddress.*/Id' $CUPSDCONF
       if ! test "none" = "$VALUE"
       then # Append the new BrowseAddress lines below the last active 'Brows' line (ignore case)
            # to have the new BrowseAddress lines at the matching place
            # but if no 'Brows' line exists, append below the first empty line
            # which is usually the line after the initial comment block
            # and if even no empty line exists, append at the end of the file.
            # Invert the ordering of the values to get the lines
            # in the original ordering of the values (needed for the test below):
            LAST_BROWSE_LINE_NUMBER="$( sed -n -e '/^[^#]*Brows/I=' $CUPSDCONF | tail -n 1 )"
            FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            APPEND_LINE_NUMBER=""
            if test -n "$LAST_BROWSE_LINE_NUMBER"
            then APPEND_LINE_NUMBER="$LAST_BROWSE_LINE_NUMBER"
            else if test -n "$FIRST_EMPTY_LINE_NUMBER"
                 then APPEND_LINE_NUMBER="$FIRST_EMPTY_LINE_NUMBER"
                 fi
            fi
            for V in $( echo "$VALUE" | tac -s ' ' )
            do if test -n "$APPEND_LINE_NUMBER"
               then test -n "$V" && sed -i -e "${APPEND_LINE_NUMBER}a$KEY $V" $CUPSDCONF
               else test -n "$V" && echo -en "\n$KEY $V\n" >>$CUPSDCONF
               fi
            done
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active BrowseAddress entries
  # are found if there is more than one which is allowed.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE"
  then if test "none" = "$VALUE"
       then RESULT="$( grep -q -i '^[[:space:]]BrowseAddress' $CUPSDCONF || echo -n 'none' )"
       fi
       if test "$RESULT" != "$VALUE"
       then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
            exit 5
       fi
  fi
}

# Function to report the existing 'Policies'
Policies()
{ # The 'tr ... [:blank:]' makes sure that all active '<Policy policy-name>' entries
  # are found if there is more than one which is allowed.
  # Remove leading spaces and trailing spaces for a nice output.
  RESULT="$( grep -i "^[[:space:]]*<[[:space:]]*Policy[[:space:]]" $CUPSDCONF | tr -d '<>' | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
}

# Function to deal with 'DefaultPolicy [ policy-name ] | DefaultPolicy default'
DefaultPolicy()
{ if test -n "$VALUE"
  then # Try to substitute an existing active DefaultPolicy line (ignore case).
       # (If there is more than one active DefaultPolicy line it is a broken config file):
       sed -i -e "s/^[[:space:]]*$KEY.*/$KEY $VALUE/i" $CUPSDCONF
       # There may exist no DefaultPolicy line or the above substitute may have failed
       # (therefore it tests not only for "^$KEY" but for "^$KEY $VALUE$"):
       if ! grep -q "^$KEY $VALUE$" $CUPSDCONF
       then # Append a DefaultPolicy line below the last active 'Policy' line
            # which is usually a '</Policy>' or a 'ErrorPolicy' line (ignore case)
            # to have the new DefaultPolicy line at the matching place
            # but if no 'Policy' line exists, append below the first empty line
            # which is usually the line after the initial comment block
            # and if even no empty line exists, append at the end of the file:
            LAST_POLICY_LINE_NUMBER="$( sed -n -e '/^[^#]*Policy/I=' $CUPSDCONF | tail -n 1 )"
            if test -n "$LAST_POLICY_LINE_NUMBER"
            then sed -i -e "${LAST_POLICY_LINE_NUMBER}a$KEY $VALUE" $CUPSDCONF
            else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
                 if test -n "$FIRST_EMPTY_LINE_NUMBER"
                 then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}a$KEY $VALUE\n" $CUPSDCONF
                 else echo -en "\n$KEY $VALUE\n\n" >>$CUPSDCONF
                 fi
            fi
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active DefaultPolicy entries
  # are found if there is more than one which is a broken config.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE" -a "$RESULT" != "$VALUE"
  then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
       exit 5
  fi
}

# Function to deal with 'ErrorPolicy [ stop-printer | retry-job | abort-job ]'
ErrorPolicy()
{ if test -n "$VALUE"
  then # Try to substitute an existing active ErrorPolicy line (ignore case).
       # (If there is more than one active ErrorPolicy line it is a broken config file):
       sed -i -e "s/^[[:space:]]*$KEY.*/$KEY $VALUE/i" $CUPSDCONF
       # There may exist no ErrorPolicy line or the above substitute may have failed
       # (therefore it tests not only for "^$KEY" but for "^$KEY $VALUE$"):
       if ! grep -q "^$KEY $VALUE$" $CUPSDCONF
       then # Append a ErrorPolicy line below the last active 'Policy' line
            # which is usually a '</Policy>' or a 'DefaultPolicy' line (ignore case)
            # to have the new ErrorPolicy line at the matching place
            # but if no 'Policy' line exists, append below the first empty line
            # which is usually the line after the initial comment block
            # and if even no empty line exists, append at the end of the file:
            LAST_POLICY_LINE_NUMBER="$( sed -n -e '/^[^#]*Policy/I=' $CUPSDCONF | tail -n 1 )"
            if test -n "$LAST_POLICY_LINE_NUMBER"
            then sed -i -e "${LAST_POLICY_LINE_NUMBER}a$KEY $VALUE" $CUPSDCONF
            else FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
                 if test -n "$FIRST_EMPTY_LINE_NUMBER"
                 then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}a$KEY $VALUE\n" $CUPSDCONF
                 else echo -en "\n$KEY $VALUE\n\n" >>$CUPSDCONF
                 fi
            fi
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active ErrorPolicy entries
  # are found if there is more than one which is a broken config.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2 
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE" -a "$RESULT" != "$VALUE"
  then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
       exit 5
  fi
}

# Function to deal with 'DirtyCleanInterval [ seconds ]'
DirtyCleanInterval()
{ if test -n "$VALUE"
  then # Try to substitute an existing active DirtyCleanInterval line (ignore case).
       # (If there is more than one active DirtyCleanInterval line it is a broken config file):
       sed -i -e "s/^[[:space:]]*$KEY.*/$KEY $VALUE/i" $CUPSDCONF
       # There may exist no DirtyCleanInterval line or the above substitute may have failed
       # (therefore it tests not only for "^$KEY" but for "^$KEY $VALUE$"):
       if ! grep -q "^$KEY $VALUE$" $CUPSDCONF
       then # Append a DirtyCleanInterval line below the first empty line
            # which is usually the line after the initial comment block
            # but if no empty line exists, append at the end of the file:
            FIRST_EMPTY_LINE_NUMBER="$( sed -n -e '/^[[:space:]]*$/=' $CUPSDCONF | head -n 1 )"
            if test -n "$FIRST_EMPTY_LINE_NUMBER"
            then sed -i -e "${FIRST_EMPTY_LINE_NUMBER}a$KEY $VALUE\n" $CUPSDCONF
            else echo -en "\n$KEY $VALUE\n\n" >>$CUPSDCONF
            fi
       fi
  fi
  # Report the resulting setting in any case:
  # The 'tr ... [:blank:]' makes sure that all active DirtyCleanInterval entries
  # are found if there is more than one which is a broken config.
  # Remove leading spaces (to cut the right part).
  # Remove trailing spaces (needed for the test below).
  RESULT="$( grep -i "^[[:space:]]*$KEY[[:space:]]" $CUPSDCONF | tr -s '[:blank:]' ' ' | sed -e 's/^ *//' | cut -s -d ' ' -f2 | tr -s '\n' ' ' | sed -e 's/ *$//' )"
  echo -n "$RESULT"
  # For a nicer output on a terminal where stdout and stderr is mixed up,
  # output a '\n' on stderr to get subsequent stuff (e.g. the shell prompt
  # or an error message because of a failed test below) on a new line:
  echo 1>&2
  # Test if the result is the expected one if a value was specified:
  if test -n "$VALUE" -a "$RESULT" != "$VALUE"
  then echo "Failed to set '$KEY $VALUE' in $CUPSDCONF." 1>&2
       exit 5
  fi
}

case "$KEY" in
     Browsing) Browsing;;
     BrowseAllow) BrowseAllow;;
     BrowsePoll) BrowsePoll;;
     Listen) Listen;;
     Allow) Allow;;
     BrowseAddress) BrowseAddress;;
     Policies) Policies;;
     DefaultPolicy) DefaultPolicy;;
     ErrorPolicy) ErrorPolicy;;
     DirtyCleanInterval) DirtyCleanInterval;;
     *) echo "Ignoring keyword '$KEY'" 1>&2
        exit 4 ;;
esac

exit 0

