#!/bin/bash
###################################################
# fou4s - Fast Online Update for SuSE
#
# Copyright (C) 2002-2009 Markus Gaugusch <fou4s@gaugusch.at>
# Many improvements by Lars Ellenberg <l.g.e@web.de>
#
# See man page fou4s(1) for more information or look at http://fou4s.gaugusch.at
#
# SVN info:
# $Author: markus $
# $Date: 2010-10-29 23:00:32 +0200 (Fre, 29 Okt 2010) $
# $Rev: 223 $
#
# Optimized for tabstop length 3 and terminal width ~160 chars
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##################################################

FOUVERSION=0.15.8
[[ $FOUVERSION == *beta* ]] && BETA=beta- # used for self-update of fou4s
[[ $FOUVERSION == *rc* ]] && BETA=beta- # used for self-update of fou4s
# probably need extended globbing for pattern matching
# available at least since suse 6.1 (but maybe not for people running fou4s
# in "server" mode on other unices)
shopt -s extglob \
|| {
		echo "Need extended globbing or some features will fail!"
		echo "Please update to a newer version of bash."
		exit 255
	}
unset LANG # to get sane error messages (necessary for parsing)
unset LC_CTYPE
HOSTNAME=`hostname` # for tcsh, which sets $HOSTNAME to fqdn
WHOAMI=$0
[[ $WHOAMI != /* ]] && WHOAMI="$PWD/${0#./}"
DIRNAME=${WHOAMI%/*}
BASENAME=${WHOAMI##*/}


if [[ $1 == --rightsok ]] || [[ $1 == --ri* ]] ; then
	shift # remove the rightsok parameter from parameter list
elif [[ -x `type -p sg` ]] ; then # only if sg exists - it is missing on some SuSE versions
	groups=`grep ^fou4s: /etc/group`
	# If root or members of group fou4s call fou4s, set group fou4s and
	# make all files group writeable
	if [[ $EUID -eq 0 && -n $groups || $groups != *: ]] ; then
		umask g+rwx
		sg fou4s -c "$0 --rightsok $*" && exit
		[[ $? -eq 1 ]] && echo "You don't have group fou4s in your system, please run 'groupadd fou4s'!" || exit $?
	fi
fi

################################ getSuSEVersion
# get version of the running suse product. Used to get the right
# patches from ftp server.
# Modifies: SUSEVERSION, ARCH, SUSEPRODUCT, NODOTSUSEVERSION
# UsedBy: main
function getSuSEVersion()
{
	local ver dir i
	dir=`dirname $0`
	[[ -O $dir/SuSE-release && -x $dir/SuSE-release ]] && ver=(`$dir/SuSE-release v1.2`) || \
	for i in /usr/bin /usr/local/bin ; do
		[[ -x $i/SuSE-release ]] && ver=(`$i/SuSE-release v1.2`) && break
	done
	SUSEVERSION=${ver[1]}
	NODOTSUSEVERSION=${ver[2]}
	ARCH=${ver[3]}
	SUSEPRODUCT=${ver[4]//\"} # vim syntax helper: "
	KERNELVER=${ver[5]}
	MACHINEARCH=${ver[6]}
	VALIDARCHS=${ver[7]//+/ }
} # getSuSEVersion


# ---------------------------
# global variable definitions
# ---------------------------
getSuSEVersion  # needed for some variables
ACCEPTPREINSTALLINFO=0 # Don't require interactive mode for preinstall info
ALLCOUNT=0      # current index in list of all packages
ALLPKGDESCFILES= # for package description cache
AUTOLIST=       # List of packages to install automatically
AUTOMODE=0      # Automatic installation of selected packages
BASEDIR=$PWD    # current directory, needed to access relative paths (rpmdir,..)
BENCHMARK=0     # do benchmark and use fastest server
CACHEDIR=/var/cache/fou4s # default cache root directory
CHECKDELETED=1  # check for deleted files that are still accessed after update
CHECKONLYDELETED=0  # check for deleted files and exit
CHECKFOU=1      # check for update of fou4s
CHECKONLY=0     # only compare patch descriptions with RPM db
CLEANCACHE=0    # clean update description cache (see --cleancache)
COLOR=1         # use colored output
COLUMNS=80      # width of screen output (default)
COMMONFUNCS=0   # for announcement2pkgdesc to include fou4s functions
COMPATIBLE=1    # be as compatible as possible to YOU
CONFIGFILE=None # default config file (information text only, in this case)
CONFIGFILES="/etc/fou4s.conf $HOME/.fou4s ./.fou4s" # config files, the last one has highest priority
CRONINST=0      # install cronjob
CRONSERVER=0    # install cronjob for server (24h connected)
CRONWORKSTATION=0 # install cronjob for workstation
DAYSOLDER=0     # only show packages older than n days
DLPATH=$CACHEDIR/packages # default path for downloaded RPMs
DOINSTALL=0     # perform installation/download
DORECODE=0      # translate from UTF8 to ISO8859-15
DOWNLOAD=0      # attempt to download files
EXCLUDELIST=    # skip packages of this series (blank seperated)
EXPORTFILE=     # filename for export function
EXPORT=0        # export files to install/download on/for another host
FAILCOUNT=0     # number of failed updates (dependency problems, etc.)
FIXPERMS=0      # Fix permissions of package descr. file on SuSE 8.1 for nonroot
FORCEDARCH=     # arch given on commandline
FORCEINSTALL=0  # force installation of current patch set
FOU4SKEY=AFB66D7C # fou4s build key id
FOUNDFILE=      # return variable for fileExists()
GENERATED=1     # check for generated package descriptions
GET81PACKAGEDESCRIPTIONS=0 # get package description file for 8.1 from ftp
GETALL=0        # download all packages
GETALLNEW=0     # download only packages that are not installed or newer
GETSOURCE=0     # download src.rpm only
GLOBALRPMOPTS=  # global rpm installation options (to be set later)
GLOBALWGETOPTS='--retr-symlinks -np -nd -e robots=off --reject="=D,=A" --passive-ftp --timestamping -t3'
GLOBALWGETOPTS="$GLOBALWGETOPTS --user-agent=fou4s-$FOUVERSION"
GLOBALCURLOPTS="-A fou4s-$FOUVERSION --progress-bar -C - -L"
HTTPPASSWD=     # For SuSE business products
HTTPUSER=       # For SuSE business products
HOSTEXEC=       # used to execute commands at remote hosts
IGNORELIST=     # packages that shouldn't be installed
IMPORT=0        # import files to install/download from/for another host
IMPORTFILE=     # filename for import function
INSTMODE=1      # 0=Every package alone, 1=each patchfile grouped, 2=All at end
INTERACTIVE=0   # interactive mode
KERNELPKG=      # name of package containing kernel
KERNELCHECK=1   # special handling for linux kernel updates
KERNELBACKUP=1  # create backup when doing a kernel update
LANGUAGE=english # for info from update descriptions
LIMIT=0         # no bandwith limits by default
LISTAVAILRPMS=0 # list available RPM files
LOGFILE=/var/log/fou4s.log # logfile that contains all installed updates
LOGLEVEL=1      # Loglevel for logfile, 0=lowest, 3=full debug
NORESUME=0      # Only work with complete files (no continued download)
OFFLINE=0       # Connect to remote host when checking for updates
ONLYINSTALLEDUSERRPMS=0  # only process installed user specified rpms
PACKAGES81=
PATCHDIR=patches # repodata for 10.1
PATCHRPMS=1     # use patch rpm's on SuSE 8.1 if possible
POSTINSTLIST=   # List of patch descriptions containing postinstall info
POSTINFO=0      # show postinstall information (later overwritten for each pkg)
PROXYDIGEST=0   # use digest auth with proxy (enforces curl)
PROXYPASS=      # http proxy password
PROXYUSER=      # http proxy username
QUIET=0         # quiet mode (for cron jobs)
REMARKLIST=     # packages that shouldn't be installed, but remarked if
REMOVE=1        # remove packages after (successful) install
RPMINSTLIST=    # global RPM installation list
RPMEXPORTLIST=  # list of RPM's to be exported to other host
RPMCACHEVER=2.1 # version of the RPM cache file
RWIDTH=22       # width of rpm name field in output
RSYNCSERVER=rsync://ftp.gwdg.de/SuSE/ftp.suse.com/suse/$ARCH #default for gpd.sh
RSYNCOPTS="-t --partial" # global rsync options: timestamps
SAFEMODE=0      # don't check for package signatures and don't run insttriggers
SECURITY=0      # only install security updates
SERVERLIST=     # download server
SKIPGPG=0       # skip GPG verification
SKIPOPTIONAL=0  # skip optional packages
SRCRPMS=        # list of packages that are downloaded as src.rpm
SUSECONFIG=1    # run SuSEconfig after update
TESTMODE=0      # run in testing mode
THREADS=1       # run in multi-threaded mode
UPDATEPACKAGELIST=0 # update list of available updates
UPDATESERVERLIST=0  # update list of ftp servers
UPDATESUSESERVERS=0 # update /etc/suseservers
USEBUILDTIME=0  # don't compare versions, but the build time of the packages
USECACHE=2      # cache for package descriptions
USECURL=0       # use curl instead of wget
USEDELTARPMS=1  # support for delta rpms
USEDIR=0        # use "directory" or "directory.1" files ...
USEFULLPATH=1   # support for structured (a1/aaa_base.rpm) download dir
USEPROGRESS=1   # default, will be reset later after option parsing
USERRPMS=       # user requested RPM file(s)
USERSYNCPROXY=1 # use --proxy value as rsync proxy
VCMP_OK=0       # try to use zypper vcmp if our vcmp fails
VERBOSE=0       # more (debug) output
VERSIONOVERRIDE=0 # this is set to 1, if user overrides detected values
XMLP=           # path to xmlp awk script (filled later)
LASTANSWER=n    # last answer for user input
INVERSE=0       # inverse coloring (for white background terminals)
BRIGHT=1        # bright colors
COUNTER=(0 0 0 0 0 0 0 0 0) # array for counters
SIZES=(0 0 0 0 0 0 0 0 0)   # array for sizes
HDSIZES=(0 0 0 0 0 0 0 0)   # size on hdd
DLSIZES=(0 0 0 0 0 0 0 0)   # size of missing data
_ZERO=0         # packages with zero size
_SECURITY=1     # -"- for security packages
_RECOMMENDED=2  # ... recommended packages
_OPTIONAL=3     # optional packages
_YAST=4         # yast packages
_GENERATED=5    # generated packages
_SCRIPT=6       # scripts
_ALL=7          # packages that need to be updated
_INST=8         # installed packages


# Global variable RETVAL for non $? success/failure return values
# array variable, you could return more than one value
# if refered without index, you get index 0, the default.
RETVAL=()


################################
################################ Functions
################################


################################ myEcho
# write message, but only if debug level is right
# parameters:
# $1 verbosity level [0-4]
# $2 Text to write to stdout if level is ok
function myEcho()
{
	[[ $VERBOSE -lt $1 ]] || echo -e "${@: 2}"
} # myEcho

################################ myWarn
# print warning message, but only if debug level is right
# parameters:
# $1 verbosity level [0-4]
# $2 Text to write to stdout if level is ok
function myWarn()
{
	echo -en ${COL_YELLOW}
	myEcho "$@"
	echo -en ${COL_NORM}
} # myWarn


################################ myError
# print error message, but only if debug level is right
# parameters:
# $1 verbosity level [0-4]
# $2 Text to write to stdout if level is ok
function myError()
{
	echo -en ${COL_RED}
	myEcho "$@"
	echo -en ${COL_NORM}
} # myError


################################ myNote
# print notification message, but only if debug level is right
# parameters:
# $1 verbosity level [0-4]
# $2 Text to write to stdout if level is ok
function myNote()
{
	echo -en ${COL_CYAN}
	myEcho "$@"
	echo -en ${COL_NORM}
} # myNote


################################ myRead
# Like bash "read", but simplifies answer (Y/Yes->y, ...)
# parameters are passed to bash read command, but prompt
# must always be given. The answer is returned in $answer
function myRead()
{
		eval read -p '"$*"' answer
		case $answer in
			Y|yes|Yes|YES) eval answer=y ;;
			N|no|No|NO) eval answer=n ;;
			S|skip) eval answer=n ;;
		esac
}


################################ logWrite
# Appends some text to $LOGFILE with timestamp
# Parameters:
# $1: loglevel (like $VERBOSE)
# rest: text
function logWrite()
{
	[[ $LOGLEVEL -lt $1 ]] && return
	shift
	local time=`date "+%Y-%m-%d %H:%M:%S"`
	echo $time $* >> $LOGFILE
} # logWrite


################################ checkLanguage
# checks if the given text is nonzero and changes language to english if it is
function checkLanguage()
{
	[[ -z $1 ]] && return 1
	return 0
}

################################ showUsageShort
# short overview about fou4s options
# UsedBy: checkParameters
function showUsageShort()
{
	cat << _EOF
fou4s v$FOUVERSION  (c) 2002-2009 Markus Gaugusch <fou4s@gaugusch.at>
usage: fou4s -u [-bqv] [--checkfou4s] [--proxyuser]
                [--proxypasswd]
       fou4s -e [--all] [-bdosv] [--proxyuser] [--proxypasswd]
       fou4s --auto [-nosv]
       fou4s -i [-abcdgnorsv] [-f file] [--interactive] [--nodeps]
                [--proxyuser] [--proxypasswd]
       fou4s -l [-os]
       fou4s --server [-vw] [--proxyuser] [--proxypasswd]
       fou4s --checkdeleted
       fou4s --fixperm
Type fou4s --help for more information.
See the manual page fou4s(1) for detailed help.
_EOF
	exit 0
} # showUsageShort


################################ showUsage
# show long usage information and exit
# UsedBy: checkParameters
function showUsage()
{
	cat << _EOF
fou4s v$FOUVERSION  (c) 2002-2009 Markus Gaugusch <fou4s@gaugusch.at>
usage: fou4s [task] [options]
task:
-u, --update     Get new package list from FTP server and exit
-i, --install    Compare package list with RPM db and get/install packages
--all            Process all available packages, no matter if they are needed
--allnew         Process packages that are newer or not installed yet
--auto           Works like -ued, plus auto installation of specific packages
-e, --evaluate   Evaluate, if new updates should be installed (no dl/install)
-l, --list       List available RPMs (for use with -f)
--server         Select ftp server to use (interactive)
--checkdeleted   Check for deleted files that are still in use and exit
--fixperm        Fix package descr. file read permissions on 8.1 for group fou4s
options:
-a               Install every package immediately after downloading (see --end)
--arch           Override architecture autodetect (get packages for other arch)
-b, --benchmark  Do benchmark (fou4s-benchmark) and use fastest server
--buildtime      Compare buildtime of packages instead of version number
-c               Do not run SuSEconfig after update (requires -i)
--config cfgfile Use "cfgfile" as configuration file. Must be the first option
--checkfou4s     Check for an updated version of fou4s (requires -u)
-d               Check packages and download RPMs (no installation)
--end            Install all updates at the end, not when listing them (see -a)
--exclude x      Don't check patch description 'x' or series 'x' (wildcards ok)
-f custom.rpm    Try to process the given file(s). -l shows available RPMs
--force          Force RPM updates. (DANGEROUS!!)
-g, --nogpg      Skip GPG signature verification (beware!)
-h               Short help
--help           Show this help screen
--interactive    Interactive mode (ask before downloading/installing a package)
--inversecolor   To see color on black-on-white terminals
--language x     Use the given language for descriptions (e.g. german or french)
--limit-rate x   Limit downloads to x kb/sec
-n, --nodownload Do not attempt to download RPM files
--nocache        Do not use the update description cache
--nocheckdeleted Do not check for deleted files still in use at end of run
--nocolor        Do not use colored output
--nocompatible   Don't try to be compatible to YOU. See man page fou4s(1)
--nodeps         Don't make RPM dependency checks (DANGEROUS!)
--nodeltarpms    Don't use delta RPMs if possible (only SuSE 9.2+)
--nopatchrpms    Don't use patch RPMs if possible
--nogenerated    Don't use generated package descriptions
--noproxy        Don't use http_proxy/ftp_proxy (from environment var.)
-o               Skip optional packages (process everything else)
--older-than x   Only show packages older than x days
--only           Only process specified RPMs, see -f
--product x      Update for SuSE Business product (e.g. eMail-Server)
--proxy x        Use "x" as proxy server. Format is http://host:port/
--proxydigest    Use digest authentication with proxy (curl only!)
--proxyuser user Use username "user" for HTTP proxy (if required)
--proxypasswd pw Use password "pw" for HTTP proxy (if required)
-q, --quiet      Quiet mode (for cron-jobs)
-r, --remove     Remove downloaded packages after successful update
-s, --security   Only process security updates (nothing else)
--safemode       Don't check signatures and disable install triggers (faster)
--src rpm1,...   Download source RPM for given packages
--suseuser x     Username for http auth (for business products)
--susepasswd x   Password for http auth (for business products)
--suseversion    Override version autodetect (get packages for other SuSE rel.)
--usecurl        Use curl instead of wget
--usedir         Use directory.X files from YOU (see fou4s(1))
-v, --verbose    Verbose mode (twice for even more verbosity)
--version        Show version information
-w               Update /etc/suseservers (only valid with --server)
_EOF
# --noresume       Don't resume downloads (delete old files first)
	exit 0
} # showUsage


################################ checkParameters
# processes fou4s command line parameters
# UsedBy: main
# Modifies: a lot
# parameters:
# $*: command line parameters to fou4s
function checkParameters()
{
	while [[ $# -gt 0 ]] ; do # parameter checking
		case "$1" in
			-a)
				INSTMODE=0
				shift ;;
			--acceptpreinstallinfo)
				ACCEPTPREINSTALLINFO=1
				shift ;;
			--all)
				PATCHRPMS=0 # don't use patch rpms when getting all packages
				GETALL=1
				[[ $USECACHE -ge 2 ]] && USECACHE=1 # only use level-1 cache (level-2 is invalid)
				shift ;;
			--allnew)
				PATCHRPMS=0 # don't use patch rpms when getting all packages
				GETALL=1
				GETALLNEW=1
				[[ $USECACHE -ge 2 ]] && USECACHE=1 # only use level-1 cache (level-2 is invalid)
				shift ;;
			--arch)
				ARCH=$2
				MACHINEARCH=$2
				FORCEDARCH=$2
				shift 2 ;;
			--auto)
				DOINSTALL=1 # installs only auto mode packages
				QUIET=1   # quiet is for -u (not used anywhere else)
				VERBOSE=1 # verbose is for -e (show update descriptions)
				AUTOMODE=1 UPDATEPACKAGELIST=1 DOWNLOAD=1
				shift ;;
			-b|--benchmark)
				BENCHMARK=1
				shift ;;
			--buildtime)
				USEBUILDTIME=1
				shift ;;
			-c)
				SUSECONFIG=0
				shift ;;
			--checkdeleted)
				CHECKONLYDELETED=1
				shift ;;
			--checkfou4s)
				CHECKFOU=1
				shift ;;
			--cleancache)
				CLEANCACHE=1
				shift ;;
			--commonfuncs)
				COMMONFUNCS=1
				shift ;;
			--cronserver)
				CRONINST=1
				CRONSERVER=1
				shift ;;
			--cronworkstation)
				CRONINST=1
				CRONWORKSTATION=1
				shift ;;
			-d)
				DOWNLOAD=1
				shift ;;
			-e|--evaluate)
				CHECKONLY=1
				shift ;;
			--end)
				INSTMODE=2
				shift ;;
			--exclude)
				EXCLUDELIST="$EXCLUDELIST $2" # multiple --exclude's should be legal
				shift 2 ;;
			--exportx) # the appended x is a hack for broken vim colors in getopt
				EXPORTFILE=$2
				EXPORT=1
				DOINSTALL=1 # is skipped later, but needed for storing in tar file
				DOWNLOAD=1
				SUSECONFIG=0
				USEDELTARPMS=0 # delta rpms don't work for remote machines
				shift 2 ;;
			-f)
				USERRPMS="$USERRPMS $2"
				shift 2 ;;
			--fixperm)
				FIXPERMS=1
				shift ;;
			--force)
				GLOBALRPMOPTS="$GLOBALRPMOPTS --force"
				shift ;;
			-g|--nogpg)
				SKIPGPG=1
				shift ;;
			--getpackagedescriptions)
				GET81PACKAGEDESCRIPTIONS=1
				shift ;;
			-h)
				showUsageShort ;; # no shift necessary, function makes exit
			--help)
				showUsage ;; # no shift necessary, function makes exit
			--host)
				HOSTNAME=${2##*@}
				HOSTEXEC="ssh $2"
				CHECKDELETED=0 # cannot check for deleted files on remote servers!
				USEDELTARPMS=0 # delta rpms don't work for remote machines
				shift 2 ;;
			-i|--install|--upgrade)
				DOINSTALL=1 DOWNLOAD=1 CHECKONLY=0
				shift ;;
			--import)
				IMPORTFILE=$2
				IMPORT=1
				shift 2 ;;
			--interactive)
				INTERACTIVE=1
				shift ;;
			--inversecolor)
				INVERSE=1
				shift ;;
			-l|--list)
				LISTAVAILRPMS=1
				USECACHE=1
				USEPROGRESS=0
				shift ;;
			--language)
				LANGUAGE=$2
				shift 2 ;;
			--limit-rate)
				LIMIT=$2
				shift 2 ;;
			-n|--nodownload)
				DOWNLOAD=0
				[[ $AUTOMODE -eq 0 ]] && UPDATEPACKAGELIST=0
				shift ;;
			--nocache)
				USECACHE=$((USECACHE-1))
				[[ $USECACHE -lt 0 ]] && USECACHE=0
				shift ;;
			--nocheckdeleted)
				CHECKDELETED=0
				shift ;;
			--nocolor)
				COLOR=0
				shift ;;
			--nocompatible)
				COMPATIBLE=0
				shift ;;
			--nodeps)
				GLOBALRPMOPTS="$GLOBALRPMOPTS --nodeps"
				shift ;;
			--nodeltarpms)
				USEDELTARPMS=0
				shift ;;
			--nopatchrpms)
				PATCHRPMS=0
				shift ;;
			--noprogress)
				USEPROGRESS=0
				shift ;;
			--noresume)
				NORESUME=1
				shift ;;
			--nogenerated)
				GENERATED=0
				shift ;;
			--noproxy)
				unset http_proxy
				unset ftp_proxy
				shift ;;
			-o)
				SKIPOPTIONAL=1
				shift ;;
			--offline)
				OFFLINE=1
				SUSEVERSION="*[0-9]"
				shift ;;
			--older-than)
				if [[ $2 -ge 1 && $2 -lt 10000 ]] ; then
					DAYSOLDER=$2
				else
					myError 0 "Invalid number of days: $2"
					exit 2 
				fi
				shift 2 ;;
			--only)
				ONLYINSTALLEDUSERRPMS=1
				shift ;;
			--product)
				SUSEPRODUCT=$2
				VERSIONOVERRIDE=1
				shift 2 ;;
			--proxy)
				if [[ $2 == http://*([^\/])/ ]] ; then
					export http_proxy=$2
					export https_proxy=$2
					export ftp_proxy=$2
					if [[ $USERSYNCPROXY -eq 1 ]] ; then
						RSYNC_PROXY=$2
						RSYNC_PROXY=${RSYNC_PROXY##*://}
						export RSYNC_PROXY=${RSYNC_PROXY%/}
					fi
				else
					echo "Proxy must start with http:// and end with / (e.g. http://proxy:8080/)"
					exit 2
				fi
				shift 2 ;;
			--proxy-digest|--proxydigest)
				PROXYDIGEST=1
				shift ;;
			--proxyuser)
				PROXYUSER=$2
				shift 2 ;;
			--proxypasswd)
				PROXYPASS=$2
				shift 2 ;;
			-q|--quiet)
				VERBOSE=0 QUIET=1
				shift ;;
			-r|--remove)
				REMOVE=1
				shift ;;
			-s|--security)
				SECURITY=1
				GENERATED=0 # speed up checking a little bit
				shift ;;
			--safemode)
				SAFEMODE=1
				shift ;;
			--server)
				UPDATESERVERLIST=1
				shift ;;
			--src|--source)
				SRCRPMS=$2
				shift 2 ;;
			--suseuser)
				HTTPUSER=$2
				shift 2 ;;
			--susepasswd|susepassword)
				HTTPPASSWD=$2
				shift 2 ;;
			--suseversion)
				SUSEVERSION=$2
				NODOTSUSEVERSION=${SUSEVERSION//./}
				VERSIONOVERRIDE=1
				shift 2 ;;
			--testmode)
				TESTMODE=1
				shift ;;
			-u|--update)
				UPDATEPACKAGELIST=1
				shift ;;
			--usecurl|--use-curl)
				USECURL=1
				USEDIR=1
				shift;;
			--usedir)
				USEDIR=1
				shift ;;
			-v|--verbose)
				QUIET=0 VERBOSE=$((VERBOSE+1))
				shift ;;
			--version)
				echo "fou4s v$FOUVERSION  (c) 2002-2009 Markus Gaugusch <fou4s@gaugusch.at>"
				exit ;;
			-w)
				UPDATESUSESERVERS=1
				shift ;;
			--)
				shift # skip "--"
				USERRPMS="$USERRPMS $*" # eat up all remaining params
				[[ $# -ge 1 && $ONLYINSTALLEDUSERRPMS -eq 0 && $USECACHE -eq 2 ]] \
					&& USECACHE=1 
					# level 2 cache is invalid with user specified rpms
				set -- # set them to null to be explicit
				break ;;
			*)
				myError 0 "Error: Unkown option: $1 (use -h for help)"
				exit 3 ;;
		esac
	done
} # checkParameters


################################ checkOption
# Verifies one config file option
# Parameters:
# $1 Value from config file
# $2 Option name
# $3 original value
# $4 If it is "bool", only 0/no/false or 1/yes/true is valid
#    If it is "range", parameters $4/$5 are used
# $5 start range
# $6 end range
function checkOption()
{
	local optionvalue="$1" option="$2" defaultvalue="$3" type="$4"
	local rangestart="$5" rangeend="$6"
	if [[ $type == bool ]] ; then
		case "$optionvalue" in
		0|false|no|off)
			echo 0
			myEcho 3 Found bool option $option with value 0 >&2 ;;
		1|true|yes|on)
			echo 1
			myEcho 3 Found bool option $option with value 1 >&2 ;;
		*)
			echo "$defaultvalue"
			myError 0 "ERROR: Invalid value for $option in config file $CONFIGFILE." >&2
			myError 0 "Value must be 0/off or 1/on. Using default $defaultvalue" >&2
		esac
	elif [[ $type == range ]] ; then
		if [[ $optionvalue -ge $rangestart && $optionvalue -le $rangeend ]] ; then
		myEcho 3 "Found range option $option with value $optionvalue" >&2
			echo "$optionvalue"
		else
			echo "$defaultvalue"
			myError 0 "ERROR: Invalid value for $option in config file $CONFIGFILE." >&2
			myError 0 "Value must be $rangestart..$rangeend, Using default $defaultvalue" >&2
		fi
	else
		echo "$optionvalue"
		myEcho 3 "Found option $option with value(s)" $optionvalue >&2
	fi
} # checkOption


################################ fixperms
# Fixes permissions of package description file on SuSE 8.1+ for group fou4s
function fixperms()
{
	local answer allow=0 dir
	if [[ $EUID -ne 0 ]] ; then
		myError 0 "Error: you must be root to fix permissions!"
		exit 4
	fi
	echo -e $COL_YELLOW
cat << _EOF
Allowing fou4s group members to access the description file is not considered
to be harmful to the system security in any way. It just allows fou4s to be run
by a non-root user to download packages. Answering no to the following question
will remove the rights.
_EOF
	echo -e $COL_NORM
	myRead "Allow group fou4s access to the description file (y/n)?"
	if [[ $answer == y ]] ; then
		if [[ `grep ^fou4s: /etc/group` != fou4s:* ]] ; then
			myRead "Group fou4s does not exist, add it (y/n)?"
			[[ $answer != y ]] && echo Aborting ... && return
			groupadd fou4s
			passwd -g fou4s -r # remove group password
		fi
		echo "Allowing group fou4s members to access to $PACKAGES81 ..." | fmt -$COLUMNS
		allow=1
	else
		echo "Removing rights to access package description file ..."
	fi
	cd /var/adm
	for dir in YaST InstSrcManager ; do
		if [[ $allow -eq 1 ]] ; then
			mode=g+x
			group=fou4s
		else
			mode=g-x
			group=root
		fi
		chgrp $group YaST YaST/InstSrcManager
		chmod $mode YaST YaST/InstSrcManager
		cd YaST/InstSrcManager
		chgrp $group IS_CACHE_0x* IS_CACHE/0x*/DATA IS_CACHE/0x*/DATA/descr
		chmod $mode IS_CACHE_0x* IS_CACHE/0x*/DATA IS_CACHE/0x*/DATA/descr
		chgrp $group $PACKAGES81
	done
	exit 0
} # fixperms

################################ installCronjob
# Copy example cronjob to /etc/cron*, depending on workstation or server
# config to cron.daily or cron.d
function installCronjob()
{
	local cronfile time crondest ok=0 dirs="examples /usr/share/doc/packages/fou4s ."
	local hour=25 min=99
	if [[ $EUID -ne 0 ]] ; then
		myError 0 "ERROR: You must run fou4s as root!"
		exit 2
	fi
	umask g-w # for safety - fou4s members should not edit cronjobs!
	for dir in $dirs ; do
		if [[ -f $dir/fou4s-crontab ]] ; then
			oldjob=/etc/cron.d/*fou4s*tab
			for f in $oldjob ; do
				[[ -f $f ]] && myWarn 0 "Warning: Old cron job detected: $f"
			done
			if [[ $CRONSERVER -eq 1 ]] ; then
				while [ $ok -eq 0 ] ; do
					echo -n "Please enter time for update checking (e.g. 3:43): "
					read time
					time=${time//[^0-9:]/}
					hour=${time%%:*}
					min=${time##*:}
					[[ -n $min && -n $hour && $hour -ge 0 && $hour -le 23 && \
						$min -ge 0 && $time = *:* && $min -le 59 ]] || continue
					ok=1
					mmin=$((min+10))
					mhour=$hour
					if [[ $min -ge 50 ]] ; then
						mhour=$((mhour+1))
						[[ $mhour -gt 23 ]] && mhour=0
						mmin=$((min+10-60))
					fi
				done
				if [[ -f /etc/cron.d/fou4s ]] ; then
					myWarn 0 -n "File /etc/cron.d/fou4s already exists - overwrite (y/n)? "
					myRead ""
					[[ $answer != y ]] && echo "Not updating crontab ..." && break
				fi
				cat $dir/fou4s-crontab | sed "s/^5 6/$min $hour/g" | \
					sed "s/^17 6/$mmin $mhour/g" > /etc/cron.d/fou4s
			else # workstation
				for c in daily monthly ; do
					if [[ -f /etc/cron.$c/fou4s ]] ; then
						myWarn 0 -n "File /etc/cron.$c/fou4s already exists - overwrite (y/n)? "
						myRead ""
						[[ $answer != y ]] && continue
					fi
					cp $dir/fou4s-crontab.workstation.$c /etc/cron.$c/fou4s
					chmod +x /etc/cron.$c/fou4s
				done
			fi
			ok=1
			myNote 0 "Crontab installation finished! Fou4s will now check for updates and download them automatically every day. Notifications about new updates are sent as mail to root." | fmt -$COLUMNS
			break
		fi
	done
	if [[ $ok -eq 0 ]] ; then
		echo "Could not find cronjob templates in one of the follwing directories:"
		echo $dirs
	fi
	exit 0
} # installCronjob

################################ get81packagedescriptions
# Get "packages" file for suse 8.1+. It contains architecture information
# for packages that are not installed.
# This is needed, because the new update descriptions of 8.1+ don't
# contain arch information!
function get81packagedescriptions()
{
	local server
	for server in $SERVERLIST ; do
		if [[ $server == *tp://* ]] && \
			[[ $server != *$ARCH/update/$SUSEPRODUCT$SUSEVERSION* ]] ; then
			echo Getting package description file from $server ... | fmt -80
			getremotefile --output-document $CACHEDIR/descr_packages.$SUSEVERSION $server/$ARCH/$SUSEVERSION/suse/setup/descr/packages
			break
		fi
	done
	exit 0
} # get81packagedescriptions


################################ showProgress
# Shows a progress bar and a percentage value
# UsedBy: checkPackage, processServerList
# Parameters:
# $1: index
# $2: max
# $3: special:
#     if 1, then show bar with 100%
#     if 2, then show bar filled with "o", not "#"
function showProgress()
{
	local index=$1 max=$2 len special=$3 bar bl restlen
	local percent=$((index*100/max))
	[[ $special == 1 ]] && percent=100
	bl="................................." # blank bar
	bar="#################################" # filled bar
	if [[ $NODOTSUSEVERSION -ge 110 ]] ; then
		if [[ $3 == 2 ]] ; then
			 bar="ooooooooooooooooooooooooooooooooo" # pass 1 bar
			 pass="1/2"
		else
			 bl="ooooooooooooooooooooooooooooooooo" # pass 2 bar
			 pass="2/2"
		fi
	fi
	len=$((percent/3))
	restlen=$((33-len))
	[[ $restlen -lt 0 ]] && restlen=0
	echo -n -e "$server: Checking $pass [${bar:0:$len}${bl:0:$restlen}] $percent %   \r"
	[[ $special == 1 ]] && echo # go to next line when forcing 100% ...
} # showProgress


################################# myRecode
# SuSE update descriptions are UTF8 encoded. Usual Linux console
# and xterms are not configured to UTF8. Therefore we show it in
# ISO-8859-15 (western european, with euro symbol) charset
function myRecode()
{
	[[ $DORECODE -eq 1 ]] && recode UTF8..ISO-8859-15 
	tr '' '\n' | tr '' '='
	if [[ $NODOTSUSEVERSION -ge 110 ]] ; then
		sed 's/&lt;/</g' 
		sed 's/&gt;/>/g' 
		sed 's/&amp;/&/g' 
	fi
}


################################ getremotefile
# Calls wget and deletes unnecessary index.html
# Parameters:
# $*: wget parameters
function getremotefile()
{
	local f ret params user pass verbose=0 cont=0 cache=1 url opts quiet=0 recursive=0 bar=0
	local outfile
	params=`getopt -l user:,pass:,output-document:,verbose,bar,continue,nocache,quiet,recursive -o u:p:o:vbcnqr -n 'getremotefile' -- "$@"`
	myEcho 2 "getremotefile $*"
	test $? -ne 0 && myError 0 "Internal error in getremotefile. Info: $@" && exit 2
	while [[ $# -gt 0 ]] ; do
		case "$1" in
			-u|--user) user=$2 ; shift ;;
			-p|--pass) pass=$2 ; shift ;;
			-o|--output-document) outfile=$2 ; shift ;;
			-v|--verbose) verbose=1 ;;
			-b|--bar) bar=1 ; shift ;;
			-c|--continue) cont=1 ;;
			-n|--nocache) cache=0 ;;
			-q|--quiet) quiet=1 ;;
			-r|--recursive) recursive=1 ;;
			--) url=$2 ; shift ;;
			*) url=$1 ;;
		esac
		shift
	done

	for f in index.htm* *\??=? ; do # remove index.html?x=y too
		[[ -f $f ]] && rm -f "$f"
	done
	if [[ $USECURL -eq 0 ]] ; then ############ wget
		[[ -n $user ]] && opts="$opts --http-user=$user"
		[[ -n $pass ]] && opts="$opts --http-passwd=$pass"
		[[ -n $outfile ]] && opts="$opts -O $outfile"
		[[ $verbose -eq 0 && ($VERBOSE -eq 0 || $QUIET -eq 1) ]] && opts="$opts -q"
		[[ $cont -eq 1 ]] && opts="$opts -c"
		[[ $recursive -eq 1 ]] && opts="$opts -r"
		[[ $cache -eq 0 ]] && opts="$opts --cache=off"
		[[ "$url" == "-" ]] && opts="$opts -i -" && url=

		if [[ $NORESUME -eq 1 ]] ; then
			o=$outfile
			if [[ -z $o ]] ; then
				echo "fixme $url `basename "$url"`"
				o=`basename "$url"`
			else
				echo "fixme outfile $o in $PWD"
			fi
			if [[ -f $o ]] ; then
				myEcho 2 "Removing $outfile (don't want to work with incomplete files)"
				echo rm $outfile
			fi
		#else
		#	if [[ `tail -1 $o` == -----END PGP SIGNATURE----- ]] ; then
		#		continue
		#	fi
		fi
		if [[ $VERBOSE -lt 3 ]] ; then
			# suppress common errors, like 416 and timestamping turned off.
			# The index.html?x=y are also suppressed for cleaner output
			if [[ $bar -eq 0 ]] ; then
				wget $GLOBALWGETOPTS $opts $url 2>&1 | grep --line-buffered -v "\(time-stamps turned off\|\?.=.\|ERROR 416: Requested Range Not Satisfiable\|ERROR 416: Unknown\)"
			else
				wget $GLOBALWGETOPTS $opts $url
			fi
			ret=${PIPESTATUS[0]} # thanks to lge
		else # on debug level 3 I want to see it all!
			wget $GLOBALWGETOPTS $opts $url
			ret=$?
		fi
	else ########## curl
		[[ -n $user && -n $pass ]] && opts="$opts -u $user:$pass"
		[[ -n $outfile ]] && opts="$opts -o $outfile"
		[[ $VERBOSE -eq 0 || $QUIET -eq 1 ]] && opts="$opts -s"
		[[ $VERBOSE -gt 1 ]] && opts="$opts -v" && outputfilter="|grep -v HTTP.server.doesn.t.seem.to.support.byte.ranges"
		[[ $cont -eq 1 ]] && opts="$opts -C -"
		[[ $recursive -eq 1 ]] && opts="$opts " # fixme
		# [[ $cache -eq 0 ]] && opts="$opts --nocache"
		if [[ "$url" == "-" ]] ; then
			opts="$opts -K -"
			url=
		fi
		[[ -z $outfile && -n $url ]] && opts="$opts -O"
		if [[ -z $url ]] ; then
			eval 'grep -v "^$" | sed "s/$/\"/g" | sed "s#^#-O\nurl = \"#g" | \
				curl $GLOBALCURLOPTS $opts $url 2>&1' $outputfilter
			ret=$?
		else
			eval 'curl $GLOBALCURLOPTS $opts $url 2>&1' $outputfilter
			ret=$?
		fi 
	fi
	f=index.htm*
	[[ $1 == http://* && $f == index.htm\* ]] && USEDIR=1 && myNote 2 "Could not read directory from $server!"
	for f in index.htm* *\??=? ; do # remove index.html?x=y too
		[[ -f $f ]] && rm -f "$f"
	done
	return $ret
} # getremotefile


################################ checkDeleted
# check for deleted files that are still in use
function checkDeleted()
{
	local p service
	[[ $QUIET -eq 0 ]] && myEcho 1 "Checking for deleted files ..."
#	PROCS=`lsof -n 2>/dev/null | grep RPMDELETE | grep -v fou4s | \
#		cut -d " " -f 1 | sort | uniq`
	PROCS=$( lsof -n -F pcn 2>/dev/null |
		sed -ne $'/^p/h;/^c/H;
		/^n.*\(-RPMDELETE\|;\)/{
		x;s/^.//;
		s/\\\n./\t/g;p;
		:1;n;/^p/!b1;h;
		}'
	) # get the pid, too
	PROCS="`echo "$PROCS" | grep -v "\<fou4s\>"`"
	if [[ -n $PROCS ]] ; then
		echo
		myWarn 0 "WARNING"
		myWarn 0 "======="
		echo
		myWarn 0 "The following processes are accessing deleted files:"
		echo
		myWarn 0 "  PID  COMMAND"
		echo "$PROCS" | while read pid name; do
			testname=${name%d}
			service=$( cd /etc/init.d && ls -1d * | grep -v -F '.rpmsave' \
		| xargs -- grep -s -lE \
	"(^[A-Z0-9]+_BIN=|^DAEMON=|\<startproc\>.*|^[A-Z0-9_]+=)([^ ]+/)?\<$testname" | head -1
			) # the first two and the last pattern are redundant ...
			[[ -n $service ]] && service="(try /etc/init.d/$service restart)"
			[[ -n $service ]] && which rc$service &>/dev/null && service="(try rc$service restart)"
			[[ $name == mingetty ]] && service="(try killall mingetty)"
			[[ $name == java ]] && service="(try rctomcat restart)"
			printf "${COL_RED}%5i  %-15s ${COL_CYAN}%s${COL_NORM}\n" "$pid" "$name" "$service"
		done
		echo
		myWarn 0 "Please restart these processes to finish the update."
		echo
		myNote 0 "You can check for used files using the command"
		myNote 0 "fou4s --checkdeleted $COL_NORM(can be abbreviated with --checkd)"
		echo or using the command
		myNote 0 "lsof -n | grep RPMDELETE"
	fi

	if [[ $KERNELCHECK -eq 1 ]] ; then
		KERNELVER=${KERNELVER//-default/}
		KERNELVER=${KERNELVER//-smp/}
		KERNELVER=${KERNELVER//-bigsmp/}
		KERNELVER=${KERNELVER//-pae/}
		reboot=0
		newkernelver=`rpm -q --qf "%{VERSION}-%{RELEASE}" $KERNELPKG`
		newkernelbuild=`rpm -q --qf "%{BUILDTIME}" $KERNELPKG`
		if [[ -n $SUSEKERNELVER && -n $newkernelver && \
			$SUSEKERNELVER != *$newkernelver* ]] ; then
			reboot=1
		elif [[ $NODOTSUSEVERSION -eq 82 ]] ; then
			# this function is a bit heuristic: if the running kernel
			# (/proc/version) is packaged less than 24 hours after it was built,
			# we assume that it corresponds to the currently installed kernel
			# package. This is because the version numbers are not compareable in
			# some SuSE versions (8.2 has always 2.4.20-4GB, no matter what the
			# RPM says).
			runningkernelbuild="`cat /proc/version | sed 's/SMP //g'`"
			runningkernelbuild=${runningkernelbuild##*#}
			runningkernelbuild=${runningkernelbuild#* }
			runningkernelbuild=`date "+%s" -d "$runningkernelbuild"`
			# we use a little bit more than one day (would be 86400 secs)
			# because it made problems with kernel 2.6.5-7.111
			if [[ $((newkernelbuild - runningkernelbuild)) -gt 100000 ]] ; then
				reboot=1
			fi
			myEcho 2 "Running kernel build: $runningkernelbuild, Installed kernel build: $newkernelbuild"
		fi
		if [[ $KERNELVER.1 == $newkernelver || $KERNELVER.2 == $newkernelver || $KERNELVER.3 == $newkernelver ]] ; then
			reboot=0 # bugfix for 64bit openSUSE 11.1 kernel, e.g. uname 2.6.27.19-3.2 vs. 2.6.27.19-3.2.1 RPM
		fi

		if [[ $reboot -eq 1 ]] ; then
			echo
			myWarn 0 "The running kernel ($KERNELVER) is different from installed \
kernel ($newkernelver). You must reboot your machine to make the kernel update \
effective. If this notification is wrong, please contact fou4s@gaugusch.at and \
set KernelCheck=0 in fou4s.conf to disable it until a fix is available." \
| fmt -$COLUMNS
		fi
	fi
	myEcho 3 "Done checking for deleted files ..."
	return 0
} # checkDeleted


################################ printUpdateInformation
# Shows detailed information about an update
# parameters:
# $1: patch description file
# $2: short description
function printUpdateInformation()
{
	local txt txtlen line pkgdescfile=$1 shortdesc=$2 len len2
	local lang gnal width=$((COLUMNS-1))
	if [[ $VERBOSE -ge 1 ]] ; then
		echo -e $COL_CYAN
		line="======================================================================================================================================================================================================"
		[[ $newbuildtime != 0 ]] && date="(`date -d "1970-01-01 $newbuildtime sec" "+%Y-%m-%d"`) "
		txt=" Update Information for $COL_YELLOW${pkgdescfile##*/}$COL_CYAN $date"
		txtlen=$((${#txt}-${#COL_YELLOW}-${#COL_CYAN}+1))
		len=$(( $width-$txtlen ))
		[[ $((len/2)) -ne $(( (len+1)/2 )) ]] && len2=$((len/2+1)) || len2=$((len/2))
		len=$((len/2))
		width=$((len+len2+txtlen)) # if width was odd
		echo -e ${line:0:$len}$txt${line:0:$len2}
		[[ -n $shortdesc ]] && echo -e $COL_WHITE$shortdesc$COL_CYAN | myRecode | sed 's/&quot;/"/g'
		lang=$LANGUAGE gnal=$EGAUGNAL
		if [[ $NODOTSUSEVERSION -lt 101 ]] ; then
			checkLanguage "`awk "/Longdescription.$LANGUAGE:/,/$EGAUGNAL.noitpircsedgnol:/" $pkgdescfile`" || { lang=english gnal=Hsilgne ; }
			awk "/Longdescription.$lang:/,/$gnal.noitpircsedgnol:/" $pkgdescfile \
			| tr --delete '\r' \
			| sed 's/^- -/\n- /g'| sed '1d;$d' | myRecode | fmt -$width | grep -v "^$"
		else
			#echo "$longdesc" | myRecode | tr --delete '\r' | sed 's/&quot;/"/g' | fmt -$width | grep -v "^$"
			echo "$longdesc" | myRecode | sed 's/&quot;/"/g' 
		fi
		echo -e ${line:0:$width}$COL_NORM # show this line only in length of the above
	#else
	#	myNote 0 "----- Updates from $COL_YELLOW${pkgdescfile##*/}$COL_CYAN: "
	fi
	return 0
} # printUpdateInformation

function showSummaryEntry()
{
	local pre=$1 which=$2 txt=$3 size hdsize hdcomment="+"
	if [[ ${COUNTER[$which]} -gt 0 ]] ; then
		size=$((SIZES[$which]/1024))
		hdsize=$((HDSIZES[$which]/1024))
		dlsize=$((DLSIZES[$which]/1024))
		[[ $hdsize -lt 0 ]] && hdsize=$((hdsize*-1)) && hdcomment="-"
		[[ $hdsize -eq 0 ]] && hdcomment=""
		#test $hdsize -eq 0 && hdsize="???"
		printf "$pre$COL_YELLOW%3d$COL_NORM $txt$COL_YELLOW%6d${COL_NORM}kB   \
$COL_YELLOW%6s${COL_NORM}kB   $COL_YELLOW%6s${COL_NORM}kB\n" ${COUNTER[$which]}\
 $size $hdcomment$hdsize $dlsize
	fi
	return 0
} # showSummaryEntry

function showSummary()
{
	local prod=${SUSEPRODUCT:=SuSE}
	prod=${prod//\//}
	echo
	myEcho 0 "Update statistics for $prod $SUSEVERSION host $HOSTNAME (fou4s $FOUVERSION):"
	if [ $DAYSOLDER -gt 0 ] ; then
		myEcho 0 "Showing only updates older than $DAYSOLDER days"
	fi
	myEcho 0 "                               Size    After Upd.   To D/L"
	showSummaryEntry "   " $_SECURITY    "${COL_RED}security$COL_NORM update(s)    "
	showSummaryEntry "   " $_RECOMMENDED "${COL_CYAN}recommended$COL_NORM update(s) "
	showSummaryEntry "   " $_YAST        "YaST2 update(s)       "
	showSummaryEntry "   " $_OPTIONAL    "optional update(s)    "
	showSummaryEntry "   " $_SCRIPT      "script(s)             "
	showSummaryEntry "   " $_GENERATED   "generated update(s)   "
	showSummaryEntry "" $_ALL "update(s), total         "
} # showSummary


################################ getKernelVersion
# Get info about running SuSE-kernel
# UsedBy: updateRpmCache
# Parameters: none
function getKernelVersion()
{
	local pkg version
	pkg=`rpm -qf /boot/vmlinuz 2>/dev/null`
	version="`grep "\((root@[-a-zA-Z0-9]*\.suse\.\|-default \|-smp \|-bigsmp \|-pae \)" < /proc/version`"
	version=${version%%(*}
	version=${version//Linux version }
	if [[ $NODOTSUSEVERSION -lt 112 && $KERNELCHECK -eq 1 && `rpm -qf /boot/vmlinuz 2>/dev/null | wc -l` -gt 1 ]] ; then
		myWarn 0 "More than one kernel is installed! Please set KernelCheck=0 in fou4s.conf or remove other kernels"|fmt -$COLUMNS
		KERNELCHECK=0
		KERNELBACKUP=0
	elif [[ -z $pkg ]] ; then
		myWarn 2 "No SuSE kernel installed - can't check Kernel updates"
		KERNELCHECK=0
		KERNELBACKUP=0
	elif [[ -z $version ]] ; then
		myWarn 2 "No SuSE kernel running - can't check Kernel updates"
		KERNELCHECK=0
		KERNELBACKUP=0
	else
		KERNELPKG=${pkg%%-[2-9]*}
		SUSEKERNELVER=$version
		myNote 2 "Found suse kernel $KERNELPKG $SUSEKERNELVER"
	fi
}


################################ updateRpmCache
# Make sourceable cache of rpm database
# UsedBy: main
# Parameters:
# $1: if this is "force", creation is forced
function updateRpmCache()
{
	local force=0 ver rpmoutput
	RPMCACHECOMPLETE=0
	[[ $OFFLINE -eq 1 ]] && return || [[ -n $HOSTEXEC ]] && force=1
	[[ $UPDATESERVERLIST -eq 1 ]] && return
	[[ $1 == force ]] && force=1
	[[ -z $HOSTEXEC && $RPMVER -eq 30 && /var/lib/rpm/packages.rpm -nt $RPMCACHE ]] && force=1
	[[ -z $HOSTEXEC && $RPMVER -ge 40 && /var/lib/rpm/Packages -nt $RPMCACHE ]] && force=1
	if [[ $RPMCACHE -nt /proc/self/cmdline ]] ; then
		myWarn 0 "Warning: Time skew detected - fou4s cache newer than system time!"
		cleanCache
		force=1
	fi
	if [[ ! -f $RPMCACHE || $force -eq 1 ]] ; then
		trap 'exec 1>&10-; myError 0 "updateRpmCache FAILED"; rm -f "$RPMCACHE.new; exit 13"' EXIT
		set -e # exit if any of the below fails ...
		myEcho 2 "Updating rpm cache"
		query="%{NAME}='%{VERSION}-%{RELEASE} %{BUILDTIME} %{ARCH} %{SIZE}'\n"
		[[ -n $HOSTEXEC ]] && query="\"$query\""
		# by l.g.e
		if [[ -z $HOSTEXEC ]] ; then
			rpmoutput=`rpm -qa --queryformat "$query"`
		else
			myNote 2 "Getting RPM cache from remote host $HOSTNAME"
			rpmoutput=`$HOSTEXEC -- "
				SuSE-release v1.2 2>/dev/null
				rpm -qa --queryformat "$query"
			"`
			ver=(`echo "$rpmoutput" | head -1`)
			if [[ ${ver[0]} = SuSE-Release* ]] ; then
				SUSEVERSION=${ver[1]}
				NODOTSUSEVERSION=${ver[2]}
				ARCH=${ver[3]}
				SUSEPRODUCT=${ver[4]}
				if [[ ${ver[0]} = SuSE-Release-v1.1: ]] ; then
					KERNELVER=${var[5]} # only check kernelver with new suse-release
				elif [[ ${ver[0]} = SuSE-Release-v1.2: ]] ; then
					KERNELVER=${var[5]} # only check kernelver with new suse-release
					MACHINEARCH=${var[6]}
					VALIDARCHS=${ver[7]//+/ }
				fi
				RPMCACHE=$CACHEDIR/.cache.$HOSTNAME/rpmcache.$SUSEVERSION # rebuild
				myNote 2 "New SuSE-release: $SUSEPRODUCT $SUSEVERSION ($ARCH)"
				rpmoutput=`echo "$rpmoutput" | grep -v ^SuSE-Release`
			else
				if [[ $VERSIONOVERRIDE -eq 0 ]] ; then
					myError 0 "Could not determine remote SuSE-release. Install at \
least fou4s 0.10 or override with --suseversion and --product"| fmt -$COLUMNS
				fi
			fi
		fi
		echo "RPMCACHEVERSION=$RPMCACHEVER" > $RPMCACHE.new
		echo "$rpmoutput" |
		sed 'h;              # remember line
		s/^.*=/=/;           # cut
		x;                   # exchange
		s/=.*$//;            # cut
		s/[^A-Za-z0-9_]/_/g; # valid identifier
		s/^/pkg_/;           # prefix
		G;                   # append again
		s/'$'\\\n''//;       # newer sed would say s/\n//;' >> $RPMCACHE.new
		# fail if either rpm or sed had problems
		[[ -z ${PIPESTATUS[*]//0} ]]
		echo "SUSEVERSION=$SUSEVERSION" >> $RPMCACHE.new
		echo "NODOTSUSEVERSION=$NODOTSUSEVERSION" >> $RPMCACHE.new
		echo "ARCH=$ARCH" >> $RPMCACHE.new
		echo "MACHINEARCH=$MACHINEARCH" >> $RPMCACHE.new
		echo "SUSEPRODUCT=$SUSEPRODUCT" >> $RPMCACHE.new
		echo "HOSTNAME=$HOSTNAME" >> $RPMCACHE.new
		echo "RPMCACHECOMPLETE=1" >> $RPMCACHE.new
		echo "VALIDARCHS=\"$VALIDARCHS\"" >> $RPMCACHE.new
		mv $RPMCACHE.new $RPMCACHE # atomic. RPMCACHECOMPLETE should no longer be necessary
		set +e # restore default handling
		trap - EXIT
		rm -f $DESCCACHE 2>/dev/null # remove old patch description cache for safety
		DESCCACHE=$CACHEDIR/.cache.$HOSTNAME/descriptioncache.$SUSEVERSION # rebuild
	else
		myEcho 2 "Not updating rpm cache"
	fi
} # updateRpmCache

################################ updateDescriptionCache
# Make sourceable cache of update descriptions
function updateDescriptionCache()
{
	local p p1
	rm -f $DESCCACHE.new
	# not writeable -> abort
	echo "DESCRIPTIONCACHE_VERSION='1.4'" 2>/dev/null >$DESCCACHE.new || return 1
	echo "DC_ARCH='$ARCH'" >> $DESCCACHE.new
	echo "DC_PRODUCT='$SUSEPRODUCT'" >> $DESCCACHE.new
	echo "DC_VERSION='$SUSEVERSION'" >> $DESCCACHE.new
	echo "DC_SERVERLIST=\"$SERVERLIST\"" >> $DESCCACHE.new
	echo "DC_LANGUAGE='$LANGUAGE'" >> $DESCCACHE.new
	for p in ${!packageinfo_*} ${!package_*} ${!longdesc_*} ${!license_*} ${!shortdesc_*} ${!dlfile1_*} ${!server1_*} ; do
		p1=${!p} 
		echo "$p='${p1//\'/\"}'" >> $DESCCACHE.new
	done
	mv $DESCCACHE.new $DESCCACHE
	return 0
} # updateDescriptionCache  


################################ cleanCache
function cleanCache()
{
	rm -rf $CACHEDIR/.cache.$HOSTNAME
	mkdir $CACHEDIR/.cache.$HOSTNAME
	return 0
} # cleanCache


################################ parsePatchesXML
# Parse the "patches.xml" file from SuSE 10.1 .. 10.3
# Parameters:
# $1:
# -f: get filenames (patch-*.xml)
# -c: get filenames with bad checksums
# $2: prefix for all printed filenames

function parsePatchesXML()
{
	local opt=$1 prefix=$2 filesum checksum

	cat patches.xml | $XMLP | while read line ; do
		case $line in
		/patches/patch/checksum=*)
			checksum=${line##*=}
		;;
		/patches/patch/location/@href=*)
			patch=${line##*/}
			if [[ -f $patch ]] ; then
				filesum=`sha1sum $patch`
				filesum=${filesum%% *}
			fi
			[[ $filesum != $checksum || $opt == -f ]] && echo $prefix$patch
			[[ $filesum != $checksum && -f $patch ]] && rm $patch
		;;
		esac
	done 

}


################################ getDirectory
# Get all files inside of all 'directory' files in the current directory
# Parameters:
# $1: Server URL, including "patches" directory
# $2: Special fou4s update option: -f (only use for fou4s updates)
function getDirectory()
{
	local dir p url=$1
	if [[ $NODOTSUSEVERSION -lt 101 ]] ; then
		for dir in directory directory.* ; do
			[[ -f $dir ]] || continue
			if grep -q '\(<\|`\|\$\|\.\.\|/\)' $dir ; then
				myWarn 0 "Invalid content found in $PWD/$dir" 
				rm -f $dir
				continue
			fi
			grep -v "^$" $dir | sed "s#^#$url#g" | getremotefile $AUTHOPTS $downloadopts -
			[[ $2 == -f ]] && break
		done
	fi
	if [[ $NODOTSUSEVERSION -lt 110 && $NODOTSUSEVERSION -ge 101 ]] ; then
		parsePatchesXML -c $url | getremotefile $AUTHOPTS $downloadopts -
	fi
} # getDirectory


################################ betterArch
# Check if one of the two given arch's is better than the other
# Parameters:
# $1: base arch
# $2: arch for comparison
# Return Value:
# 0 if $1 is better or equal to $2, 1 otherwise (and in case of error)
function betterArch()
{
	local a i=0 a1=0 a2=0
	[[ $1 == $2 ]] && return 0
	if [[ $1 == @(${VALIDARCHS// /|}) && $2 == @(${VALIDARCHS// /|}) ]] ; then
		for a in $VALIDARCHS ; do
			i=$((i+1))
			[[ $a == $1 ]] && a1=$i
			[[ $a == $2 ]] && a2=$i
		done
		[[ $a1 -lt $a2 ]] && return 0
	fi
	return 1
}


################################ updatePackagelist
# Get the package list from the server
# Parameters:
# $1: server name (e.g. ftp.gwdg.de)
# $2: serverpath: directory on server, where updates are located
#     (e.g. /pub/linux/suse/i386/update/8.1/
function updatePackagelist()
{
	local server=$1 serverpath=$2 dirspec rsyncopts=-qz err=0 recurse ret err q
	local downloadopts="-c" # default: medium verbosity FIXME -nv!!
	local servername

	servername=${SERVERURL##*@}
	cd "$BASEDIR"
	mkdir -p "$DLPATH/$server/$serverpath/$PATCHDIR" || exit
	cd "$DLPATH/$server/$serverpath/$PATCHDIR"
	recurse=          # recurse setting is needed when going over http
	[[ -n $serverpath ]] && serverpath=$serverpath/
	if [[ $SERVERURL == *$SERVERPATH* ]] && \
		[[ $SERVERURL == rsync://* ]] ; then
		if [[ $QUIET -eq 0 ]] ; then
			myNote 0 "Generating patch descriptions from $servername" | fmt -$COLUMNS
		fi
		for g in "$DIRNAME/gpd.sh" /usr/bin/gpd.sh /usr/local/bin/gpd.sh ; do
			[[ -x $g ]] && gpd=$g && break
			g=""
		done
		[[ -n $gpd ]] && $gpd -v $VERBOSE --server $SERVERURL -q 1 --destpath .. || myError 0 "Error while calling gpd.sh!"
		logWrite 1 "Updated patch descriptions from $servername"
		return
	fi
	if [[ $QUIET -eq 1 ]] ; then
		downloadopts="-q -c" # quiet mode
	else
		myNote 0 "Getting patch descriptions from $servername"
		if [[ $VERBOSE -ge 1 ]] ; then
			downloadopts="-c" #default wget is very verbose, only set continue option
			rsyncopts=-vz
		fi
	fi
	if [[ $SERVERURL == ftp://* ]] ; then
		dirspec='*' # ftp servers need * after the path
	else
		dirspec= # http servers don't work with the *
		recurse="-r" # recursive option
	fi
	# check for fou4s updates
	if [[ $CHECKFOU -eq 1 && $NODOTSUSEVERSION -lt 101 ]] ; then
		myEcho 2 "Getting fou4s patches ..."
		if [[ $USEDIR -eq 1 ]] ; then
			[[ -f directory ]] && mv directory directory.old
			getremotefile -q --nocache $downloadopts "http://fou4s.gaugusch.at/${BETA}patches/directory" 
			getDirectory "http://fou4s.gaugusch.at/${BETA}patches/" -f
			[[ -f directory ]] && rm -f directory.old || mv directory.old directory
		else
			getremotefile -q --nocache $downloadopts -r "http://fou4s.gaugusch.at/${BETA}patches/" 
		fi
		CHECKFOU=0 # disable fou4s check when processing other servers
	fi
	# get YOU update descriptions
	if [[ $SERVERURL == @(rsync://*|*:/[^/]*) ]] ; then
		rsync $RSYNCOPTS $rsyncopts "$SERVERURL/$SERVERPATH/$PATCHDIR/*" .
		ret=$?
	else
		if [[ $USEDIR -eq 1 ]] ; then
			if [[ $NODOTSUSEVERSION -ge 110 ]] ; then
				for infofile in primary updateinfo deltainfo ; do
					test -f $infofile.xml.gz && mv "$infofile.xml.gz" "$infofile.xml.old"
					getremotefile $AUTHOPTS $downloadopts $q "$SERVERURL/$serverpath$PATCHDIR/$infofile.xml.gz"
					if [[ ! -f $infofile.xml.gz && $infofile != deltainfo ]] ; then
						myWarn 0 "Description file could not be updated for $SERVERURL: $infofile"
						for i in *.old ; do
							test -f $i && mv $i ${i%%old}.gz
						done
						break
					fi
				done
			else
				[[ $NODOTSUSEVERSION -ge 101 ]] && dir=patches.xml || dir=directory.3
				[[ $VERBOSE -eq 0 || $QUIET -eq 1 ]] && q= || q=-v
				[[ -f $dir ]] && mv "$dir" "$dir.old"
				getremotefile $AUTHOPTS $downloadopts $q "$SERVERURL/$serverpath$PATCHDIR/$dir"
				ret=$?
				if [[ $ret -ne 0 && -f $dir.old ]] ; then
					mv "$dir.old" "$dir"
				fi
				getDirectory "$SERVERURL/$serverpath$PATCHDIR/"
			fi
		else
			getremotefile $AUTHOPTS -q $downloadopts $recurse "$SERVERURL/$serverpath$PATCHDIR/$dirspec"
			ret=$?
		fi
	fi
	[[ $ret -ne 0 ]] && myError 0 "Warning: download error $ret from $server" && err=1
	if [[ $err == 0 ]] ; then
		logWrite 1 "Updated patch descriptions from $SERVERURL"
	else
		logWrite 0 "Error while updating patch descriptions from $SERVERURL"
	fi
	cd "$BASEDIR"
} # updatePackagelist


################################ updateConfigFile
# Replace value in fou4s.conf
# Parameters:
# $1: Key
# $2: Value
function updateConfigFile()
{
	local key=$1 value=$2 oldkey newcfgfile

	if [[ -n `grep ^$key= $CONFIGFILE` ]] ; then
		if [[ `grep "^$key=" $CONFIGFILE | wc -l` -gt 1 ]] ; then
			oldkey=`grep "^$key=" $CONFIGFILE| head -1`
			oldkey=${oldkey##$key=}
			myEcho 1 "Replacing first $key: $oldkey"
		else
			oldkey= # just replace existing key
		fi
		myEcho 3 "Key $key Oldkey $oldkey Value $value"
		newcfgfile=`sed "s!^$key=$oldkey.*!$key=$value!g" < $CONFIGFILE`
		if [[ -z $newcfgfile ]] ; then
			myError 0 "Internal error - cannot update config file!"
		else
			echo "$newcfgfile" > $CONFIGFILE
		fi
	else # append new key
		newcfgfile="`grep -v "^$key=" $CONFIGFILE`"
		echo "$newcfgfile" > $CONFIGFILE
		echo $key=$value >> $CONFIGFILE
	fi
	return 0
}


################################ updateServerlist
# Get the ftp server list from www.suse.de and install in /etc/suseservers
function updateServerlist()
{
	local filename=suseservers.txt
	if [[ $UPDATESUSESERVERS -eq 1 ]] ; then
		if [[ $EUID -eq 0 ]] ; then
			cd /etc
			rm -f suseservers.bak 2>/dev/null
			[[ -f suseservers ]] && cp suseservers suseservers.bak
			if [[ $NODOTSUSEVERSION -eq 101 && $SUSEVERSION = 10 ]] ; then
				echo https://nu.novell.com/repo/$RCE/SLES10-SP2-Updates/sles-10-$MACHINEARCH > /etc/suseservers
			elif [[ $NODOTSUSEVERSION -lt 103 ]] ; then
				# the following line is from online_update_start.ycp :-)
				TIMEZONE=`grep "^TIMEZONE=".*"$" /etc/sysconfig/clock | tail -1 | sed 's/TIMEZONE="//g'| sed 's/"//g'`
				[[ -n $TIMEZONE ]] && TIMEZONE="&timezone=$TIMEZONE"
				url="http://www.suse.de/cgi-bin/suseservers.cgi?product=${SUSEPRODUCT:-SUSE%20LINUX}&version=$SUSEVERSION&basearch=$ARCH&arch=$MACHINEARCH&timezone=$TIMEZONE"
				[[ -n $SUSEPRODUCT ]] && url="$url&business=1&yast2-online-update=2.9.21-0.3&yast2-packagemanager=2.9.67-0.3.1"
				getremotefile -q --output-document /etc/suseservers "$url"
				ret=$?
				if [[ $ret -ne 0 ]] ; then
					myWarn 0 "Warning: download error $ret while retrieving suseservers"
				fi
			else ## openSUSE 10.3 has automatic mirror selection through download.opensuse.org
				echo http://download.opensuse.org/ > /etc/suseservers
			fi
			cd "$BASEDIR"
		else
			myError 0 "ERROR: You must run fou4s as root to update /etc/suseservers!"
			exit 5
		fi
	fi
	echo The download server will be written to $CONFIGFILE
	if [[ ! -f /etc/suseservers ]] ; then
		echo "Could not find /etc/suseservers. Please run fou4s --server -w to get it."
		exit 6
	fi
	if [[ ! -w $CONFIGFILE ]] ; then
		myError 0 "ERROR: No permission to write to $CONFIGFILE - aborting"
		exit 7
	fi
	if [[ `cat /etc/suseservers | wc -l` -gt 1 ]] ; then
		select server in `grep -v "^#" /etc/suseservers | sed 's/;.*//g'` ; do
			cd "$BASEDIR"
			break
		done
	else
		server=`cat /etc/suseservers`
	fi
	if [[ $server == ftp://* ]] ; then
		myRead "Use HTTP instead of FTP (recommended, but doesn't work on all servers!)? (y/n)"
		if [[ $answer == y ]] ; then
			server="http://${server##ftp://}" # strip ftp:// and add http://
			echo Using $server ...
		fi
	fi
	updateConfigFile Server $server
} # updateServerList


################################ prepareKernelUpdate
# Makes a backup of the existing kernel before updating it.
function prepareKernelUpdate()
{
	local i
	if [[ ! -f /boot/vmlinuz ]] ; then
		myWarn 0 "Warning! Could not find old kernel! Can't make backup."
		return 1
	fi
	myNote 0 "Creating backup of old Kernel (to /boot/vmlinuz.fou4s) ..."
	myNote 0 "Remember to create an appropriate entry in your grub or lilo configuration."
	for i in vmlinuz System.map initrd ; do
		rm -f /boot/$i.fou4s
		[[ -f /boot/$i ]] && cat < /boot/$i > /boot/$i.fou4s
	done
	rm -rf /lib/modules/[23].[0-9]*.fou4s
	cp -a /lib/modules/$KERNELVER /lib/modules/$KERNELVER.fou4s
}


################################ postKernelUpdate
# post-kernel-installation work for kernel backup
function postKernelUpdate()
{
	local backup="`echo /lib/modules/[23].[0-9]*.fou4s`"
	local target=${backup%%.fou4s}
	myEcho 2 "Finishing Kernel backup ... ($backup, $target)"
	if [[ -d $backup ]] ; then
		if [[ ! -d $target ]] ; then
			ln -s /lib/modules/$KERNELVER.fou4s /lib/modules/$KERNELVER
		else
			myWarn 0 "Could not rename old modules back to original name (`basename "$target"`). Either the kernel update failed or the new kernel uses the same directory name (common on older SuSE 8.x) In case of problems, you must rename the modules directory manually (the backup is at $backup)" | fmt -$COLUMNS
		fi
	fi
}


################################ suDo
# parameter:
# $*: command to execute via su (to get rid of group fou4s when working as root)
function suDo()
{
	# we use an additional 'su' to get rid of group fou4s
	if [[ $EUID -eq 0 ]] ; then
		su - root -c "$*" # FIXME check su call below
		# su - root -c '$0 "$@"' -- "$@" #by l.g.e, doesn't work on some machines?
	else
		"$@"
	fi
}


################################ rpmInstall
# parameter:
# $*: filename1.rpm filename2.rpm ...
function rpmInstall()
{
	local currentversion
	local allpkgs="$*" count=$# output p lang gnal currentdir cmd
	local postKernel=0 # do kernel-update post-install operations (backup)
	if [[ -n $EXPORTFILE && $EXPORT -eq 1 ]] ; then
		for p in $allpkgs ; do
			addToRpmExportList $p
		done
		return
	fi
	if [[ $EUID -ne 0 && $TESTMODE -eq 0 && $1 != *.src.rpm ]] ; then
		myError 0 "Error: You must be root to install packages!"
		return
	fi
	if [[ $count == 1 ]] ; then
		echo "Installing ${allpkgs##*/}"
	else
		echo
		echo -e "Installing the following $COL_YELLOW$count$COL_NORM package(s): "
		for p in $allpkgs ; do
			output="$output${p##*/} "
		done
		echo "$output" | fmt -$COLUMNS
		echo
	fi

	[[ $TESTMODE -eq 0 && $KERNELBACKUP -eq 1 && $allpkgs == *$KERNELPKG* ]] && \
		prepareKernelUpdate && postKernel=1
	#we can use -U in favor of -F because the package is either already
	#installed, or HAS to be installed (because of -f option)
	[[ $TESTMODE -eq 1 ]] && test=--test
	cmd="rpm -Uvh $test $GLOBALRPMOPTS $allpkgs"
	if [[ $VERBOSE -ge 1 ]] ; then
		rpmresult=""
		suDo $cmd
	else
		rpmresult="`suDo $cmd 2>&1`"
	fi
	if [[ $? -eq 0 ]] ; then
		[[ $postKernel -eq 1 ]] && postKernelUpdate
		[[ $TESTMODE -eq 1 ]] && test=" (test mode)"
		for p in $allpkgs ; do
			# FIXME current version not available
			pkgname=`rpm -qp --qf "%{NAME}" $p`
			pkgname=${pkgname//[^a-zA-Z0-9_]/_}
			oldpkg=pkg_$p
			currentversion=pkg_$pkgname currentversion=(${!currentversion})
			currentversion=${currentversion[0]}
			logWrite 0 "Installed $p$test (old version was $currentversion)"
			COUNTER[_INST]=$((COUNTER[_INST]+1))
		done
		for p in $allpkgs ; do
			if [[ $REMOVE -eq 1 ]] ; then
				myEcho 1 "Removing ... $p$test" | fmt -$COLUMNS
				logWrite 0 "Removed $p$test"
				[[ $TESTMODE -eq 0 ]] && rm -f $p
			fi
		done
		if [[ -n $POSTINSTLIST ]] ; then
			currentdir=$PWD
			cd "$BASEDIR"
			POSTINSTLIST=`echo $POSTINSTLIST | sort | uniq`
			for j in $POSTINSTLIST ; do
				echo -e "$COL_CYAN*** Postinstall information for ${j##*/}:"
				lang=$LANGUAGE gnal=$EGAUGNAL
				checkLanguage "`awk "/Postinformation.$lang:/,/$gnal.noitamrofnitsop:/" $j`" || { lang=english gnal=Hsilgne ; }
				awk "/Postinformation.$lang:/,/$gnal.noitamrofnitsop:/" "$j" | sed '$1;$d' | fmt -$COLUMNS | myRecode
				echo -e $COL_NORM
			done
			POSTINSTLIST=
			cd "$currentdir"
		fi
		myNote 1 "Update successful!"
		# run suseconfig only if a pkg was installed, and testmode is not active
		[[ $SUSECONFIG -eq 1 ]] && SUSECONFIG=2
		rm -f $DESCCACHE $AUTOSKIPPEDPATCHESFILE &>/dev/null #force creation next time
	else
		echo -e $COL_RED
		cat << _EOF

************************
* ERROR: Update failed *
************************

$rpmresult

_EOF
		echo -e $COL_NORM
		if [[ $INSTMODE -gt 0 ]] ; then
			echo "Please try option --end, -a or --nodeps in case of dependency problems."
			echo "Using --nopatchrpms might also help"
		else
			echo "Please try without option --end, -a or use --nodeps in case of dependency problems." | fmt -$COLUMNS
			echo "Using --nopatchrpms might also help"
		fi
		myError 0 "THE PACKAGES SHOWN ABOVE HAVE NOT BEEN INSTALLED!"
		FAILCOUNT=$((FAILCOUNT+count))
	fi
	cd "$BASEDIR"
	cd "$DLPATH"
} # rpmInstall


################################ cleanupPackagelist
# Remove old patch description files from currently processed server
function cleanupPackagelist()
{
	local slashcount lastpkg i pkg
	myEcho 2 "Cleaning up packagelist directory ... "
	if [[ $NODOTSUSEVERSION -ge 101 ]] ; then
		myWarn 0 "Fixme: Implement cleanupPackagelist for 10.1"
		return 
	fi
	local slashcount="$DLPATH/$server/$SERVERPATH/"
	slashcount=${slashcount//[^\/]/}
	slashcount=${#slashcount}
	for i in `ls $DLPATH/$server/$SERVERPATH/$PATCHDIR/*-*([0-9]) 2>/dev/null | sort -t / +$slashcount` ; do
		pkg=${i%-*}
		pkg=${pkg##*/}
		newversion=${i##*-}
		myEcho 4 "Cleanup checking $i"
		if [[ $lastpkg == $pkg ]] ; then
			if [[ $newversion -gt $lastversion ]] ; then
				myEcho 2 Cleanup removing $lastfullpkg
				rm -f $lastfullpkg
			else
				myEcho 2 Cleanup removing $i
				rm -f $i
				i=$lastfullpkg
			fi
		fi
		lastpkg=${i%-*}
		lastpkg=${lastpkg##*/}
		lastversion=${i##*-}
		lastfullpkg=$i
	done
} # cleanupPackagelist


################################ hasGoodSignature
# checks a signature (gpg output)
# Parameters:
# $1: gpg output
# $2: DSA or RSA
# $3: key id
# $4: signer (name + email address)
# $5: builddate (optional)
# returns: buildtime (date of signature)
function hasGoodSignature()
{
	local sigresult="$1" buildtime cmp1 cmp2 cmp3=xuxux cmp4=xuxux keyid=$3
	if [[ -n $5 ]] ; then
		# get time from signature
		buildtime=${SIGRESULT##*Signature made }
		buildtime=${buildtime%% using $2 key*}
		buildtime=`date -d "$buildtime" +%s`
	fi
	cmp1="*Signature made * using $2 key ID $keyid*"
	cmp2="*Good signature from *$4*"
	if [[ $RPMVER -eq 40 ]] ; then
		keyid=`echo $keyid | tr '[A-Z]' '[a-z]'`
		cmp3="*MD5 digest: OK*"
		cmp4="*V[34] DSA signature: OK, key ID $keyid*"
	elif [[ $RPMVER -ge 48 ]] ; then
		keyid=`echo $keyid | tr '[A-Z]' '[a-z]'`
		cmp1="*V3 $2 Signature, key ID $keyid: OK*"
		cmp2="*Header V3 $2 Signature, key ID $keyid: OK*"
		cmp3="*MD5 digest: OK *"
		cmp4="*V3 RSA/SHA256 Signature, key ID $keyid: OK*"
	elif [[ $RPMVER -ge 44 ]] ; then
		keyid=`echo $keyid | tr '[A-Z]' '[a-z]'`
		cmp3="*MD5 digest: OK*"
		cmp4="*V3 RSA/SHA256 signature: OK, key ID $keyid*"
	fi
	if [[ $sigresult == $cmp1 && $sigresult == $cmp2 ]] ; then
		[[ $5 ]] && RETVAL="$buildtime"
		return 0
	elif [[ $sigresult == $cmp3 && $sigresult == $cmp4 ]] ; then
		[[ $5 ]] && RETVAL="$buildtime"
		return 0
	elif [[ $sigresult == *no\ valid\ OpenPGP\ data\ found* ]] ; then
		RETVAL="$sigresult"
		return 254
	else
		RETVAL="$sigresult"
		return 255 # error
	fi
} # hasGoodSignature


################################ hasGoodRpmSignature
# check the signature of an rpm file
# parameter:
# $1 rpm file to check
function hasGoodRpmSignature()
{
	local baserpm=$1
	sigresult="`rpm -v --checksig $baserpm 2>&1`"
	if hasGoodSignature "$sigresult" RSA/SHA256 3dbdc284 "openSUSE Project Signing Key <opensuse@opensuse.org>" ; then
		return 0 # ok
	elif hasGoodSignature "$sigresult" DSA 9C800ACA "SuSE Package Signing Key <build@suse.de>" ; then
		return 0 # ok
	elif [[ $pkg == fou4s ]] && hasGoodSignature "$sigresult" DSA $FOU4SKEY "fou4s build key <fou4s@gaugusch.at>" ; then
		return 0 # ok
	elif [[ $pkg == fou4s ]] && hasGoodSignature "$sigresult" DSA/SHA1 $FOU4SKEY "fou4s build key <fou4s@gaugusch.at>" ; then
		return 0 # ok
	elif [[ $sigresult == *gpg:\ Good\ signature\ from* ]] && \
		[[ $sigresult != *gpg:\ WARNING:\ This\ key\ is\ not\ certified\ with\ a\ trusted\ signature* ]] ; then
		return 0 # ok
	elif [[ $sigresult == *V3\ DSA\ signature:\ OK* ]] && \
		[[ $sigresult != *gpg:\ WARNING:\ This\ key\ is\ not\ certified\ with\ a\ trusted\ signature* ]] ; then
		return 0 # ok
	else
		if [[ `echo "$sigresult" | wc -l` -le 2 && $sigresult == *MD5\ sum\ OK* ]] ; then
			myError 0 "No signature found in $baserpm! You may want to use -g to ignore this error" | fmt -$COLUMNS
		else
			myError 0 "Signature verification failed for $baserpm (no installation)! You may want to try the -g option to ignore this error." | fmt -$COLUMNS
			myWarn 0 "$sigresult"
		fi
		echo
		return 254 # not ok
	fi
} # hasGoodRpmSignature


################################ padZero
# replaces all consecutive digits with zero padded equivalents of known
# length, all nondigits remain intact:
# 3.0.20 -> 00000003.00000000.00000020
# 3.0.3  -> 00000003.00000000.00000003
# by l.g.e
function padZero()
{
	local v="$1" part allzeroes=0000000000
	RETVAL=()
	while [[ -n $v ]] ; do
		part=${v%%[0-9]*} # nondigits
		v=${v#$part}
		RETVAL=$RETVAL$part
		[[ $v ]] || break
		part=${v%%[^0-9]*} # digits
		v=${v#$part}
		part=${allzeroes: ${#part}}$part
		RETVAL=$RETVAL$part
	done
} # padZero


################################ vcmp
# version compare version_a op version_b
# op is one of the allowed operators in [ ]
# by l.g.e
function vcmp()
{
	local a=$1 op=$2 b=$3 ret bool
	padZero $a; a=$RETVAL
	padZero $b; b=$RETVAL

	[ $a $op $b ]
	ret=$?
	myEcho 3 "Comparing $a $op $b: $ret "
	# if this happens, you called this with an invalid op
	# check source code!
	[[ $ret -gt 1 ]] && { myError 0 "Error ($ret) in vcmp ($pkg):" [ $a \\$op $b ]; }

	# warn to stderr if length of padded a and b differ
	# it was something like a=3.0 and b=3.0.1
	# or a=0.8.7 and b=0.9.1_beta
	if [[ ${#a} != ${#b} && $FORCEINSTALL -eq 0 && $3 != 0.0.0.0-0 && $3 != N/A && $kind != generated ]] && ! packageFiltered $pkg $kind $series; then
		if [ $VCMP_OK -eq 0 ] ; then
			if [[ "`zypper vcmp 2>&1 | head -1`" == "Required argument missing." ]]; then
				myEcho 3 "Tested zypper fallback ok"
				VCMP_OK=1
			fi
		fi
		if [[ $VCMP_OK -eq 1 ]] ; then
				myEcho 3 "Falling back to zypper vcmp $1 $3"
			if [[ `zypper vcmp $1 $3` == *newer* ]] ; then
				ret=0
			else
				ret=1
			fi
		else
			if [[ $QUIET -eq 0 ]] ; then
				[[ $ret -eq 1 ]] && bool=false || bool=true
				myWarn 2 "Warn: Different version structure: $pkg: $1 $op $3 = $bool" >&2
				myWarn 2 "Using buildtime to check again for safety ..." >&2
			fi
			usebuildtime=1 # non-global variable only used for this check in checkpkg.
			ret=1
		fi
	fi
	return $ret
} # vcmp


################################ packageFiltered
# Returns true, if package should not be processed
# parameters:
# $1: package name
# $2: kind
# $3: series
function packageFiltered()
{
	local pkg=$1 kind=$2 series=$3 patchname currenttime
	[[ $SECURITY -eq 1 && $kind != security ]] && return 0
	[[ $SKIPOPTIONAL -eq 1 && $kind == optional ]] && return 0
	[[ $GENERATED -eq 0 && $kind == generated ]] && return 0
	currenttime=`date +%s`
	if [[ $((newbuildtime+DAYSOLDER*60*60*24)) -gt $currenttime ]] ; then
		return 0
	fi
	[[ $((BUILDTIME+days*60*60*24)) -gt $currenttime ]] && return 0
	if [[ -n $EXCLUDELIST ]] ; then
		patchname=${i##*/}
		if [[ $series == @($EXCLUDELIST) ]] ; then
			myEcho 2 "Skipping $pkg with series $series"
			return 0
		fi
		if [[ $patchname == @($EXCLUDELIST) ]] ; then
			myEcho 2 "Skipping $pkg in patch description ${i##*/}"
			return 0
		fi
		if [[ $pkg == @($EXCLUDELIST) ]] ; then
			myEcho 2 "Skipping $pkg in patch description ${i##*/}"
			return 0
		fi
		if [[ $pkg-$newversion == @($EXCLUDELIST) ]] ; then
			myEcho 2 "Skipping $pkg in patch description ${i##*/}"
			return 0
		fi
	fi
	if [[ -n $IGNORELIST ]] &&
		[[ $pkg-$newversion == @($IGNORELIST) || $pkg == @($IGNORELIST) ]]
	then
		# quiet and verbose are both set in automode!
		# but we don't want ignore messages there
		[[ $QUIET -eq 0 ]] && \
			myNote 1 "\nIgnoring $pkg (matches config file directive $IGNORELIST)"
		return 0
	fi
	if [[ $pkg-$newversion == @($REMARKLIST) || $pkg == @($REMARKLIST) ]] ; then
		remarkonly=1
		myEcho 2 "Remark only: $pkg"
	fi
	if [[ $ONLYINSTALLEDUSERRPMS -eq 1 ]] ; then
		[[ $pkg == @($USERRPMS) ]] || return 0
		[[ $currentversion == N/A && $kind != script && $GETALL -eq 0 ]] && return 0
	fi
	return 1 # package was not filtered
} # packageFiltered


################################ guessVersionFromFilename
# expects $rpm (=path/name-ver-rel.somthing.rpm) in the environment,
# splits it up into its components
# modifies environment:
# newversion = ver-rel
# pkg        = name
# series     = path components after first SUSEVERSION/
function guessVersionFromFilename ()
{
	local rel ver path=${rpm#*$SUSEVERSION/}
	while true; do     # kind of exception handling :)
		pkg=${path##*/}
		series=${path%/*};
		[[ $series == $path ]] && series="unknown"
		rel=${pkg##*-}
		[[ $rel != $pkg && $rel == [0-9]* ]] || break #no '-' found
		rel=${rel%%.*}
		pkg=${pkg%-*}
		ver=${pkg##*-}
		[[ $ver == $pkg ]] && break # no second '-' found
		pkg=${pkg%-*}
		newversion="$ver-$rel"
		return 0
	done
	# only reached on error
	pkg=${pkg%%.*}
	return 1
} # guessVersionFromFilename


################################# checkPreInstallInfo
# Handles preinstall information for the user
# parameters:
# $1: package name
# $2: patch description file containing pre install info
function checkPreInstallInfo()
{
	local pkg=$1 pkgdescfile=$2 currentdir answer lang gnal
	if [[ $PREINFO -eq 1 && $INTERACTIVE -eq 1 ]] ; then
		myNote 0 "Package $pkg contains the following pre-install (license) information:"
		currentdir=$PWD
		cd "$BASEDIR"
		cd "$DLPATH"
		lang=$LANGUAGE gnal=$EGAUGNAL
		echo -en "$COL_WHITE"
		if [[ $NODOTSUSEVERSION -lt 101 ]] ; then
			checkLanguage "`awk "/Preinformation.$lang:/,/$gnal.noitamrofnierp:/" $pkgdescfile`" || { lang=english gnal=Hsilgne ; }
			awk "/Preinformation.$lang:/,/$gnal.noitamrofnierp:/" $pkgdescfile | grep -v -F "Preinformation.$lang" | grep -v -F "$gnal.noitamrofnierp" | fmt -$COLUMNS
		else
			echo "$license" | myRecode | fmt -$COLUMNS
		fi
		echo -en "$COL_NORM"
		cd "$currentdir"
		if [[ $ACCEPTPREINSTALLINFO -eq 0 ]] ; then
			myRead "Perform update (yes/no)? " <&6
			[[ $answer != y ]] && return 1
		fi
	fi
	return 0
} # checkPreInstallInfo


################################# getUserChoice
# ask user to perform download/update
# returns false if not updating, else true
function getUserChoice()
{
	local ok=0 v descr
	if [[ $INTERACTIVE -eq 1 ]] ; then
		[[ $DOINSTALL -eq 1 ]] && upd="/update"
		while [ $ok -ne 1 ] ; do
			[[ $VERBOSE -lt 1 ]] && descr="/show [D]escription" || descr=
			myRead "Perform download$upd ([Y]es/[N]o/[S]kip forever$descr)? [$LASTANSWER] " <&6
			case "$answer" in
				y)
					ok=1 ;;
				n)
					ok=1 ;;
				s)
					ok=1 ;;
				d|D)
					v=$VERBOSE
					VERBOSE=1
					printUpdateInformation $i "$shortdesc"
					VERBOSE=$v
					;;
				V)
					if [[ $VERBOSE -lt 4 ]] ; then
						VERBOSE=$((VERBOSE+1))
						echo Verbosity increased to $VERBOSE
					fi ;;
				v)
					if [[ $VERBOSE -gt 0 ]] ; then
						VERBOSE=$((VERBOSE-1))
						echo Verbosity decreased to $VERBOSE
					fi ;;
				"")
					answer=$LASTANSWER
					ok=1 ;;
				*)
					echo "Wrong input! Use y/n/s or return for last choice. Change verbosity +/- with V/v."
			esac
		done
		LASTANSWER=$answer
		if [[ $answer == s ]] ; then
			myEcho 0 "${COL_YELLOW}Skipping $COL_RED$pkg-$newversion$COL_YELLOW forever ... $COL_NORM(look at skipped-patches file to enable again)" | fmt -$COLUMNS
			if [[ $NODOTSUSEVERSION -lt 110 ]] ; then
				addToSkippedPatches "`basename $i`"
			else
				addToSkippedPatches "$pkg-$newversion"
			fi
			return 1
		fi
		if [[ $answer != y ]] ; then
			myNote 0 "Skipping $pkg from $patchfile ..."
			return 1
		fi
	fi
	return 0
} # getUserChoice


################################# getRpm
# downloads a package if user permits (in interactive mode)
# parameters:
# $1: name of patch (for printing)
# $2: rpm file to download
# $3: size of rpm
# $4: URL to download
# return value: true if download went ok
function getRpm()
{
	local rpm=$1 pkg=$2 size=$3 dlfile=$4 wgetopts="-q" rsyncopts="-q --progress"
	[[ $VERBOSE -ge 1 ]] && wgetopts="" && rsyncopts="--progress -v"
	cd "$BASEDIR"
	cd "$DLPATH"
	if fileExists $server $rpm && [ $FOUNDSIZE -eq $size ] ; then
		myWarn 2 "Skipping download of $pkg - already complete"
		return 0
	fi
	[[ $kind == script ]] && fileExists $server $rpm && return 0
	cd "$server/$SERVERPATH"
	[[ ! -d $rpmpath ]] && mkdir -p $rpmpath
	cd "$rpmpath"
	if [[ $DOWNLOAD -eq 1 ]] ; then
		if [[ $origurl == *://* ]] ; then
			[[ $QUIET -eq 0 ]] && myNote 0 "Getting $origurl"
		else
			[[ $QUIET -eq 0 ]] && myNote 0 "Getting $server:$rpm"
		fi
		if [[ $dlfile == rsync://* ]] ; then
			rsync $RSYNCOPTS $rsyncopts "$dlfile" .
			ret=$?
			[[ $ret -eq 20 ]] && exit 8 # handle ctrl-c
		else
			getremotefile -b -c $AUTHOPTS $wgetopts "$dlfile"
			ret=$?
		fi
		if [[ ! -f ${rpm##*/} ]] ; then # only abort if file doesn't exist
			[[ $ret -ne 0 ]] && myError 0 "Error getting $server:$rpm: $ret"
			cd "$BASEDIR"
			cd "$DLPATH"
			return 1 # error
		fi

		if [[ $USEDELTARPMS -eq 1 ]] ; then
			rpmname=`basename $rpm`
			if [[ $rpmname == *.delta.rpm ]] ; then
				applydeltarpm $rpmname ${rpmname%%.delta.rpm}.rpm
			fi
		fi
	fi
	cd "$BASEDIR"
	cd "$DLPATH"
	return 0 # ok
} # getRpm


################################# processDownloadedRpm
# checks the downloaded rpm signature, version and installs it
# parameters:
# $1: package name
# $2: rpm file name
# $3: new version
# $4: short filename (without path)
function processDownloadedRpm()
{
	local pkg=$1 rpm=$2 newversion=$3 baserpm=$4
	cd "$BASEDIR"
	cd "$DLPATH"
	cd "${FOUNDFILE%/*}" # directory where file was found
	if [[ $AUTOMODE -eq 1 ]] && [[ -z $pkg || $pkg != @($AUTOLIST) ]]; then
		# package not in autolist
		cd "$BASEDIR"
		return
	fi

	if [[ $SKIPGPG -eq 0 ]] ; then
		hasGoodRpmSignature $baserpm || { FAILCOUNT=$((FAILCOUNT+1));return; }
	fi

	# check version of downloaded rpm again - it may be different
	dlrpminfo=(`rpm -qp $baserpm --queryformat "%{VERSION}-%{RELEASE} %{ARCH}" 2>/dev/null`)
	newpkgver=${dlrpminfo[0]}
	newrpmarch=${dlrpminfo[1]}
	ok=0
	for a in $VALIDARCHS ; do
		[[ $a == $newrpmarch ]] && ok=1 && break
	done
	[[ $ok -eq 0 ]] && myWarn 0 "Bad arch for $pkg: $newrpmarch, skipping" && return
	if [[ $newpkgver != $newversion && $currentversion != N/A ]] ; then
		myWarn 0 "Warning: Downloaded RPM has version $newpkgver, but patch description says $newversion!"
		if [[ $newpkgver != $currentversion ]] ; then
			# compare with version
			if vcmp "$newpkgver" \> "$currentversion" ; then
				echo "Using $baserpm $newpkgver anyway"
			else
				myWarn 0 "Not installing $baserpm (same or newer version already installed)."
				return
			fi
		else
			myNote 0 "Same $baserpm already installed (no installation)."
			return
		fi
	fi

	# if pre install info is there, we can't continue (except for interactive m.)
	checkPreInstallInfo $pkg $i || return

	if [[ $baserpm == *.delta.rpm ]] ; then
		myEcho 1 "Applying delta RPM for $pkg ..."
		[[ ! -f ${baserpm%%.delta.rpm}.rpm ]] \
			&& applydeltarpm $baserpm ${baserpm%%.delta.rpm}.rpm
		FOUNDFILE=${FOUNDFILE%%.delta.rpm}.rpm
		baserpm=$FOUNDFILE
		hasGoodRpmSignature $baserpm || { FAILCOUNT=$((FAILCOUNT+1));return; }
	fi

	if [[ $INSTMODE -gt 0 ]] ; then
		RPMINSTLIST="$RPMINSTLIST $FOUNDFILE"
		if [[ $POSTINFO -eq 1 ]] ; then      # show post installation info for
			POSTINSTLIST="$POSTINSTLIST $i" # this package
			POSTINFO=0
		fi
	else
#fixme old version is not available!
		logWrite 1 "Going to install $pkg $newversion [$kind]$test"
		rpmInstall $baserpm
	fi
	cd "$BASEDIR"
	cd "$DLPATH"
} # processDownloadedRpm


################################# processScript
# processes an install script
# parameters:
# $1: script name
function processScript()
{
	local pkg=$1 fullname=$DLPATH/$server/$SERVERPATH/scripts/$pkg
	TMPFILE=$CACHEDIR/$pkg.$PPID

	[[ -e $TMPFILE ]] && rm -f $TMPFILE
	if hasGoodSignature "`gpg --no-tty -o "$TMPFILE" --decrypt "$fullname" 2>&1`" \
		DSA 9C800ACA "SuSE Package Signing Key <build@suse.de>" ; then

		# if pre install info is there, we can't continue (except for interactive m.)
		checkPreInstallInfo $pkg $i || return

		if [[ -n $EXPORTFILE && $EXPORT -eq 1 ]] ; then
			addToRpmExportList $fullname
			return 0
		fi
		logWrite 1 "Going to install $pkg $newversion [$kind]$test"
		chmod +x "$TMPFILE"
		myNote 0 "Executing script $pkg ..."
		if [[ $TESTMODE -eq 0 ]] ; then
			$TMPFILE
			ret=$?
			myEcho 0 "Script finished with code $ret"
			if [[ $ret -eq 0 ]] ; then
				myEcho 0 "Adding ${i##*/} to skipped-patches"
				addToSkippedPatches ${i##*/}
				[[ $SUSECONFIG -eq 1 ]] && SUSECONFIG=2
			else
				myNote 0 "Not adding $pkg to skipped-patches file because of bad return code!"
			fi
		else
			myEcho 0 "... skipped because of testmode"
		fi
		rm -f $TMPFILE
	else
		myError 0 "Bad signature in script $pkg!"
		rm -f $TMPFILE
		return 1
	fi
} # processScript


################################# processXMLScript
# processes an install script from an XML patch description
function processXMLScript()
{
	echo "executing script: $scriptcode" | myRecode
}


################################# fileExists
# checks if a given package has been downloaded. All servers are checked
# and some archs for probing unknown packages
# Parameter:
# $1: primary server to search on
# $2: rpm name with path (a1/bash-2.0.4-4.i586.rpm or i586/bash-2.0.4-4.i586.rpm
function _fileExists()
{
	local server=$1 rpm=$2 f arch file a rpmarch

	FOUNDFILE=$DLPATH/$server/$SERVERPATH/$rpm
	[[ -f $FOUNDFILE ]] && return 0 # that was easy
	rpmname=${rpm##*/}
	firstpath=${rpm%%/*}
	for a in $VALIDARCHS ; do
		[[ $rpm == *.$a.rpm ]] && rpmarch=$a && break
	done
	for f in $DLPATH/*/$SERVERPATH ; do
		FOUNDFILE=$f/$rpm
		[[ -f $FOUNDFILE ]] && return 0 # that was relatively easy
		for a in $VALIDARCHS ; do
			FOUNDFILE=$f/$firstpath/$a/${rpmname//$rpmarch/$a}
			[[ -f $FOUNDFILE ]] && return 0 # not so easy to find
		done
	done
	FOUNDFILE=
	return 1 # not found
} # _fileExists

function fileExists()
{
	FOUNDSIZE=0
	_fileExists "$@" && FOUNDSIZE=$(find $FOUNDFILE -maxdepth 0 -printf "%s")
} # fileExists


################################# showPackage
# Shows the given package to the user in a friendly way
# Parameters:
# $1: package name (short)
# $2: new version
# $3: old version
# $4: kind
# $5: size
# $6: series
# $7: remarkonly (0/1)
# Return value:
# Returns false, if the package is on remarklist. checkPackage will exit in that
# case
function showPackage()
{
	# local pkg=$pkg.$series # for arch debugging only
	#showPackage $pkg $newversion $currentversion $kind $size $series $remarkonly || continue
	#local pkg=$1 newversion=$2 currentversion=$3 kind=$4 size=$5 series=$6
	#local remarkonly=$7
	local ksize=$((size/1024)) ckind # colored version of 'kind' field
	local nvsize=13 cvsize=13 # size of version fields in printf
	local p=" " rwidth=$RWIDTH
	if [[ $VERBOSE -ge 2 ]] ; then
		[[ $rpm == *.delta.rpm ]] && p="*"
		[[ $rpm == *.patch.rpm ]] && p="+"
	fi
	if fileExists $server $rpm ; then
		[[ $kind == script ]] && FOUNDSIZE=$size
		[[ $FOUNDSIZE == $size ]] \
		&& dl="$COL_GREEN[ok]$COL_NORM" \
		|| dl="$COL_YELLOW[d?]$COL_NORM"
	else
		dl="$COL_RED[dl]$COL_NORM"
	fi
	if [[ -n $COL_NORM ]] ; then # color support
		[[ $kind = security ]] && ckind="$COL_RED"
		[[ $kind = recommended ]] && ckind="$COL_CYAN"
	fi
	[[ ${#newversion} -ge 13 ]] && z=${#newversion} && cvsize=$((13-(z-13)))
	[[ ${#currentversion} -ge 13 ]] && z=${#currentversion} && nvsize=$((13-(z-13)))
	nvs=${#newversion}
	cvs=${#currentversion}
	[[ $((nvs+cvs)) -gt 26 ]] && z=$((nvs+cvs)) && rwidth=$((RWIDTH-(z-26) )) 
	[[ $updateinfoshown -eq 0 ]] && printUpdateInformation $i "$shortdesc" && updateinfoshown=1
	printf "$COL_WHITE%-"$rwidth"b $COL_GREEN%-"$nvsize"b$COL_NORM (%b%-"$cvsize"b%b)$p$dl $ckind%-12b$COL_NORM%5bkb \n" "$pkg" "$newversion" "$COL_RED" "$currentversion" "$COL_NORM" "$kind" "$ksize"
	[[ $size = 0 && $series != script ]] && \
		myWarn 1 "Warning: $pkg: Invalid size in patch description: 0!"
	if [[ $remarkonly -eq 1 && $INTERACTIVE -eq 0 ]] ; then
		echo "($pkg will not be updated because of RemarkList entry)"
		return 1
	fi

	if [[ $INTERACTIVE -eq 0 && $EXPORT -eq 0 ]] ; then
		if [[ $series == script ]] ; then
			myWarn 0 "NOTE: Script must be installed using$COL_CYAN fou4s -i --interactive:$COL_YELLOW $pkg"
		elif [[ $PREINFO -eq 1 ]] ; then
			myNote 0 "NOTE: Package $pkg contains pre-install information."
			if [[ $ACCEPTPREINSTALLINFO -eq 0 ]] ; then
				myNote 0 "You must install it using$COL_YELLOW fou4s -i --interactive or --acceptpreinstallinfo"
			fi
		fi
	fi
	return 0
} # ShowPackage


################################# checkPackage
# processes one RPM file
# parameters:
# $1: package name
# $2: path of rpm on ftp server
# $3: new version
# $4: kind of package
# $5: download size of normal rpm
# $6: download size of patch rpm
# $7: buildtime of package
# $8: series of package (optional)
# $9: filename field of patch description
# $10: short description
function checkPackage()
{
	#local pkg=$1 rpm=$2 newversion=$3 kind=$4 size=$5 patchsize=$6
	#local newbuildtime=$7 series=$8 filename=$9 shortdesc=${10}
	local usepatchrpmnow=0 currentversion="N/A" arch curbuildtime=0 remarkonly=0
	local founduserrpm=0 update=0 cacheinfo oldhdsize=0 usedeltarpmnow=0

	ALLCOUNT=$((ALLCOUNT+1)) # used for progress bar
	[[ $USEPROGRESS -eq 1 ]] && showProgress $ALLCOUNT $MAXCOUNT

	# if patchsize is given, we will try to use it
	[[ $patchsize -gt 0 ]] && usepatchrpmnow=1
	[[ $deltasize -gt 0 ]] && usedeltarpmnow=1

	if [[ -z $pkg ]] ; then
		myError 0 "Error, no package name, skipping ($# args)"
		return 255
	fi
	if [[ $rpm != *.rpm && $NODOTSUSEVERSION -lt 81 && -z $SUSEPRODUCT ]] ; then
		myError 0 "Error, invalid filename in $pkg (${i##*/}), skipping"
		return 255
	fi

	if [[ $newversion == fix-me ]] ; then
		myWarn 0 "WARNING: No version in $pkg! Trying to construct version number from filename '${rpm##*/}' ..." | fmt -$COLUMNS
		if ! guessVersionFromFilename ; then
			myError 0 "ERROR No version found in filename! Skipping ..."
			return
		fi
		echo "Using $pkg $newversion from filename ..."
	fi

	myNote 2 "Checking $pkg: $newversion [$kind, ${size}b]"

	if [[ $NODOTSUSEVERSION -lt 110 ]] ; then
		# don't ask me why ...
		filename=${filename%%.rpm}
	fi
	r=${pkg//[^a-zA-Z0-9_]/_} alreadyfound=packageinfo_$r alreadyfound=(${!alreadyfound})
	if [[ $founduserrpm -eq 0 ]] ; then
		cacheinfo=pkg_${pkg//[^a-zA-Z0-9_]/_} cacheinfo=${!cacheinfo}
		#fixme check the following if $pkg is already on the system
		if [[ $pkg.rpm == @($USERRPMS) || $pkg == @($USERRPMS) ]] ; then
			founduserrpm=1 currentversion=0.0.0.0-0 usepatchrpmnow=0
			if [[ $NODOTSUSEVERSION -lt 101 ]] ; then
				arch=`grep "^=Pkg: $pkg " $PACKAGES81 | grep -v src$`
				arch=${arch##* }
			fi
			if [[ -z $arch ]] ; then
				for a in $VALIDARCHS ; do
					[[ $rpm == *.$a.rpm ]] && arch=$a && break
				done
			fi
			myEcho 3 "Using arch '$arch' for $pkg ($rpm)"
		elif [[ $FORCEINSTALL -eq 0 && -z $cacheinfo && $GETALL -eq 0 ]] ; then
			PENDINGARRAY[$PENDINGCOUNT]="$*"
			PENDINGCOUNT=$((PENDINGCOUNT+1))
			myEcho 3 "Added $pkg to pending list"
			return # no forced install - add to pending list
		fi
		if [[ -z $cacheinfo ]] ; then # forced installation
			# disable use of patch rpms if no installed package is found
			currentversion="N/A" usepatchrpmnow=0
		else
			cacheinfo=($cacheinfo) # convert to array
			currentversion=${cacheinfo[0]}
			curbuildtime=${cacheinfo[1]}
			arch=${cacheinfo[2]}
			oldhdsize=${cacheinfo[3]}
			if [[ $usepatchrpmnow -eq 1 ]] ; then # check PatchRpmBasedOn list
				[[ -n $patchbase ]] && usepatchrpmnow=0 && for p in $patchbase ; do
					if [[ $p = $currentversion ]] ; then
						usepatchrpmnow=1
						break
					fi
				done
				[[ -z $patchbase ]] && usepatchrpmnow=1
			fi # use patch rpm
		fi # empty currentversion
	fi # ! GETALL
	if [[ -z $arch && $NODOTSUSEVERSION -lt 101 ]] ; then
		arch=`grep "^=Pkg: $pkg " $PACKAGES81 | grep -v src$`
		arch=${arch##* }
	fi
	if [[ $NODOTSUSEVERSION -lt 110 ]] ; then
		[[ $series != default && $series == @(${VALIDARCHS// /|}) ]] && myEcho 2 "Using arch $series instead of $arch for $pkg" && arch=$series # fixme does this really work?!?!
	fi
	if [[ $NODOTSUSEVERSION -ge 110 ]] ; then
	[[ $series != $arch ]] && myEcho 2 "Arch $series for $pkg does not match original arch $arch" && return
	fi
	if [[ $filename != *.rpm ]] ; then
		if [[ $usepatchrpmnow -eq 1 ]] ; then
			filename=$filename-$newversion.$arch.patch.rpm
		else
			filename=$filename-$newversion.$arch.rpm
		fi
		myEcho 3 "Found incomplete filename $filename"
	fi
	if [[ $rpm == unknown ]] || [[ -z $rpm ]] ; then
		if [[ -z $arch && $NODOTSUSEVERSION -lt 101 ]] ; then
			arch=`grep "^=Pkg: $pkg " $PACKAGES81 | grep -v src$`
			arch=${arch##* }
		fi
		if [[ -z $arch ]] ; then
			arch=`rpm -q $pkg --queryformat "%{ARCH}" 2>/dev/null`
		fi
		if [[ -z $arch && $series != script ]] ; then
			myError 0 "\nError: No arch for $pkg!! Fou4s can't install this package ($rpm, $i)"
			# FIXME try short name and guess arch
			return
		fi
		rpm=rpm/$arch/$filename
	fi
	quit=1
	[[ $kind == script ]] && quit=0

	for a in $VALIDARCHS ; do
		[[ $arch == $a ]] && quit=0 && break
	done
	[[ $quit -eq 1 ]] && myWarn 2 "Invalid arch for $pkg: $arch, skipping" && return
# fixme - does this work?
	if [[ $USEFULLPATH -eq 0 || $rpm == *tp://* ]] ; then
		baserpm=${rpm##*/} rpmpath=.
	else
		baserpm=${rpm##*/} rpmpath=${rpm%/*}
		rpmpath=${rpmpath##+(/)} # strip leading slashes
		rpmpath=${rpmpath##*..+(/)} # some/ugly/../../crappy/../..///path -> path
		#rpm="$rpmpath/$baserpm" # build again without ../../crappy//things
	fi
	[[ $GETALL -eq 1 && $GETALLNEW -eq 0 ]] && update=1 # force when getting all
	[[ $GETALLNEW -eq 1 && $currentversion == N/A ]] && update=1 # force when getting all
	# forced because of installtrigger or updateonlyinstalled=0
	[[ $FORCEINSTALL -eq 1 && $remarkonly -eq 0 ]] && update=1
	# compare packages
	if [[ $update -eq 0 ]] ; then
		local usebuildtime=$USEBUILDTIME # can be overwritten in vcmp
		if [[ $USEBUILDTIME -eq 0 ]] ; then
			[[ $currentversion == $newversion ]] && return # same version
			[[ -n $currentversion && $currentversion != N/A ]] && \
				vcmp "$newversion" \> "$currentversion" && update=1
			[[ $currentversion == N/A ]] && update=1
		fi
		if [[ $usebuildtime -eq 1 && $currentversion != N/A ]] ; then
			[[ -n $newbuildtime && -n $curbuildtime && \
				$newbuildtime -gt $curbuildtime ]] && update=1
		fi
	fi
	origurl=$rpm
	if [[ $rpm == *://* ]] ; then # check for absolute http/ftp links
		dlfile=$rpm
	else
		if [[ $NODOTSUSEVERSION -lt 101 && $SERVERPATH != *$ARCH/update/$SUSEPRODUCT$SUSEVERSION* ]] ; then
			dlfile=$SERVERURL/$rpm
		elif [[ $NODOTSUSEVERSION -ge 101 && $SERVERPATH != *update/$SUSEPRODUCT$SUSEVERSION* ]] ; then
			dlfile=$SERVERURL/$rpm
		else
			dlfile=$SERVERURL/$SERVERPATH/$rpm
		fi
	fi
	rpm="$rpmpath/$baserpm" # build again without ../../crappy//things

	# try to prevent download of one package several times
	# for each package we create version_<packagename> variable with the version
	# that was found. if this variable exists, tha package has already been found
	if [[ -n $alreadyfound ]] ; then
		foundarch=packageinfo_$r
		foundarch=(${!foundarch}) foundarch=${foundarch[4]}
		if [[ $alreadyfound == $newversion ]] ; then
			if ! betterArch "$arch" "$foundarch"; then
				myWarn 2 "Using ${rpm##*/} as alternative (same version already found)."
				eval dlfile1_$r=$dlfile
				eval server1_$r=$server
				return
			fi
		fi
		if vcmp "$alreadyfound" \> "$newversion" ; then
			myWarn 2 "Not processing ${rpm##*/} (newer version already found)."
			return
		fi
	fi
	[[ $update -eq 0 ]] && return
	p=${pkgdescname//[^a-zA-Z0-9_]/_}
	val="$newversion $currentversion $kind $size $series $remarkonly $rpm $dlfile $server $oldhdsize $newhdsize $i $pkgdescname $newbuildtime $PREINFO $sourcerpm $origurl $SERVERPATH $usepatchrpmnow $patchsize $SERVERURL $usedeltarpmnow $deltasize $deltarpm $patchrpm"
	eval packageinfo_$r='$val'
	eval package_${p}_MG_$r='$pkg'
	eval shortdesc_$r='$shortdesc'
	eval longdesc_$r='$longdesc'
	eval license_$r='$license'
	if [[ $UPDATEONLYINSTALLED -eq 0 && $COMPATIBLE -eq 1 && $PENDINGCOUNT -gt 0 ]] ; then
		myWarn 3 "Forcing $PENDINGCOUNT pending packages"
		FORCEINSTALL=1
		count=$((PENDINGCOUNT-1)) # seq command requires this
		PENDINGCOUNT=0 # recursion would fail if we leave it to some other value
		for args in "${PENDINGARRAY[@]}" ; do
			ALLCOUNT=$((ALLCOUNT-1)) # we check again, therefore must decrement
			checkPackage $args
		done
	fi
} # CheckPackage


################################# handleInstallTrigger
# sets FORCEINSTALL if install trigger returns true (0)
function handleInstallTrigger()
{
	if [[ $SAFEPKGDESC -eq 1 ]] ; then # only signed packages!
		TMPFILE=`mktemp $CACHEDIR/insttrigger.XXXXXX`
		if [[ $? -eq 0 ]] ; then
			while read ; do
				case "$REPLY" in
					Reggirtllatsni:)
					break ;;
				esac
				echo "$REPLY" >> $TMPFILE
			done
			chmod +x $TMPFILE
			$TMPFILE # execute install trigger
			if [[ $? -eq 0 ]] ; then
				myEcho 3 "Forcing install of $pkg because of installtrigger"
				FORCEINSTALL=1
			fi
			# FIXME what should we do, if install trigger returns false?
			# Normal checking or ignoring this package?!?! Currently: normal check
			rm -f $TMPFILE
		else
			myError 0 "Error creating tempfile!! Installtrigger cannot be checked!"
		fi
	else
		myWarn 0 "WARNING: Installtrigger found in unsafe patch desc. ${i##*/} - IGNORING!"
	fi
} # handleInstallTrigger


################################# showRpmSummary
# shows summary of one available rpm package (fou4s -l)
function showRpmSummary
{
	if packageFiltered $pkg $kind $series; then
		return
	fi
	if [[ $VERBOSE -ge 1 && $LISTAVAILRPMS -eq 1 ]] && \
		! packageFiltered $pkg $kind $series; then
		[[ $updateinfoshown -eq 0 ]] && printUpdateInformation $i "$shortdesc" && updateinfoshown=1
	fi
	newhdsize=0 # for correct summary
	updateStatistics
	size=$((size/1024))
	ckind=$COL_NORM
	[[ $kind == security ]] && ckind="$COL_RED"
	[[ $kind == recommended ]] && ckind="$COL_CYAN"
	printf "$COL_WHITE%-35b $COL_GREEN%-18b $ckind%-14b$COL_NORM %5bkb \n" "$pkg" "$newversion" "$kind" "$size"
} # showRpmSummary


################################# processXMLDescRPM
# save parsed information from xml description (SuSE 10.1 .. 10.3) to internal
# variables
function processXMLDescRPM()
{
	myNote 2 "processing rpm $rpm pkg $pkg version $newversion cat $kind arch $series"
	if [[ $LISTAVAILRPMS -eq 1 ]] ; then # only show available rpms
		showRpmSummary
	else
		if [[ $USECACHE -ge 1 ]] ; then
			for p in pkg rpm newversion kind size newbuildtime series filename shortdesc patchbase patchsize newhdsize sourcerpm PREINFO POSTINFO UPDATEONLYINSTALLED deltasize deltarpm patchrpm longdesc license scriptcode; do
				pkg1=${pkg//[^a-zA-Z0-9_-]/}
				echo "$p='${!p//\'/\"}'" >> $cachefile.$pkg1
			done
		fi
		checkPackage
	fi

	# reset values in case that the next loop will not refill them
	series=default rpm=unknown pkg=unknown filename=unknown.rpm
	newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 
	patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
	deltarpm=unknown patchrpm=unknown
} 


################################# processUpdateinfoRPM
# save parsed information from xml description (SuSE >= 11.0) to internal
# variables
function processUpdateinfoRPM()
{
	newversion=$newversion1-$newversion2
	myNote 2 "processing updateinfo rpm $filename pkg $pkg version $newversion cat $kind arch $arch in $pkgdescname"
	series=$arch
	# echo Needing filename $filename from pkg $pkg pkgdescname $pkgdescname kind $kind ver $newversion
	data="$"
	p="file_${pkg//[^a-zA-Z0-9_]/_}__${newversion//[^a-zA-Z0-9_]/_}__$arch"
	# echo "fixme $p -- ${!p}"
	primaryinfo=(${!p})
	newversion=${primaryinfo[0]}
	size=${primaryinfo[1]}
	arch=${primaryinfo[2]}
	rpm=${primaryinfo[3]}
	server=${primaryinfo[4]}
	newhdsize=${primaryinfo[5]}
	newbuildtime=${primaryinfo[6]}
	p="deltafile_${pkg//[^a-zA-Z0-9_]/_}__${newversion//[^a-zA-Z0-9_]/_}__$arch"
	deltainfo=(${!p})
	deltasize=${deltainfo[0]}
	deltarpm=${deltainfo[1]}
	i=$pkgdescname

	if [[ $LISTAVAILRPMS -eq 1 ]] ; then # only show available rpms
		showRpmSummary
	else
		if [[ $USECACHE -ge 1 ]] ; then
			pkg1=${pkg//[^a-zA-Z0-9_-]/}
			for p in pkg rpm newversion kind size newbuildtime series filename shortdesc patchbase patchsize newhdsize sourcerpm PREINFO POSTINFO UPDATEONLYINSTALLED deltasize deltarpm patchrpm longdesc license scriptcode pkgdescname i; do
				echo "$p='${!p//\'/\"}'" >> $cachefile.$pkgdescname.$pkg1.$series
			done
		fi
		checkPackage
	fi

	# reset values in case that the next loop will not refill them
	series=default rpm=unknown pkg=unknown filename=unknown.rpm
	newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 
	patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
	deltarpm=unknown patchrpm=unknown PREINFO=0
} 


################################# processdeltainfoRPM
# save parsed information from delta xml description (SuSE >= 11.0) to internal
# variables
function processdeltainfoRPM()
{
	newversion="$newversion1-$newversion2"
	myNote 2 "processing delta rpm $deltarpm pkg $pkg version $newversion arch $deltaarch deltarpm $deltarpm"
	
	p=${pkg//[^a-zA-Z0-9_]/_}__${newversion//[^a-zA-Z0-9_]/_}__$deltaarch
	val="$deltasize $deltarpm"
	eval deltafile_$p='$val'

	# reset values in case that the next loop will not refill them
	series=default rpm=unknown pkg=unknown filename=unknown.rpm
	newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 
	patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
	deltarpm=unknown patchrpm=unknown
} 


################################# processPrimaryRPM
# save parsed information from xml description (SuSE >= 11.0) to internal
# variables
function processPrimaryRPM()
{
	myNote 2 "processing primary rpm $rpm pkg $pkg version $newversion arch $series"
	
	p=${primaryfilename//[^a-zA-Z0-9_]/_}__${newversion//[^a-zA-Z0-9_]/_}__$arch
	val="$newversion $size $arch $rpm $server $newhdsize $newbuildtime $SERVERPATH $SERVERURL"
	eval file_$p='$val'
	echo "file_$p='$val'" >> $cachefile.tmp

	# reset values in case that the next loop will not refill them
	series=default rpm=unknown pkg=unknown filename=unknown.rpm
	newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 
	patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
	deltarpm=unknown patchrpm=unknown
} 
################################# processPrimaryXML
# cycle through primary.xml, updateinfo.xml and deltainfo.xml from openSUSE 11.0 and newer
function processPrimaryXML()
{
	local rpm pkg newversion size newbuildtime filename patchbase patchsize
	local sourcerpm readingPkg series updateinfoshown shortdesc newhdsize kind
	local POSTINFO PREINFO FORCEINSTALL PENDINGCOUNT PENDINGARRAY
	local UPDATEONLYINSTALLED SAFEPKGDESC i deltasize deltarpm first longdesc
	local patchrpm license scriptcode

	myEcho 2 "Checking package information on $server"
	if [[ ! -f primary.xml.gz || ! -f updateinfo.xml.gz ]] ; then
		myWarn 0 "Description files (primary/deltainfo/updateinfo) are not complete for $server"
		return 1
	fi
	[[ $USEPROGRESS -eq 1 ]] && MAXCOUNT=`zgrep "<name>" primary.xml.gz 2>/dev/null| wc -l`
	[[ $MAXCOUNT == 0 ]] && MAXCOUNT=1 # otherwise div/0 would occur ...
	ALLCOUNT=0 # reset progress bar
	myNote 2 "########## Processing primary.xml on $server"
	# [[ ! -f $i ]] && myWarn 2 "Missing patch description $i" && continue
	cachefile=$CACHEDIR/.cache.$HOSTNAME/primary.$LANGUAGE.$server
	newbuildtime=0
	pkgdescname=UNKNOWN
	rpm=unknown pkg=unknown newversion=0.0-0 size=0 newbuildtime=0 newhdsize=0
	filename=unknown.rpm patchbase= patchsize=0 sourcerpm=unknown oldhdsize=0
	kind=unknown
	readingPkg=0 #currently (not) reading a package info from the pkgdesc file
	series=default # initialisation to sane defaults
	updateinfoshown=0 # indicates if update information has been shown
	shortdesc= # short description
	POSTINFO=0 # postinstall information present?
	FORCEINSTALL=0 # force installation because of installtrigger
	PREINFO=0 PENDINGCOUNT=0 PENDINGARRAY=()
	UPDATEONLYINSTALLED=1 # default!!
	SAFEPKGDESC=$SKIPGPG # use default from disable gpg option
	deltarpm=unknown deltasize=0 first=1 longdesc= patchrpm=unknown license=
	deltasize1= deltabaserel= deltabasever= currentversion= deltaseqinfo=
	scriptcode= deltachecksum=0
	i=UNK

	if [[ $DESCCACHE -nt primary.xml.gz && $DESCCACHE -nt deltainfo.xml.gz && $DESCCACHE -nt updateinfo.xml.gz ]] ; then
		myEcho 2 "Skipping server $server"
		return 0
	fi
	if [[ $USECACHE -ge 1 && -f $cachefile && $cachefile -nt primary.xml.gz ]] ; then
		myEcho 2 "Reading primary xml cache $cachefile ($server)"
		source $cachefile
	else
		xmlparsed=`mktemp $CACHEDIR/.cache.$HOSTNAME/xmlprim.XXXXXXXX`
		finished=0
		{
			zcat primary.xml.gz | $XMLP > $xmlparsed
		#xmlpcount=`wc -l < $xmlparsed`
		#MAXCOUNT=$((MAXCOUNT+xmlpcount))
		myEcho 2 "Reading primary.xml"
		while read line ; do
			# does not work because it is inside a subshell
			##[[ $USEPROGRESS -eq 1 ]] && showProgress $ALLCOUNT $MAXCOUNT 
			##ALLCOUNT=$((ALLCOUNT+1))
			line=${line//\/metadata\/package\//}
			if [[ $line == /metadata/package ]] ; then
				if [[ $first -eq 0 && readingPkg -eq 1 ]] ; then
					processPrimaryRPM
				else
					first=0
				fi
				newbuildtime=0
				rpm=unknown pkg=unknown newversion=0.0-0 size=0 newbuildtime=0 newhdsize=0
				filename=unknown.rpm 
				series=default # initialisation to sane defaults
				readingPkg=1 
			fi
			[[ $first -eq 0 && $readingPkg -eq 0 ]] && continue

			case $line in 
			arch=*) 
				series="${line##*=}" 
				arch=$series
					[[ -n $series && $series != default && $series != @(${VALIDARCHS// /|}) ]] && readingPkg=0 && myEcho 3 Ignoring package $pkg with foreign arch $series
				;;
			location/@href=*)
				rpm=${line##*=} ;;
			name=*)
				pkg=${line##*=} 
				primaryfilename="${line##*=}" 
				filename=$pkg.rpm ;;
			version/@ver=*)
				newversion=${line##*=} ;;
			version/@rel=*)
				newversion=$newversion-${line##*=} ;;
			size/@package=*)
				size=${line##*=} ;;
			size/@installed=*)
				newhdsize=${line##*=} ;;
			time/@build=*)
				newbuildtime=${line##*=} ;;
			esac
		done < $xmlparsed
		if [[ $readingPkg -eq 1 ]] ; then
			processPrimaryRPM
		fi
		rm -f $xmlparsed
		}&
		if [[ $THREADS -eq 0 ]] ; then
			wait
		fi
	fi
	if [[ $USECACHE -ge 1 && $cachefile -nt deltainfo.xml.gz ]] ; then
		myEcho 2 "Reading deltainfo from cache"
		# source $cachefile.delta
	elif [[ $USEDELTARPMS -eq 1 ]] ; then
		myEcho 2 "Reading deltainfo.xml ..." # todo 2
		xmlparsed=`mktemp $CACHEDIR/.cache.$HOSTNAME/xmlpdelta.XXXXXXXX`
		zcat deltainfo.xml.gz | $XMLP > $xmlparsed
		xmldeltacount=`wc -l < $xmlparsed`
		MAXCOUNT=$((MAXCOUNT+xmldeltacount))
		readingPkg=0
		firstpkg=1
		while read line ; do
			[[ $USEPROGRESS -eq 1 ]] && [[ $USEPROGRESS -eq 1 ]] && showProgress $ALLCOUNT $MAXCOUNT 2
			ALLCOUNT=$((ALLCOUNT+1))
			#line=${line//\/deltainfo\/newpackage\//}
			if [[ $line == /deltainfo/newpackage ]]; then
				[[ $readingPkg -eq 1 ]] && processdeltainfoRPM
				readingPkg=0
				firstpkg=0
			fi
			case $line in 
			/deltainfo/newpackage/delta)
				if [[ $firstpkg == 1 ]] ; then
					firstpkg=0
				else
					# got one RPM from this updateinfo.xml.gz
					[[ $readingPkg -eq 1 ]] && processdeltainfoRPM
					readingPkg=0
				fi
			;;
			/deltainfo/newpackage/@version=*)
				newversion1="${line##*=}" ;;
			/deltainfo/newpackage/@release=*)
				newversion2="${line##*=}" ;;
			/deltainfo/newpackage/@name=*)
				pkg="${line##*=}" ;;
			/deltainfo/newpackage/delta/filename=*)
				deltarpm1="${line##*=}" ;;
			/deltainfo/newpackage/delta/size=*)
				deltasize1="${line##*=}" ;;
			/deltainfo/newpackage/delta/@oldversion=*|delta/@ver=*)
				deltabasever=${line##*=} ;;
			/deltainfo/newpackage/delta/@oldrelease=*|delta/@rel=*)
				deltabaserel=${line##*=} ;;
			/deltainfo/newpackage/@arch=*)
				deltaarch=${line##*=} ;;
			/deltainfo/newpackage/delta/sequence=*)
				deltaseqinfo=`echo "$line" | sed 's/^.deltainfo.newpackage.delta.sequence=//g'` ;;
			/deltainfo/newpackage/delta/checksum=*) # dummy usage because it is the last element
				deltarpm=unknown
				cacheinfo=pkg_${pkg//[^a-zA-Z0-9_]/_} cacheinfo=(${!cacheinfo})
				currentversion=${cacheinfo[0]}
				currentarch=${cacheinfo[2]}
				
				if [[ -n $deltaseqinfo && $deltabasever-$deltabaserel == $currentversion && "$deltaarch" == "$currentarch" ]] ; then
					myEcho 2 "Checking if delta RPM $deltabasever-$deltabaserel-$deltaarch for $pkg-$currentversion-$currentarch size $deltasize1 works ... $PWD $deltarpm1"
					if [[ -f ../${deltarpm1##.delta.rpm}.rpm ]] || applydeltarpm -c -s "$deltaseqinfo" 2>/dev/null ; then
						myWarn 3 "Found delta for $pkg: $deltarpm1"
						deltachecksum=${line##*=}
						deltasize=$deltasize1
						deltarpm=$deltarpm1
						readingPkg=1
					fi
				else
					deltasize=0
					myEcho 2 "NOT Checking if delta RPM $deltabasever-$deltabaserel-$deltaarch for $pkg-$currentversion-$currentarch size $deltasize1 works ..."
				fi
				;;
			/deltainfo/newpackage/delta/@arch=*)
				arch=${line##*=}
				readingPkg=1
				[[ $arch != @(${VALIDARCHS// /|}) ]] && readingPkg=0 && myEcho 2 Ignoring package $pkg with foreign arch $arch in deltainfo
				;;
			esac
		done < $xmlparsed
		rm -f $xmlparsed
	fi
	if [[ $readingPkg -eq 1 ]] ; then
		processdeltainfoRPM
	fi

	#if [[ -f $cachefile-1 && $cachefile.1 -nt updateinfo.xml.gz ]] ; then
	# fixme check this
	if ! [[ $USECACHE -ge 1 && $LISTAVAILRPMS -eq 0 && -f $cachefile-1 \
		&& $cachefile-1 -nt updateinfo.xml.gz ]] ; then
		myEcho 2 "Reading updateinfo.xml ..." # todo 2
		xmlparsed=`mktemp $CACHEDIR/.cache.$HOSTNAME/xmlpu.XXXXXXXX`
		zcat updateinfo.xml.gz | $XMLP > $xmlparsed
	fi

	wait
	ALLCOUNT=0
	test -f $cachefile.tmp && mv $cachefile.tmp $cachefile
	# read cached info from primary.xml
	source $cachefile

	if [[ $USECACHE -ge 1 && $LISTAVAILRPMS -eq 0 && -f $cachefile-1 \
		&& $cachefile-1 -nt updateinfo.xml.gz ]] ; then
		myEcho 2 "Reading updateinfo from cache ($server)"
		for j in $cachefile.* ; do
			test -f $j && source $j  || continue
			checkPackage
			# reset values in case that the next loop will not refill them
			series=default rpm=unknown pkg=unknown filename=unknown.rpm
			newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 
			patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
			deltarpm=unknown patchrpm=unknown PREINFO=0 deltachecksum=0
		done
	else
		myEcho 2 "Parsing updateinfo.xml ..." # todo 2
		if [ $LISTAVAILRPMS -eq 1 ] ; then
			rm -f $cachefile-1
		else
			touch $cachefile-1
		fi
		firstpkg=1
		firstupdate=1
		while read line ; do
			line=${line//\/updates\/update\//}
			if [[ $line == /updates/update ]] ; then
				if [[ $readingPkg -eq 1 ]] ; then
					processUpdateinfoRPM
				fi
				firstupdate=0
				firstpkg=1
				readingPkg=0
				pkgdescname= kind= longdesc= filename=
				updateinfoshown=0
			fi
			# info: 
			# @version, @type, id, title/@, issued/@date, description
			# pkglist/collection/package/@release,@name,@arch,@version,filename=
			# echo reading updateinfo line $line
			case $line in 
			@version=*) 
				pkgdescname="${line##*=}" i=$pkgdescname ;;
			@type=*) 
				kind="${line##*=}" ;;
			id=*) 
				pkg="${line##*=}" 
				pkgdescname="${line##*=}-$pkgdescname" ;;
			description=*)
				longdesc=${line##*=} 
				longdesc=${longdesc%} 
				longdesc=${longdesc///=} ;;
			title=*)
				shortdesc=${line##*=} 
				shortdesc=${shortdesc%} 
				shortdesc=${shortdesc///=} ;;
			pkglist/collection/package)
				if [[ $firstpkg == 1 ]] ; then
					firstpkg=0
				else
					# got one RPM from this updateinfo.xml.gz
					[[ $readingPkg -eq 1 ]] && processUpdateinfoRPM
				fi
			;;
			pkglist/collection/package/@version=*)
				newversion1="${line##*=}"  ;;
			pkglist/collection/package/@release=*)
				newversion2="${line##*=}"  ;;
			pkglist/collection/package/@name=*)
				pkg="${line##*=}"  ;;
			pkglist/collection/package/filename=*)
				filename="${line##*=}"  ;;
			pkglist/collection/package/reboot_suggested=*)
				line="${line##*=}"
				if [[ $line == 1 ]] ; then
					PREINFO=1
					license="A reboot is suggested after installing this update"
				fi
				;;
			pkglist/collection/package/@arch=*)
				arch=${line##*=}
				readingPkg=1
				[[ $arch != @(${VALIDARCHS// /|}) ]] && readingPkg=0 && myEcho 2 Ignoring package $pkg with foreign arch $arch in updateinfo $pkgdescname
				;;
			*)
			#echo "ignoring $line in updateinfo"
			;;
			esac
		done < $xmlparsed
		if [[ $readingPkg -eq 1 ]] ; then
			processUpdateinfoRPM
		fi
	fi
	rm -f $xmlparsed
}

################################# processXMLPatchDescriptions
# cycle through all XML package description files for SUSE 10.1 .. 10.3
# Parameters:
# $*: the patch description files
function processXMLPatchDescriptions()
{
	local rpm pkg newversion size newbuildtime filename patchbase patchsize
	local sourcerpm readingPkg series updateinfoshown shortdesc newhdsize kind
	local POSTINFO PREINFO FORCEINSTALL PENDINGCOUNT PENDINGARRAY
	local UPDATEONLYINSTALLED SAFEPKGDESC i deltasize deltarpm first longdesc
	local patchrpm license scriptcode

	myEcho 2 "Need to check $# package information files on $server"
	[[ $# -eq 0 ]] && return
	[[ $USEPROGRESS -eq 1 ]] && MAXCOUNT=`grep "<name>" $* 2>/dev/null| wc -l`
	[[ $MAXCOUNT == 0 ]] && MAXCOUNT=1 # otherwise div/0 would occur ...
	ALLCOUNT=0 # reset progress bar
	for i in $* ; do
		pkgdescname=${i##*/patch-}
		pkgdescname=${pkgdescname%%.xml}
		myNote 3 "########## Processing XML patch description $i $pkgdescname"
		[[ ! -f $i ]] && myWarn 2 "Missing patch description $i" && continue
		cachefile=$CACHEDIR/.cache.$HOSTNAME/$i.$LANGUAGE
		newbuildtime=0
		rpm=unknown pkg=unknown newversion=0.0-0 size=0 newbuildtime=0 newhdsize=0
		filename=unknown.rpm patchbase= patchsize=0 sourcerpm=unknown oldhdsize=0
		kind=unknown
		readingPkg=0 #currently (not) reading a package info from the pkgdesc file
		series=default # initialisation to sane defaults
		updateinfoshown=0 # indicates if update information has been shown
		shortdesc= # short description
		POSTINFO=0 # postinstall information present?
		FORCEINSTALL=0 # force installation because of installtrigger
		PREINFO=0 PENDINGCOUNT=0 PENDINGARRAY=()
		UPDATEONLYINSTALLED=1 # default!!
		SAFEPKGDESC=$SKIPGPG # use default from disable gpg option
		deltarpm=unknown deltasize=0 first=1 longdesc= patchrpm=unknown license=
		deltasize1= deltabaserel= deltabasever= currentversion= deltaseqinfo=
		scriptcode=

		if [[ $USECACHE -ge 1 ]] ; then
			if [[ -f $cachefile && $cachefile -nt $i ]] ; then
				for cf in $cachefile.* ; do
					if [[ -f $cf ]] ; then
						source $cf
						if [[ $LISTAVAILRPMS -eq 1 ]] ; then # only show available rpms
							showRpmSummary
						else
							checkPackage
						fi
					fi
				done
				continue
			fi
			mkdir -p ${cachefile%/*} # create directory for cache file
			rm -f $cachefile*
			touch $cachefile # empty dummy
		fi

		xmlparsed=`mktemp $CACHEDIR/.cache.$HOSTNAME/xmlp.XXXXXXXX`
		$XMLP <$i > $xmlparsed
		while read line ; do
			line=${line//\/patch\//}
			if [[ $line == atoms/package ]] ; then
				if [[ $first -eq 0 && readingPkg -eq 1 ]] ; then
					processXMLDescRPM
				else
					first=0
				fi
				readingPkg=1 
			fi
			[[ $first -eq 0 && $readingPkg -eq 0 ]] && continue

			case $line in 
			atoms/package/arch=*) 
				series="${line##*=}" 
				arch=$series
					[[ -n $series && $series != default && $series != @(${VALIDARCHS// /|}) ]] && readingPkg=0 && myEcho 2 Ignoring package $pkg with foreign arch $series
				;;
			category=*) 
				kind="${line##*=}" ;;
			license-to-confirm=*) 
				PREINFO=1 ;;
			atoms/package/location/@href=*)
				rpm=${line##*=} ;;
			atoms/package/name=*)
				pkg=${line##*=} 
				filename=$pkg.rpm;;
			atoms/package/version/@ver=*)
				newversion=${line##*=} ;;
			atoms/package/version/@rel=*)
				newversion=$newversion-${line##*=} ;;
			atoms/package/size/@package=*)
				size=${line##*=} ;;
			atoms/package/size/@installed=*)
				newhdsize=${line##*=} ;;
			atoms/package/time/@build=*)
				newbuildtime=${line##*=} ;;
			description/@lang=en)
				read line
				[[ $line == /patch/description=* ]] && longdesc=${line##*=} 
				longdesc=${longdesc%} longdesc=${longdesc///=}
				;;
			summary/@lang=en)
				read line
				[[ $line == /patch/summary=* ]] && shortdesc=${line##*=} ;;
			license-to-confirm)
				read line
				[[ $line == /patch/license-to-confirm=* ]] && PREINFO=1 license=${line##*=} || read line
				[[ $line == /patch/license-to-confirm=* ]] && PREINFO=1 license=${line##*=} 
				;;
			atoms/package/pkgfiles/patchrpm/location/@href=*)
				patchrpm=${line##*=} ;;
			atoms/package/pkgfiles/patchrpm/size/@package=*)
				patchsize=${line##*=} ;;
			atoms/package/pkgfiles/patchrpm/base-version/@ver=*)
				patchbase=${line##*=} ;;
			atoms/package/pkgfiles/patchrpm/base-version/@rel=*)
				patchbase=$patchbase-${line##*=} ;;
			atoms/package/pkgfiles/deltarpm/location/@href=*)
				deltarpm1=${line##*=} ;;
			atoms/package/pkgfiles/deltarpm/size/@package=*)
				deltasize1=${line##*=} ;;
			atoms/package/pkgfiles/deltarpm/base-version/@ver=*)
				deltabasever=${line##*=} ;;
			atoms/package/pkgfiles/deltarpm/base-version/@rel=*)
				deltabaserel=${line##*=}
				cacheinfo=pkg_${pkg//[^a-zA-Z0-9_]/_} cacheinfo=(${!cacheinfo})
				currentversion=${cacheinfo[0]}
				myEcho 2 "Checking if delta RPM $deltabasever-$deltabaserel for $pkg-$currentversion works ..."
				if [[ -n $deltaseqinfo && $deltabasever-$deltabaserel == $currentversion ]] && applydeltarpm -c -s "$deltaseqinfo" 2>/dev/null ; then
					myWarn 3 "Found delta for $pkg: $deltarpm1"
					deltasize=$deltasize1
					deltarpm=$deltarpm1
				else
					deltasize=0
					deltarpm=unknown
				fi
				;;
			atoms/package/pkgfiles/deltarpm/base-version/@sequence_info=*)
				deltaseqinfo=${line##*=} ;;
			atoms/script/do=*)
				scriptcode=${line##*=} ; scriptcode=${scriptcode///
}
				processXMLDescRPM ;;
			# *) echo "line $line" ;;
			esac
		done < $xmlparsed
		if [[ $readingPkg -eq 1 ]] ; then
			processXMLDescRPM
		fi
		rm -f $xmlparsed
	done
}

################################# processPatchDescriptions
# cycle through all package description files
# Parameters:
# $*: the patch description files
function processPatchDescriptions()
{
	local rpm pkg newversion size newbuildtime filename patchbase patchsize
	local sourcerpm readingPkg series updateinfoshown shortdesc newhdsize kind
	local POSTINFO PREINFO FORCEINSTALL PENDINGCOUNT PENDINGARRAY
	local UPDATEONLYINSTALLED SAFEPKGDESC i deltasize deltarpm
	myEcho 2 "Need to check $# package information files on $server"
	[[ $# -eq 0 ]] && return
	[[ $USEPROGRESS -eq 1 ]] && MAXCOUNT=`grep ^Filename: $* 2>/dev/null| wc -l`
	[[ $MAXCOUNT == 0 ]] && MAXCOUNT=1 # otherwise div/0 would occur ...
	ALLCOUNT=0 # reset progress bar
	for i in $* ; do
		pkgdescname=${i##*/}
		myNote 3 "########## Processing patch description $i"
		[[ ! -f $i ]] && myWarn 2 "Missing patch description $i" && continue
		cachefile=$CACHEDIR/.cache.$HOSTNAME/$i.$LANGUAGE
		newbuildtime=0
		rpm=unknown pkg=unknown newversion=0.0-0 size=0 newbuildtime=0 newhdsize=0
		filename=unknown.rpm patchbase= patchsize=0 sourcerpm=unknown oldhdsize=0
		kind=unknown
		readingPkg=0 #currently (not) reading a package info from the pkgdesc file
		series=default # initialisation to sane defaults
		updateinfoshown=0 # indicates if update information has been shown
		shortdesc= # short description
		POSTINFO=0 # postinstall information present?
		FORCEINSTALL=0 # force installation because of installtrigger
		PREINFO=0 PENDINGCOUNT=0 PENDINGARRAY=()
		UPDATEONLYINSTALLED=0 # default!!
		SAFEPKGDESC=$SKIPGPG # use default from disable gpg option
		deltarpm=unknown deltasize=0
		if [[ $USECACHE -ge 1 ]] ; then
			if [[ -f $cachefile && $cachefile -nt $i ]] ; then
				for cf in $cachefile.* ; do
					if [[ -f $cf ]] ; then
						source $cf
						if [[ $LISTAVAILRPMS -eq 1 ]] ; then # only show available rpms
							showRpmSummary
						else
							checkPackage
						fi
					fi
				done
				continue
			fi
			mkdir -p ${cachefile%/*} # create directory for cache file
			rm -f $cachefile*
			touch $cachefile # empty dummy
		fi
		while read tag rest; do
			if [[ -z $tag && $readingPkg -eq 0 ]] ; then
				continue
			fi
			tag=${tag//\`/}  # kill special shell characters
			tag=${tag//\$(/} # kill special shell characters
			# if we are currently reading a package, it is finished with one of
			# the following strings
			if [[ $readingPkg -eq 1 ]] ; then
				processpkg=0
				case "$tag" in
				Segakcap:|-----BEGIN|Filename:)
					[[ $pkg != unknown && -n $pkg ]] && processpkg=1 ;;
				esac
				if [[ $processpkg -eq 1 ]] ; then
					if [[ $LISTAVAILRPMS -eq 1 ]] ; then # only show available rpms
						showRpmSummary
					else
						pkg=${pkg%%-$newversion*}
						pkg1=${pkg//[^a-zA-Z0-9_-]/}
						if [[ $USECACHE -ge 1 ]] ; then
							for p in pkg rpm newversion kind size newbuildtime series filename shortdesc patchbase patchsize newhdsize sourcerpm PREINFO POSTINFO UPDATEONLYINSTALLED deltasize deltarpm; do
								echo "$p='${!p//\'/\"}'" >> $cachefile.$pkg1
							done
						fi
						checkPackage # "$pkg" "$rpm" "$newversion" "$kind" "$size" "$patchsize" "$newbuildtime" "$series" "$filename" "$shortdesc"
					fi
					# reset values in case that the next loop will not refill them
					series=default rpm=unknown pkg=unknown filename=unknown.rpm
					newversion=0.0-0 size=0 newbuildtime=0 readingPkg=0 shortdesc=
					patchbase= patchsize=0 sourcerpm=unknown newhdsize=0 deltasize=0
					deltarpm=unknown
				fi
			fi

			case "$tag" in # get tags from patch description
			Deltas:)
				[[ $readingPkg -eq 0 ]] && continue
				[[ $USEDELTARPMS -lt 0 ]] && continue
				while true ; do
					read tag rest
					[[ $tag == Satled: ]] && break
					rest=($rest) # make array
					myEcho 2 "Checking if delta RPM for $pkg works ..."
					if applydeltarpm -c -s "${rest[5]}" 2>/dev/null ; then
						# we assume that there is only one matching delta rpm
						deltasize=${rest[0]}
						deltarpm="$tag"
						myEcho 3 "Found delta for $pkg: $tag"
					fi
				done
			;;
			UpdateOnlyInstalled:)
				if [[ $rest == true ]] ; then
					UPDATEONLYINSTALLED=1
				fi ;;
			Filename:)
				pkg=${rest%%.rpm}
				readingPkg=1 # we have found a new package within the pkgdesc
				filename=$rest ;;
			Version:)
				newversion=$rest
				if [[ -z $newversion ]] ; then
					newversion=fix-me
					[[ $VERBOSE -ge 1 ]] && \
						echo "Invalid version found in $i! Please read http://fou4s.gaugusch.at/problems.shtml and report to fou4s@gaugusch.at if it is not known." | fmt -$COLUMNS
				fi ;;
			BuiltFrom:)
				sourcerpm="rpm/src/$rest" ;;
			Prescript:*|Postscript:*)
				series=script
				FORCEINSTALL=1 # because we have no version comparison
				[[ -z $rest ]] && rest=${tag##Prescript:} # fix missing blank
				pkg=$rest rpm=scripts/$rest newversion=script kind=script size=0
				pkg1=$pkg
				patchsize=0 newbuildtime=0
				if [[ $USECACHE -ge 1 ]] ; then
					for p in pkg rpm newversion kind size patchsize newbuildtime series filename shortdesc patchbase newhdsize sourcerpm PREINFO POSTINFO UPDATEONLYINSTALLED FORCEINSTALL; do
						echo "$p='${!p//\'/\"}'" >> $cachefile.$pkg1
					done
				fi
				checkPackage # "$rest" "scripts/$rest" "script" "script" "0" "0" "0" "$series" "$rest" "$shortdesc"
				break ;;
			Shortdescription.english:)
				[[ -z $shortdesc ]] && shortdesc=${rest//\`/\'} shortdesc=${shortdesc//$/$ } ;;  # vim syntax helper: '
			Shortdescription.$LANGUAGE:)
				[[ -n $rest ]] && shortdesc=${rest//\`/\'} shortdesc=${shortdesc//$/$ } ;;  # vim syntax helper: '
			InstPath:)
				rpm=$rest ;;
			Kind:)
				kind=$rest ;;
			Postinformation.english*|Postinformation.$LANGUAGE*)
				POSTINFO=1 ;;
			Preinformation.english*|Preinformation.$LANGUAGE*)
				PREINFO=1 ;;
			Series:)
				series=$rest
				if [[ $NODOTSUSEVERSION -ge 92 ]] ; then
					[[ -n $series && $series != default && $series != @(${VALIDARCHS// /|}) ]] && readingPkg=0 && myEcho 2 Ignoring package $pkg with foreign arch $series
				fi
				[[ -z $series ]] && series="default" ;;
			Size:)
				[[ $readingPkg -eq 0 ]] && continue
				size=($rest) # convert to an array
				newhdsize=${size[0]}
				size=${size[1]} # get second field
				if [[ -z $size ]] ; then
					size=0
					[[ -n $newhdsize ]] && size=$newhdsize
					myEcho 2 "$pkg: Invalid size! Using $size"
				fi
				;;
			PatchRpmSize:)
				rest=($rest)
				patchsize=${rest[1]} # get second field
				;;
			PatchRpmBasedOn:)
				patchbase=$rest
				;;
			Buildtime:)
				newbuildtime=$rest ;;
			Installtrigger:)
				if [[ $SAFEMODE -eq 0 && $i != *-G ]] ; then
					hasGoodSignature "`gpg --no-tty --verify "$i" 2>&1`" DSA 9C800ACA "SuSE Package Signing Key <build@suse.de>"
					ret=$?
					if [[ $ret -eq 0 ]] ; then
						SAFEPKGDESC=1
					elif [[ $ret -eq 254 ]] ; then
						myEcho 3 "No signature in patch description ${i##*/} - using safe mode!"
					else
						[[ $QUIET -eq 0 ]] && myEcho 0 "Invalid signature in patch description ${i##*/} - using safe mode!"
					fi
				fi
				handleInstallTrigger ;;
			esac;
		done < $i
	done
} # processPatchDescriptions


################################# addToSkippedPatches
# Adds one or more entries to the skipped-patches file
# parameters:
# $*: The entries to add
function addToSkippedPatches()
{
	local pwd=$PWD
	cd "$BASEDIR"
	printf "%s\n" $* >> $SKIPPEDPATCHESFILE
	cd "$pwd"

	# prevent creating cache with bogus data at end of program
	[[ $USECACHE -eq 2 ]] && USECACHE=1
	rm -f $DESCCACHE $AUTOSKIPPEDPATCHESFILE &>/dev/null #force creation next time
} # addToSkippedPatches


################################# getOptionsFromConfigFile
# get options from config file
# Parameters:
# $1: filename
function getOptionsFromConfigFile()
{
	local CONFIGFILE=$1
	if [[ -f $CONFIGFILE ]] ; then
		while read optval ; do
			optval=${optval%%#*}
			[[ -z $optval ]] && continue
			key=${optval%%=*}
			value=${optval#*=}
			case $key in
			DefaultVerbosity)
				VERBOSE=`checkOption "$value" $key $VERBOSE range 0 3` ;;
			Proxy)
				export ALL_PROXY=`checkOption "$value" $key $ftp_proxy`
				export http_proxy=`checkOption "$value" $key $http_proxy`
				export https_proxy=`checkOption "$value" $key $https_proxy`
				export HTTPS_PROXY=`checkOption "$value" $key $https_proxy`
				export ftp_proxy=`checkOption "$value" $key $ftp_proxy`
				export FTP_PROXY=`checkOption "$value" $key $ftp_proxy` ;;
			AcceptPreinstallInfo)
				ACCEPTPREINSTALLINFO=`checkOption "$value" $key $ACCEPTPREINSTALLINFO` ;;
			Arch)
				ARCH=`checkOption "$value" $key $ARCH`
				FORCEDARCH=$ARCH ;;
			Auto)
				AUTOLIST=`checkOption "$value" $key "$AUTOLIST"` ;;
			Benchmark)
				BENCHMARK=`checkOption "$value" $key $BENCHMARK bool` ;;
			CacheDir)
				CACHEDIR=`checkOption "$value" $key $CACHEDIR` ;;
			CheckFou4s)
				CHECKFOU=`checkOption "$value" $key $CHECKFOU bool` ;;
			ColorOutput)
				COLOR=`checkOption "$value" $key $COLOR bool` ;;
			Compatible)
				COMPATIBLE=`checkOption "$value" $key $COMPATIBLE bool` ;;
			DescriptionCache)
				USECACHE=`checkOption "$value" $key $USECACHE range 0 2` ;;
			Exclude)
				EXCLUDELIST=`checkOption "$value" $key "$EXCLUDELIST"` ;;
			KernelBackup)
				KERNELBACKUP=`checkOption "$value" $key "$KERNELBACKUP" bool` ;;
			KernelCheck)
				KERNELCHECK=`checkOption "$value" $key "$KERNELCHECK" bool` ;;
			Language)
				LANGUAGE=`checkOption "$value" $key "$LANGUAGE"` ;;
			RpmDir)
				DLPATH=`checkOption "$value" $key $DLPATH` ;;
			SuSEUser)
				HTTPUSER=`checkOption "$value" $key $HTTPUSER` ;;
			SuSEPassword)
				HTTPPASSWD=`checkOption "$value" $key $HTTPPASSWD` ;;
			TestMode)
				TESTMODE=`checkOption "$value" $key $TESTMODE bool` ;;
			IgnoreList)
				IGNORELIST=`checkOption "$value" $key "$IGNORELIST"` ;;
			Interactive)
				INTERACTIVE=`checkOption "$value" $key $INTERACTIVE bool` ;;
			InverseColor)
				INVERSE=`checkOption "$value" $key $INVERSE bool`
				[[ $INVERSE -eq 1 ]] && BRIGHT=0 ;;
			LogFile)
				LOGFILE=`checkOption "$value" $key $LOGFILE` ;;
			LogLevel)
				LOGLEVEL=`checkOption "$value" $key $LOGLEVEL range 0 3` ;;
			OlderThan)
				DAYSOLDER=`checkOption "$value" $key $LOGLEVEL range 0 10000` ;;
			ProxyDigest)
				PROXYDIGEST=`checkOption "$value" $key $PROXYDIGEST bool` ;;
			ProxyUser)
				PROXYUSER=`checkOption "$value" $key $PROXYUSER` ;;
			ProxyPasswd)
				PROXYPASS=`checkOption "$value" $key $PROXYPASS` ;;
			RemarkList)
				REMARKLIST=`checkOption "$value" $key "$REMARKLIST"` ;;
			RemoveAfterInstall)
				REMOVE=`checkOption "$value" $key $REMOVE bool` ;;
			Server)
				SERVERLIST="$SERVERLIST `checkOption "$value" $key "$SERVERLIST"`";;
			SourceRpms)
				SRCRPMS="$SRCRPMS `checkOption "$value" $key "$SRCRPMS"`";;
			SuSEProduct)
				SUSEPRODUCT=`checkOption "$value" $key "$SUSEPRODUCT"` ;;
			SuSEVersion)
				SUSEVERSION=`checkOption "$value" $key $SUSEVERSION` ;;
			UseDeltaRpms)
				USEDELTARPMS=`checkOption "$value" $key $USEDELTARPMS bool` ;;
			UseCurl)
				USECURL=`checkOption "$value" $key $USECURL` ;;
			UseDir)
				USEDIR=`checkOption "$value" $key $USEDIR` ;;
			UsePatchRpms)
				PATCHRPMS=`checkOption "$value" $key $PATCHRPMS bool` ;;
			PackageListDir|RSyncServer|GpdList|UseFullPath)
				myWarn 0 "Fou4s Warning: Please remove the obsolete option $key from $CONFIGFILE";;
			*)
				myError 0 "Error: Unknown option in $CONFIGFILE: $key"
				exit 2 ;;
			esac
		done < $CONFIGFILE
	fi
} # getOptionsFromConfigFile vim"


# 'compile' the lists into patterns
# need to squeeze separators first
# then introduce | as separator
function listsToPatterns()
{
	local listvar tmp
	set -f # no globbing
	# eval because it makes extension easier
	for listvar in $*; do
		IFS=$' \t\n,'; tmp=(${!listvar})     # split on whitespace and comma
		IFS="|"; eval $listvar="'${tmp[*]}'" # join with "|"
		IFS=$' \t\n' # restore standard IFS
	done
	set +f
} # listsToPatterns vim"


################################# revert
# Revert a text and change first letter to a capital
function revert()
{
	local i j text=$1 res first
	text=`echo "$text" | tr '[A-Z]' '[a-z]'`
	local len=${#text}
	len=$((len-1))
	for i in `seq 1 $len` ; do
		i=$((i-1))
		j=$((i-1))
		res="${text:$i:1}$res"
	done
	res="`echo ${text:$len:1} | tr "[a-z]" "[A-Z]"`$res"
	echo $res
} # revert


################################# optionPostProcessing
# updates some variables after all options and arguments have been read
function optionPostProcessing()
{
	local o
	[[ -x `which dcop &>/dev/null` && -n $KONSOLE_DCOP_SESSION ]] && o=`dcop $KONSOLE_DCOP_SESSION schema` && [[ $o == "" ]] && INVERSE=1
	[[ $INVERSE -eq 1 ]] && BRIGHT=0
	# use color if stdin is a terminal - use | less -R to get colored
	# output when piping to less!
	if [[ $COLOR -eq 1 && -t 0 ]]; then
		COL_RED="\033[$BRIGHT;31m"
		COL_GREEN="\033[$BRIGHT;32m"
		COL_YELLOW="\033[$BRIGHT;33m"
		COL_WHITE="\033[$BRIGHT;37m"
		COL_CYAN="\033[$BRIGHT;36m"
		COL_BLACK="\033[0;30m"
		COL_NORM="\033[0m"
		if [[ $INVERSE -eq 1 ]] ; then
			COL_WHITE="\033[$BRIGHT;30m" # black
			COL_YELLOW="\033[0;33m" # dark yellow
			COL_CYAN="\033[$BRIGHT;34m" # blue
		fi
	fi
	# determine suse version from filename of rpmcache when in offline mode
	EGAUGNAL=`revert $LANGUAGE` # reverse string of language
	if [[ $LANGUAGE != english && -f `which recode 2>&1` ]] ; then
		DORECODE=1
	fi
	RPMVER=`rpm --version` # used for signature verification
	RPMVER=${RPMVER##*version }
	[[ $RPMVER == 4.0* ]] && RPMVER=30 # hack for puretec suse 8.0 with rpm v4
	[[ $RPMVER == 4.4.* ]] && RPMVER=44
	[[ $RPMVER == 4.7.* ]] && RPMVER=47
	[[ $RPMVER == 4.8.* ]] && RPMVER=48
	RPMVER=${RPMVER%%.*}
	[[ -z $RPMVER ]] && RPMVER=30 # just for safety
	[[ $RPMVER -lt 10 ]] && RPMVER=${RPMVER}0
	if [[ -t 0 ]] ; then
		COLUMNS=`stty size| cut -d " " -f 2`
	fi
	[[ -z $COLUMNS ]] && COLUMNS=80

	if [[ $EXPORT -eq 1 && $EXPORTFILE == *.tar ]] ; then
		[[ $EXPORTFILE != /* ]] && EXPORTFILE="$PWD/${EXPORTFILE#./}"
		OFFLINE=1 # don't query host if exporting for it!
		SUSEVERSION="*[0-9]" # must end with a number
	elif [[ $EXPORT -eq 1 && $EXPORTFILE != *.fou ]] ; then
		myError 0 "The parameter --export takes either a .fou file (offline machine) or a .tar file (internet connected machine)" | fmt -$COLUMNS
		exit 14
	fi

#	if [[ $MACHINEARCH == i?86 ]] ; then # don't allow i686 on i586 machines, etc
#		VALIDARCHS=noarch
#		for a in i386 i486 i586 i686 i786 ; do
#			VALIDARCHS="$VALIDARCHS $a"
#			[[ $a == $MACHINEARCH ]] && break
#		done
#	elif [[ $MACHINEARCH == x86_64 ]] ; then #amd64 also works with ix86 pkg
#		VALIDARCHS="noarch i386 i486 i586 i686 x86_64"
#	fi
	RPMCACHE=$CACHEDIR/.cache.$HOSTNAME/rpmcache.$SUSEVERSION # cache file with all versions
	if [[ $SUSEVERSION == \*\[0-9\] ]] ; then
		SUSEVERSION=`echo $RPMCACHE` # glob filename
		if [[ $SUSEVERSION == *\ * ]] ; then
			myError 0 "More than one cache file was found, please check $RPMCACHE"
			exit 20
		fi
		SUSEVERSION=${SUSEVERSION##*/} # strip path
		SUSEVERSION=${SUSEVERSION#*.} # strip "rpmcache."
		NODOTSUSEVERSION=${SUSEVERSION//.} # rebuild
		RPMCACHE=$CACHEDIR/.cache.$HOSTNAME/rpmcache.$SUSEVERSION # rebuild
	fi

	if [[ $OFFLINE -eq 1 && ! -f $RPMCACHE ]] ; then
		myError 0 "RPM cache for host $HOSTNAME not found - please import with --import or don't use --offline" | fmt -$COLUMNS
		myError 2 "Cachefile is $RPMCACHE"
		exit 15
	fi
	if [[ $IMPORT -eq 1 && -f $IMPORTFILE ]] ; then
		if [[ $IMPORTFILE == *.fou ]] ; then
			SUSEVERSION=`zgrep '^SUSEVERSION=' "$IMPORTFILE"`
			SUSEVERSION=${SUSEVERSION##SUSEVERSION=}
			SUSEPRODUCT=`zgrep '^SUSEPRODUCT=' "$IMPORTFILE"`
			SUSEPRODUCT=${SUSEPRODUCT##SUSEPRODUCT=}
			RPMCACHEVERSION=`zgrep '^RPMCACHEVERSION=' "$IMPORTFILE"`
			RPMCACHEVERSION=${RPMCACHEVERSION##RPMCACHEVERSION=}
			HOSTNAME=`zgrep '^HOSTNAME=' "$IMPORTFILE"`
			HOSTNAME=${HOSTNAME##HOSTNAME=}
			if [[ $HOSTNAME == `hostname` ]] ; then
				myError 0 "Cannot import for localhost! Use --host to specify the host"
				exit 15
			fi
			if [[ $RPMCACHEVERSION != $RPMCACHEVER ]] ; then
				myError 0 "The exporting machine uses a too old version of fou4s, import failed!"
				exit 15
			fi
			cleanCache
			RPMCACHE=$CACHEDIR/.cache.$HOSTNAME/rpmcache.$SUSEVERSION # cache file with all versions
			if [[ ! -d `dirname $RPMCACHE.gz` ]] ; then
				mkdir -p `dirname $RPMCACHE.gz`
			fi
			cp "$IMPORTFILE" $RPMCACHE.gz && rm -f $RPMCACHE
			gunzip $RPMCACHE.gz
			myNote 0 "Successfully imported `basename $IMPORTFILE` (${SUSEPRODUCT:=SuSE} $SUSEVERSION) from host $HOSTNAME. Now run"
			myNote 0 "fou4s -u --host $HOSTNAME --export myExport.tar"
			myWarn 0 "Then copy myExport.tar to target machine and run"
			myNote 0 "fou4s --import myExportfile.tar -i"
			exit 0
		elif [[ $IMPORTFILE == *.tar ]] ; then
		 	myNote 0 "Importing packages ..."
			if [[ $IMPORTFILE != /* ]] ; then
				IMPORTFILE=$PWD/$IMPORTFILE # relative path to fixed path conversion
			fi
			for SERVERURL in $SERVERLIST; do
				server=${SERVERURL#@(ftp|http|https|rsync)://} server=${server%%/*}
				server=${server##*@}
				break # only use first server
			done
			cd "$BASEDIR" ;
			mkdir -p "$DLPATH/$server"
			cd "$DLPATH/$server"
			tar xf $IMPORTFILE
			myNote 0 "Successfully imported `basename $IMPORTFILE`"
			exit 0
		else
			myError 0 "Error: Importfile must be named '*.fou' or '*.tar'!"
			exit 16
		fi
	elif [[ $IMPORT -eq 1 ]] ; then
		myError 0 "Error: Importfile $IMPORTFILE does not exist!"
		exit 16
	fi
	# diable progressbar on verbose level ge 2
	[[ $VERBOSE -lt 2 ]] || USEPROGRESS=0

	if [[ $LIMIT -ne 0 ]] ; then
		RSYNCOPTS="$RSYNCOPTS --bwlimit $LIMIT"
		GLOBALWGETOPTS="$GLOBALWGETOPTS --limit-rate ${LIMIT}k"
		GLOBALCURLOPTS="$GLOBALCURLOPTS --limit-rate ${LIMIT}k"
	fi

	# NODOTSUSEVERSION=${SUSEVERSION//./} # SuSE version without dots for comparisons # removed because of SLES9 bug (C. Thiel)
	SERVERLIST=${SERVERLIST//,/ } # remove "," and replace with space
	#DLPATH=$CACHEDIR/packages # default path for downloaded RPMs # FIXME don't overwrite always, only if dlpath has default value
	DESCCACHE=$CACHEDIR/.cache.$HOSTNAME/descriptioncache.$SUSEVERSION # cache of patch descriptions
	listsToPatterns AUTOLIST EXCLUDELIST IGNORELIST REMARKLIST USERRPMS SRCRPMS

	if [[ $SERVERLIST == *rsync://* && -z `type -p rsync` ]] ; then
		myError 0 "You have an rsync server configured, but rsync is not installed. Aborting!"
		exit 9
	fi
	if [[ -z `type -p wget` ]] ; then
		myEcho 2 "wget not found, falling back to curl ..."
		USECURL=1
	fi
	# curl requires usedir (don't move into above if, because USECURL might
	# be set by an option or parameter as well!)
	[[ $USECURL -eq 1 ]] && USEDIR=1
	[[ $NODOTSUSEVERSION -ge 101 ]] && USEDIR=1
	[[ $NODOTSUSEVERSION -ge 110 ]] && PATCHRPMS=0

	# output colum sizes
	[[ -n $COLUMNS && $COLUMNS -gt 86 ]] && RWIDTH=$((RWIDTH+COLUMNS-86))

	if [[ $GETSOURCE -eq 1 && $DOINSTALL -eq 1 ]] ; then
		myError 0 "Source RPM's cannot be installed. Use fou4s -ed to download them and copy from $DLPATH/.../rpm/src to a directory of your choice." | fmt -$COLUMNS
		exit 10
	fi

	if [[ -n $SUSEPRODUCT ]] ; then
		SUSEPRODUCT=$SUSEPRODUCT/  # append slash
		if [[ $SERVERLIST == *ftp.gwdg.de* ]] ; then
			myWarn 0 "You are using ftp.gwdg.de and a business product, this will probably not work. Set SuSEUser= and SuSEPassword= in fou4s.conf and use sdb.suse.de as update server." | fmt -$COLUMNS
			echo
		fi
		if [[ -z $HTTPUSER && $AUTOMODE -eq 0 ]] ; then
			myWarn 0 "WARNING: You are using a business product and haven't given a username."
			myWarn 0 "Edit $CONFIGFILE and search for SuSEUser= and SuSEPassword="
		fi
	fi
	if [[ -n $HTTPUSER && -n $HTTPPASSWD ]] ; then
		AUTHOPTS="--user $HTTPUSER --pass $HTTPPASSWD"
	elif [[ -n $HTTPUSER || -n $HTTPPASSWD ]] ; then
		myWarn 0 "WARNING: You must give a password for http auth, too!"
	fi

	# must be set after reading config file and checking parameter
	if [[ -z $SUSEVERSION ]]; then
		myError 0 "Cannot determine SuSE-Version from /etc/SuSE-release or config file"
		myError 0 "Exiting..."
		exit 2
	fi
	[[ $NODOTSUSEVERSION -ge 101 ]] && SERVERPATH=update/$SUSEPRODUCT$SUSEVERSION || SERVERPATH=$ARCH/update/$SUSEPRODUCT$SUSEVERSION
	if [[ $NODOTSUSEVERSION -ge 101 && $SUSEVERSION == 10 ]] ; then
		 SERVERPATH=/ # for SLES 10
	fi

	if [[ -n $http_proxy && $http_proxy != http://*:*/ ]] ; then
		echo "Proxy must start with http:// and end with / (e.g. http://proxy:8080/)"
		echo "Please check your http_proxy environment variable"
		exit 11
	fi
	#if [[ -n $ftp_proxy ]] ; then
	#	if [[ $ftp_proxy != http://*:*/ ]] ; then
	#		echo "Proxy must start with http:// and end with / (e.g. http://proxy:8080/)"
	#		echo "Please check your ftp_proxy environment variable"
	#		exit 1
	#	fi
	#fi

	if [[ -n $PROXYUSER ]] ; then
		GLOBALWGETOPTS="$GLOBALWGETOPTS --proxy-user=$PROXYUSER" 
		if [[ $PROXYDIGEST -eq 1 ]] ; then
			GLOBALCURLOPTS="$GLOBALCURLOPTS --proxy-digest -U $PROXYUSER:$PROXYPASS"
			USECURL=1
		else
			GLOBALCURLOPTS="$GLOBALCURLOPTS --proxy-basic -U $PROXYUSER:$PROXYPASS"
		fi
	fi

	[[ -n $PROXYPASS ]] && GLOBALWGETOPTS="$GLOBALWGETOPTS --proxy-passwd=$PROXYPASS"

	if [[ $BENCHMARK -eq 1 ]] ; then
		BSERVER="`fou4s-benchmark -q | head -1 | awk '{print $2}'`"
		if [[ -n $BSERVER ]] ; then
			SERVERLIST=$BSERVER
			echo "Using benchmarked server $SERVERURL"
		else
			myError 0 "Error: No benchmark server found! Using default ..."
		fi
	fi

	if [[ $CHECKFOU -eq 1 && $NODOTSUSEVERSION -ge 101 ]] ; then
		SERVERLIST="http://fou4s.gaugusch.at $SERVERLIST"
	fi

	if [[ ! -d $CACHEDIR/.cache.$HOSTNAME ]] ; then
		mkdir "$CACHEDIR/.cache.$HOSTNAME"
	fi
	Fou4skey=`echo $FOU4SKEY | tr "[A-Z]" "[a-z]"`

	if [[ $NODOTSUSEVERSION -lt 92 ]] || ! type -p applydeltarpm >/dev/null ; then
		USEDELTARPMS=-1
	fi
	
	if [[ $NODOTSUSEVERSION -ge 101 ]] ; then
		PATCHDIR=repodata
	fi
	for g in "$DIRNAME/xmlp.awk" /usr/bin/xmlp.awk /usr/local/bin/xmlp.awk ; do
		[[ -x $g ]] && XMLP=$g && break
		g=""
	done
} # optionPostProcessing


################################# doStartupChecks
# performs some initial checks that are needed for running fou4s
function doStartupChecks()
{
	if [[ ! -d $DLPATH ]] ; then
		echo "You need to create a directory to store the downloaded packages: $DLPATH (change $CONFIGFILE for a different path)"| fmt -$COLUMNS
		echo "Exiting ..."
		exit 2
	fi
	if [[ $CHECKONLY -eq 0 && $UPDATESERVERLIST -eq 0 && $UPDATEPACKAGELIST -eq \
		0 && $DOINSTALL -eq 0 && $LISTAVAILRPMS -eq 0 && $EXPORT -eq 0 && \
		$IMPORT -eq 0 ]] ; then
		echo "Please use one of -u, -e, -i, -l, --auto or --server!"
		exit 2
	fi
	[[ -f $CACHEDIR/descr_packages.$SUSEVERSION ]] && PACKAGES81=$CACHEDIR/descr_packages.$SUSEVERSION
	for f in $PACKAGES81 ; do
		if [[ ! -f $f ]] ; then
			myWarn 0 "Warning: $f can't be read. Run fou4s --fixperm to enable access for members of group fou4s or check if the 0x00000001 directory exists (create a link or rename the other one if not)." | fmt -80
			myWarn 0 "Alternatively, fou4s can download the file to $CACHEDIR/descr_packages.$SUSEVERSION (fou4s --getpackagedescriptions), if you are not root." | fmt -80
			# exit 1
		fi
	done
	if [[ $SKIPGPG -eq 0 && ! -x `type -p gpg` ]] ; then
		echo "GPG was not found. If you want to skip GPG signature checking, use"
		echo "the -g option. It is highly recommended to install gpg!"
		exit 2
	fi

	if [[ $SKIPGPG -eq 0 ]] ; then
		# check for gnupg directory for RPM (currently only on 8.0)
		# it is used for key comparison when root installs packages.
		[[ -d /usr/lib/rpm/gnupg && $EUID -eq 0 ]] && export GNUPGHOME=/usr/lib/rpm/gnupg
		[[ $EUID -eq 0 && -z $HOME ]] && export HOME=/root

		gpgproxyopt="--keyserver-options honor-http-proxy"
		if [[ $CHECKFOU -eq 1 ]] ; then
			if [[ `gpg --with-colons --list-keys $FOU4SKEY 2>/dev/null | grep ^pub: | cut -f1,3-6,10 -d:` != pub:1024:17:0EA1932BAFB66D7C:2002-04-28:fou4s\ build\ key\ \<fou4s@gaugusch.at\> && $RPMVER -lt 40 ]] ; then
				myNote 0 "Installing fou4s public key ..."
				if [[ -f ./fou4s_public.gpg ]] ; then
					gpg --import ./fou4s_public.gpg
				elif [[ -f /usr/share/doc/packages/fou4s/fou4s_public.gpg ]] ; then
					gpg --import /usr/share/doc/packages/fou4s/fou4s_public.gpg
				else
					gpg $gpgproxyopt --keyserver wwwkeys.pgp.net --recv-keys $FOU4SKEY
				fi
			fi
			if [[ $UPDATESERVERLIST -eq 0 && $RPMVER -ge 40 && `rpm -qi gpg-pubkey-$Fou4skey 2>/dev/null` == package\ gpg-pubkey-$Fou4skey\ is\ not\ installed ]] ; then
				myNote 0 "Installing fou4s public key (rpm V4)..."
				if [[ -f ./fou4s_public.gpg ]] ; then
					rpm --import ./fou4s_public.gpg
				elif [[ -f /usr/share/doc/packages/fou4s/fou4s_public.gpg ]] ; then
					rpm --import /usr/share/doc/packages/fou4s/fou4s_public.gpg
				fi
			fi
		fi
		if [[ `gpg --with-colons --list-keys 9C800ACA 2>/dev/null | grep ^pub: | cut -f1,3-6,10 -d:` != pub:1024:17:A84EDAE89C800ACA:2000-10-19:SuSE\ Package\ Signing\ Key\ \<build@suse.de\> && $RPMVER -lt 40 ]] ; then
			myNote 0 "Installing SuSE build key ..."
			gpg $gpgproxyopt --keyserver wwwkeys.pgp.net --recv-keys 9C800ACA
		fi
	fi

	if [[ -d $DLPATH/server0 ]] ; then
		echo "Converting numbered to named server directories ..."
		i=0
		for SERVERURL in $SERVERLIST; do
			if [[ ${BASH_VERSINFO[0]} -ge 3 ]] ; then
				SERVERURL=${SERVERURL//\%ARCH/$ARCH}
				SERVERURL=${SERVERURL//\%VERSION/$SUSEVERSION}
			else
				SERVERURL=${SERVERURL//\\%ARCH/$ARCH}
				SERVERURL=${SERVERURL//\\%VERSION/$SUSEVERSION}
			fi
			server=${SERVERURL#@(ftp|http|https|rsync)://} server=${server%%/*}
			server=${server##*@} # strip username part of urls
			if [[ ! -d $DLPATH/$server ]] ; then
				mv -vf $DLPATH/server$i $DLPATH/$server
			else # directory exists already - mv won't work
				cp -r $DLPATH/server$i/* $DLPATH/$server/
				rm -rf $DLPATH/server$i
			fi
			i=$((i+1))
		done
	fi
	[[ $QUIET -eq 1 ]] && USEPROGRESS=0
	[[ -t 1 ]] || USEPROGRESS=0 # test file descriptor of stdout
} # doStartupChecks


################################# updateStatistics
# Updates the counters for different update types
function updateStatistics()
{
	COUNTER[_ALL]=$((COUNTER[_ALL]+1))
	SIZES[_ALL]=$((SIZES[_ALL]+size))
	DLSIZES[_ALL]=$((DLSIZES[_ALL]+size-FOUNDSIZE))
	[[ $newhdsize != 0 && $oldhdsize != 0 ]] && HDSIZES[_ALL]=$((HDSIZES[_ALL]+newhdsize-oldhdsize))
	case $kind in
		security)
			SIZES[_SECURITY]=$((SIZES[_SECURITY]+size))
			DLSIZES[_SECURITY]=$((DLSIZES[_SECURITY]+size-FOUNDSIZE))
			[[ $newhdsize != 0 ]] && HDSIZES[_SECURITY]=$((HDSIZES[_SECURITY]+newhdsize-oldhdsize))
			COUNTER[_SECURITY]=$((COUNTER[_SECURITY]+1)) ;;
		recommended)
			SIZES[_RECOMMENDED]=$((SIZES[_RECOMMENDED]+size))
			DLSIZES[_RECOMMENDED]=$((DLSIZES[_RECOMMENDED]+size-FOUNDSIZE))
			[[ $newhdsize != 0 ]] && HDSIZES[_RECOMMENDED]=$((HDSIZES[_RECOMMENDED]+newhdsize-oldhdsize))
			COUNTER[_RECOMMENDED]=$((COUNTER[_RECOMMENDED]+1)) ;;
		generated)
			SIZES[_GENERATED]=$((SIZES[_GENERATED]+size))
			DLSIZES[_GENERATED]=$((DLSIZES[_GENERATED]+size-FOUNDSIZE))
			[[ $newhdsize != 0 ]] && HDSIZES[_GENERATED]=$((HDSIZES[_GENERATED]+newhdsize-oldhdsize))
			COUNTER[_GENERATED]=$((COUNTER[_GENERATED]+1)) ;;
		optional)
			SIZES[_OPTIONAL]=$((SIZES[_OPTIONAL]+size))
			DLSIZES[_OPTIONAL]=$((DLSIZES[_optional]+size-FOUNDSIZE))
			[[ $newhdsize != 0 ]] && HDSIZES[_OPTIONAL]=$((HDSIZES[_OPTIONAL]+newhdsize-oldhdsize))
			COUNTER[_OPTIONAL]=$((COUNTER[_OPTIONAL]+1)) ;;
		YaST2)
			SIZES[_YAST]=$((SIZES[_YAST]+size))
			DLSIZES[_YAST]=$((DLSIZES[_YAST]+size-FOUNDSIZE))
			[[ $newhdsize != 0 ]] && HDSIZES[_YAST]=$((HDSIZES[_YAST]+newhdsize-oldhdsize))
			COUNTER[_YAST]=$((COUNTER[_YAST]+1)) ;;
		script)
			SIZES[_SCRIPT]=$((SIZES[_SCRIPT]+size))
			COUNTER[_SCRIPT]=$((COUNTER[_SCRIPT]+1))
			DLSIZES[_SCRIPT]=$((DLSIZES[_SCRIPT]+size-FOUNDSIZE))
	esac
	[[ $size == 0 ]] && COUNTER[_ZERO]=$((COUNTER[_ZERO]+1)) #wrong size cnt
} # updateStatistics



################################# addToRpmExportList
# If --export *.tar is given, we save all found packages to that file
function addToRpmExportList()
{
	local rpm=${1##*$DLPATH/$server/}
	if [[ $EXPORT -eq 1 ]] ; then
		pushd "$BASEDIR">/dev/null
		if [[ $rpm == /* ]] ; then # absolute path found (file from alternate server)
			pushd ${rpm%%/$ARCH/update/$SUSEVERSION/*}>/dev/null # go to alternate server
			rpm=$ARCH/update/${rpm##*$ARCH/update/}
		else
			pushd "$DLPATH/$server">/dev/null # go to current server
		fi
		myEcho 3 "Adding to export file $EXPORTFILE: $rpm"
		[[ -f $EXPORTFILE ]] && tar -rf "$EXPORTFILE" $rpm && myNote 0 "Added to exportfile: `basename "$rpm"`" ||\
		myError 0 "Could not add $rpm to export file!";
		popd >/dev/null # dlpath/server
		popd >/dev/null # basedir
	fi
	return 0
}


################################# processFoundUpdates
# Processes the updates that have been found
# the information is gathered through the package_XXX variables
function processFoundUpdates()
{
	local last_shown packageinfo pkg x y newversion currentversion shortdesc test
	local longdesc license
	for pkg in ${!package_*} ; do
		cd "$BASEDIR"
		cd "$DLPATH"
		# package_$pkgdescfile_MG_$rpm
		x=${pkg##*_MG_} #current patch name
		y=${pkg##package_} y=${y%%_MG_*} #real patch description file containing pkg
		pkg=${!pkg}
		packageinfo=packageinfo_$x packageinfo=(${!packageinfo})
		newversion=${packageinfo[0]}
		currentversion=${packageinfo[1]}
		kind=${packageinfo[2]}
		size=${packageinfo[3]}
		series=${packageinfo[4]}
		remarkonly=${packageinfo[5]}
		rpm=${packageinfo[6]}
		dlfile=${packageinfo[7]}
		server=${packageinfo[8]}
		oldhdsize=${packageinfo[9]}
		newhdsize=${packageinfo[10]}
		i=${packageinfo[11]}
		patchfile=${packageinfo[12]}
		newbuildtime=${packageinfo[13]}
		PREINFO=${packageinfo[14]}
		sourcerpm=${packageinfo[15]}
		origurl=${packageinfo[16]}
		SERVERPATH=${packageinfo[17]}
		usepatchrpmnow=${packageinfo[18]}
		patchsize=${packageinfo[19]}
		SERVERURL=${packageinfo[20]}
		usedeltarpmnow=${packageinfo[21]}
		deltasize=${packageinfo[22]}
		deltarpm=${packageinfo[23]}
		patchrpm=${packageinfo[24]}
		shortdesc=shortdesc_$x shortdesc=${!shortdesc}
		longdesc=longdesc_$x longdesc=${!longdesc}
		license=license_$x license=${!license}

		if [[ $pkg == @($SRCRPMS) ]] ; then
			rpm=$sourcerpm
			dlfile=$SERVERURL/$SERVERPATH/$sourcerpm
			usepatchrpmnow=0
		fi

		[[ $PATCHRPMS -eq 0 ]] && usepatchrpmnow=0
		[[ $USEDELTARPMS -eq 0 ]] && usedeltarpmnow=0

		if [[ $usedeltarpmnow -eq 1 ]] ; then
			size=$deltasize
			[[ $NODOTSUSEVERSION -lt 101 ]] && rpm=deltas/$deltarpm || rpm=$deltarpm
			dlfile=$SERVERURL/$SERVERPATH/$rpm
		elif [[ $usepatchrpmnow -eq 0 && $rpm == *.patch.rpm ]] ; then
			rpm=${rpm%%.patch.rpm}.rpm
			origurl=${origurl%%.patch.rpm}.rpm
			dlfile=${dlfile%%.patch.rpm}.rpm
		elif [[ $usepatchrpmnow -eq 1 && $patchsize -gt 0 && $kind != script \
			&& $rpm != *.patch.rpm ]] ; then
			size=$patchsize
			rpm=${rpm%%.rpm}.patch.rpm
			origurl=${origurl%%.rpm}.patch.rpm
			dlfile=${dlfile%%.rpm}.patch.rpm
		elif [[ $usepatchrpmnow -eq 1 ]] ; then
			[[ $NODOTSUSEVERSION -ge 101 ]] && rpm=$patchrpm
			size=$patchsize
		fi
		if packageFiltered $pkg $kind $series; then
			myEcho 4 "Skipping $pkg because of filter"
			continue
		fi
		if [[ $NODOTSUSEVERSION -lt 110 && ! -f $i ]] ; then
			myWarn 2 "Package description file $i is missing, skipping $pkg" # | fmt -$COLUMNS
			continue
		fi
		if [[ $y != ${patchfile//[^A-Za-z0-9_]/_} && -n $patchfile ]] ; then
			myWarn 2 "Skipping $pkg from $y because used $patchfile"
			continue
		fi
		if [[ $last_shown != $patchfile ]] ; then
			updateinfoshown=0
			cd "$BASEDIR"
			cd "$DLPATH"
			if [[ -n $RPMINSTLIST && $INSTMODE -eq 1 ]] ; then
				rpmInstall $RPMINSTLIST
				RPMINSTLIST=
			fi
		fi
		last_shown=$patchfile
		showPackage || continue
		updateStatistics
		[[ $DOINSTALL -eq 0 && $DOWNLOAD -eq 0 ]] && continue
		if [[ $DOINSTALL -eq 0 && $DOWNLOAD -eq 1 && -n $FOUNDFILE ]] ; then
			if [[ $size == 0 ]] ; then
				myEcho 2 "$pkg: correct size is unknown, trying to resume download."
			elif [[ $FOUNDSIZE == $size ]] ; then
				myEcho 2 "$pkg: correct size in local mirror, no need to download $rpm"
				continue
			else
				myEcho 2 "$pkg: incorrect size in local mirror, trying to resume download ($FOUNDSIZE/$size)."
			fi
		fi
		getUserChoice || continue
		# download package if desired and return if it fails
		rpmpath=${rpm%/*}
		if [[ $DOWNLOAD -eq 1 ]] && ! getRpm "$rpm" "$pkg" "$size" "$dlfile" ; then
			if ! fileExists $server $rpm ; then
				# try alternate server
				dlfile1=dlfile1_$x
				dlfile1=${!dlfile1}
				if [[ -n $dlfile1 ]] ; then
					server=server1_$x
					server=${!server}
					getRpm $rpm $pkg $size $dlfile1
				fi
			fi
		fi
		fileExists $server $rpm || continue
		[[ $INTERACTIVE -eq 0 && $PREINFO -eq 1 && $ACCEPTPREINSTALLINFO -eq 0 &&\
			$EXPORT -eq 0 ]] && continue

		# update rpminstlist with pending packages (only if in you-compat mode)
		[[ $CHECKONLY -eq 1 ]] && continue
		if ! fileExists $server $rpm ; then
			myWarn 0 "File not found: $rpm - skipping."
			continue
		fi
		[[ $TESTMODE -eq 1 ]] && test=" (test mode)"
		if [[ $series != script ]] ; then
			processDownloadedRpm $pkg $rpm $newversion ${rpm##*/}
		elif [[ $INTERACTIVE -eq 1 ]] ; then
			processScript $pkg
		fi
	done # found updates
} # processFoundUpdates


################################ processServerList
# Cycles through all available servers and calls processPatchDescriptions
# for each
function processServerList()
{
	local nonfiles files
	lastserver=
	for SERVERURL in $SERVERLIST ; do
		if [[ ${BASH_VERSINFO[0]} -ge 3 ]] ; then
			SERVERURL=${SERVERURL//\%ARCH/$ARCH}
			SERVERURL=${SERVERURL//\%VERSION/$SUSEVERSION}
		else
			SERVERURL=${SERVERURL//\\%ARCH/$ARCH}
			SERVERURL=${SERVERURL//\\%VERSION/$SUSEVERSION}
		fi
		server=${SERVERURL#@(ftp|http|https|rsync)://} server=${server%%/*}
		server=${server##*@} server=${server%:*}
		if [[ $SERVERURL != *$ARCH/update/$SUSEPRODUCT$SUSEVERSION* ]] && \
			[[ $SERVERURL == rsync://* ]] ; then
			SERVERPATH=${SERVERURL#rsync://}
			SERVERPATH=${SERVERPATH#*/} # customized rsync server path
		else
			SERVERURL=${SERVERURL%%/$ARCH*}
			[[ $NODOTSUSEVERSION -ge 101 ]] && SERVERPATH=update/$SUSEVERSION || \
				SERVERPATH=$ARCH/update/$SUSEPRODUCT$SUSEVERSION
			[[ $NODOTSUSEVERSION -eq 101 && $SUSEVERSION = 10 ]] && SERVERPATH=/ # for SLES10
		fi
		if [[ $SERVERURL == ftp://* && -n $ftp_proxy && $USEDIR -eq 0 && $USECURL -eq 0 ]] ; then
			echo
			myWarn 0 "WARNING: You are using an FTP server and ftp_proxy is set. Fou4s can't use the proxy, because wget makes some trouble with FTP over a proxy." | fmt -$COLUMNS
			echo
			unset ftp_proxy
		fi
		[[ $UPDATEPACKAGELIST -eq 1 ]] && updatePackagelist $server $SERVERPATH
		lastserver=$server
		[[ $CHECKONLY -eq 0 && $DOINSTALL -eq 0 && $LISTAVAILRPMS -eq 0 ]] && continue
		# quiet and verbose are set in automode!
		myEcho 2 "Processing server $SERVERURL"
		if [[ ! -f $SKIPPEDPATCHESFILE ]] ; then
			touch $SKIPPEDPATCHESFILE 2>/dev/null
			if [[ $? -ne 0 ]] ; then
				myWarn 0 "Could not create $SKIPPEDPATCHESFILE, consider changing CacheDir in $CONFIGFILE" | fmt -$COLUMNS
			fi
		fi
		PATCHGLOB=$PATCHDIR
		if [[ $GENERATED -eq 1 ]]; then
			PATCHGLOB="$PATCHGLOB/.*-[0-9G]*"
		else
			PATCHGLOB="$PATCHGLOB/.*-[0-9]*"
		fi
		if [[ $NODOTSUSEVERSION -ge 101 ]] ; then
			PATCHGLOB="$PATCHDIR/patch-*.xml"
		fi

		cd "$BASEDIR"
		cd "$DLPATH"
		PKGDESCFILES=
		if [ $NODOTSUSEVERSION -lt 110 ] ; then
			if [[ $USEDIR -eq 1 ]] ; then
				if [[ $NODOTSUSEVERSION -lt 101 ]] ; then
					DIRFILES=`find ./$server/$SERVERPATH -follow -regex ".*\/directory.*" 2>/dev/null`
					PKGDESCFILES=`cat $DIRFILES| sed "s#^#./$server/$SERVERPATH/$PATCHDIR/#g"| grep -v -F -f $SKIPPEDPATCHESFILE`
					[[ -z $PKGDESCFILES ]] && myEcho 3 "No directory file found on $server"
				else
					pushd "$server/$SERVERPATH/$PATCHDIR" >/dev/null
					PKGDESCFILES=`parsePatchesXML -f ./$server/$SERVERPATH/$PATCHDIR/ |\
						grep -v -F -f $SKIPPEDPATCHESFILE`
					popd >/dev/null
				fi
			elif [[ -z $PKGDESCFILES ]] ; then
				PKGDESCFILES=`find ./$server/$SERVERPATH/$PATCHDIR* -follow -regex ".*$PATCHGLOB" 2>/dev/null\
				| grep -v -F -f $SKIPPEDPATCHESFILE`
			fi
			PKGDESCFILES=`echo $PKGDESCFILES | tr " " "\n" | sort -u`
			# if --nogenerated is used, we don't want to see this message
			if [[ -z $PKGDESCFILES && $GENERATED -eq 1 ]] ; then
				echo -e $COL_RED
				echo "WARNING: No update description files found for $SERVERURL" | fmt -$COLUMNS
				echo "Run fou4s with option -u to download patch descriptions!"
				echo
				echo -e "${COL_YELLOW}You may also want to check $SKIPPEDPATCHESFILE, this file contains all patch description names that will be skipped by fou4s. Make sure that it doesn't contain any blank lines!!" | fmt -$COLUMNS
				echo -e $COL_NORM
				continue
			fi
			ALLPKGDESCFILES="$ALLPKGDESCFILES $PKGDESCFILES"
			if [[ $EXPORT -eq 1 ]] ; then
				(
				cd "$BASEDIR"
				cd "$DLPATH"
				cd $server
				for d in $ALLPKGDESCFILES ; do
					tar -rf "$EXPORTFILE" ${d##./$server/}
				done
				)
			fi
			if [[ $USECACHE -ge 2 && -f $AUTOSKIPPEDPATCHESFILE ]] ; then
				myNote 2 "Processing auto-skipped patches on $server"
				nonfiles="`cat $AUTOSKIPPEDPATCHESFILE`"
				listsToPatterns nonfiles
				files=$PKGDESCFILES
				PKGDESCFILES=
				INVALIDPKGDESCS=
				for f in $files ; do
					if [[ $f == @($nonfiles) && -f $f && $f -ot $DESCCACHE ]] ; then
						myEcho 3 "Skipping $f"
					else
						PKGDESCFILES="$PKGDESCFILES $f"
						myNote 3 "Using $f"
					fi
				done
				myEcho 2 "Done with auto-skipped patches on $server"
			fi
			if [[ $NODOTSUSEVERSION -ge 101 ]] ; then
				processXMLPatchDescriptions $PKGDESCFILES # compares versions
			else
				processPatchDescriptions $PKGDESCFILES # compares versions
			fi
		else
			pushd "$server/$SERVERPATH/$PATCHDIR" >/dev/null
			processPrimaryXML
		fi
		[[ $USEPROGRESS -eq 1 ]] && showProgress 1 1 1 # force 100% display
	done # serverlist
} # processServerList


#################################
################################# main program
#################################
[[ $# -lt 1 ]] && showUsageShort
[[ $1 = --config ]] && CONFIGFILES=$2 && shift 2
for c in $CONFIGFILES ; do
	[[ -f $c ]] && CONFIGFILE=$c  # the last one wins
done

getOptionsFromConfigFile $CONFIGFILE

PARAMETERS=`getopt -l all,allnew,arch:,auto,benchmark,buildtime,checkdeleted,\
checkfou4s,commonfuncs,config:,end,evaluate,exclude:,exportx:,fixperm,force,\
getpackagedescriptions,help,host:,import:,interactive,inversecolor,limit-rate:,\
list,nocache,nocheckdeleted,nocolor,nocompatible,nocont,nodeps,nodownload,\
nodeltarpms,nogenerated,nogpg,nopatchrpms,noprogress,noproxy,offline,only,\
product:,proxy:,proxypasswd:,proxyuser:,quiet,remove,safemode,security,server,\
source:,src:,susepasswd:,susepassword:,suseuser:,suseversion:,testmode,update,\
upgrade,verbose,version,language:,cleancache,cronserver,cronworkstation,usedir,\
install,usecurl,use-curl,proxy-digest,noresume,older-than:,acceptpreinstallinfo\
 -o abcdef:ghilnoqrsuvw -n 'fou4s' -- "$@"`
[[ $? -ne 0 ]] && echo "Run fou4s --help for more information" && exit 12
eval checkParameters "$PARAMETERS" # parameter checking

if [[ $EXPORT -eq 1 && $EXPORTFILE == *.tar ]] ; then
	if [[ -f $EXPORTFILE ]] ; then
		rm "$EXPORTFILE"
		#myError 0 "Error: Output file already exists: $EXPORTFILE - aborting"
		#exit 13
	fi
fi
optionPostProcessing

[[ $CLEANCACHE -eq 1 ]] && cleanCache && exit

if [[ $EXPORT -eq 1 && $EXPORTFILE == *.fou ]] ; then
	[[ -f $EXPORTFILE ]] && rm "$EXPORTFILE"
	if [[ ! -f $RPMCACHE ]] ; then
		updateRpmCache force || (myError 0 "Could not create RPM cache!";exit)
	fi
	RPMCACHEVERSION==`grep RPMCACHEVERSION $RPMCACHE`
	if [[ $RPMCACHEVERSION != $RPMCACHEVER ]] ; then
		updateRpmCache force || (myError 0 "Could not update RPM cache!";exit)
	fi
	cp "$RPMCACHE" "$EXPORTFILE"
	gzip "$EXPORTFILE"
	mv "$EXPORTFILE.gz" "$EXPORTFILE"
	myWarn 0 "Successfully exported machine info to $EXPORTFILE."
	myWarn 0 "Please go to internet connected machine and run the following commands there:"
	myNote 0 "fou4s --import `basename "$EXPORTFILE"`"
	myNote 0 "fou4s -u --host $HOSTNAME --export myExport.tar"
	myWarn 0 "Then copy myExport.tar back to this machine and run "
	myNote 0 "fou4s --import myExportfile.tar -i"
	exit 0
fi

[[ $TESTMODE -eq 1 ]] && myError 0 "*************** USING TEST MODE"
if [[ -n $FORCEDARCH ]] ; then
	cleanCache
	myEcho 3 "Forcing arch $ARCH to $FORCEDARCH"
	ARCH=$FORCEDARCH
fi
updateRpmCache # for faster version comparison

getKernelVersion

if [[ -n $SUSEPRODUCT && $SUSEPRODUCT != */ ]]; then
	SUSEPRODUCT=$SUSEPRODUCT/  # append slash
fi
if [[ $VERBOSE -ge 2 ]] ; then
	cat <<- _EOF
		Using fou4s version $FOUVERSION
		Using config file   $CONFIGFILE
		Using host          $HOSTNAME
		Using Server(s)     $SERVERLIST
		Using Arch          $ARCH/$MACHINEARCH
		Using Valid Archs   $VALIDARCHS
		Using Product       ${SUSEPRODUCT:=SuSE Linux}
		Using Version       $SUSEVERSION ($NODOTSUSEVERSION)
		Using Kernel        $KERNELPKG (running: $KERNELVER, installed: $SUSEKERNELVER)
		Using Cache/DL-dir  $CACHEDIR $DLPATH
	_EOF
fi

if [[ $NODOTSUSEVERSION -lt 81 ]] ; then
	echo
	myError 0 "Sorry! SuSE versions older than 8.1 are no longer supported. Please get fou4s 0.12.6. This is the last version with support for SuSE 7.x and 8.0."| fmt -80
	exit 17
fi
#if [[ $NODOTSUSEVERSION -lt 90 ]] ; then
#	echo
#	myError 0 "Sorry! SuSE versions older than 9.1 are no longer supported. Please get fou4s 0.14.0. This is the last version with support for SuSE 8.x and 9.0."| fmt -80
#	exit 17
#fi

# only check for deleted files?
[[ $CHECKONLYDELETED -eq 1 ]] && checkDeleted && exit

for i in /var/adm/YaST/InstSrcManager/IS_CACHE_0x000000*/DATA/descr/packages ; do
	[[ -f $i ]] && PACKAGES81="$PACKAGES81 $i" && myEcho 3 "Found SuSE package description file $i"
done
# fix permissions on suse 8.1? This function will exit after being run
[[ $FIXPERMS -eq 1 ]] && fixperms
[[ $GET81PACKAGEDESCRIPTIONS -eq 1 ]] && get81packagedescriptions
[[ $CRONINST -eq 1 ]] && installCronjob

if [[ ! -d $DLPATH ]] ; then
	echo "You need to create a directory to store the patches: "
	echo "$DLPATH"
	echo "(Edit the config file $CONFIGFILE for a different path)"
	echo "Exiting ..."
	exit 2
fi

# location of gunzipped ARCHIVES.gz
# where gpd.sh finds the ARCHIVES file, to get the package descriptions from
# exported, so get_info_from_ARCHIVES.pl sees it.
set -- $SERVERLIST
SERVERURL=$1
server=${1#@(ftp|http|https|rsync)://}
server=${server%%/*}
server=${server/:*} # strip port number
export ARCHIVES=$DLPATH/$server/$SERVERPATH/ARCHIVES

#used by announcement2pkgdesc and gpd.sh
[[ $COMMONFUNCS -eq 1 ]] && return
doStartupChecks # some initial checks
[[ $UPDATESERVERLIST -eq 1 ]] && updateServerlist && exit
source $RPMCACHE # source the rpm cache
# source the update description cache
SKIPPEDPATCHESFILE=$CACHEDIR/skipped-patches.$HOSTNAME.$SUSEVERSION
AUTOSKIPPEDPATCHESFILE=$CACHEDIR/.cache.$HOSTNAME/skipped-patches.$HOSTNAME.$SUSEVERSION.$LANGUAGE.auto
if [[ $USECACHE -ge 2 && -f $DESCCACHE ]] ; then
	myNote 3 "Sourcing description cache"
	source $DESCCACHE
	if [[ $DESCRIPTIONCACHE_VERSION != 1.4 || $DC_ARCH != $ARCH || \
		$DC_PRODUCT != $SUSEPRODUCT || $DC_VERSION != $SUSEVERSION || \
		$DC_SERVERLIST != $SERVERLIST || $DC_LANGUAGE != $LANGUAGE || \
		$SKIPPEDPATCHESFILE -nt $AUTOSKIPPEDPATCHESFILE ]] ; then
		myEcho 2 "Removing autoskipped-patches because description cache is invalid!"
		rm -f $AUTOSKIPPEDPATCHESFILE $DESCCACHE
		# unset all found packages
		for i in `echo ${!longdesc_*} ${!license_*} ${!shortdesc_*} ${!packageinfo_*} ${!package_*} ${!dlfile1_*} ${!server1_*}` ; do
			unset $i
		done
	fi
fi
if [[ $RPMCACHEVERSION != $RPMCACHEVER || $RPMCACHECOMPLETE != 1 || -z $pkg_bash ]] ; then
	updateRpmCache force
	myNote 3 "Sourcing RPM DB cache"
	source $RPMCACHE
	if [[ $RPMCACHEVERSION != $RPMCACHEVER || $RPMCACHECOMPLETE != 1 ]] ; then
		myError 0 "BUG! updateRpmCache failed twice without obvious reason."
		exit 13
	fi
fi
[[ $INTERACTIVE -eq 1 ]] && exec 6<&0 # copy stdin

if [[ $NODOTSUSEVERSION -ge 110 ]] && [ -f $SKIPPEDPATCHESFILE ] ; then
	while read line; do
		EXCLUDELIST="$EXCLUDELIST $line"
	done < $SKIPPEDPATCHESFILE 
	EXCLUDELIST=${EXCLUDELIST## }
	listsToPatterns EXCLUDELIST
fi

processServerList # find updates from all servers
if [ $USECACHE -ge 2 ] ; then # update cache with found updates
	updateDescriptionCache
	printf "%s\n" $ALLPKGDESCFILES > $AUTOSKIPPEDPATCHESFILE
	# strip blank lines:
	grep -v "^$" $AUTOSKIPPEDPATCHESFILE 2>/dev/null > \
		$AUTOSKIPPEDPATCHESFILE.new && \
		mv $AUTOSKIPPEDPATCHESFILE.new $AUTOSKIPPEDPATCHESFILE
fi
if [[ $CHECKONLY -eq 1 || $DOINSTALL -eq 1 ]] ; then
	processFoundUpdates # shows them on the screen and installs
fi
cd "$BASEDIR"
cd "$DLPATH"
[[ -n $RPMINSTLIST ]] && rpmInstall $RPMINSTLIST

if [[ $CHECKONLY -eq 1 && $QUIET -eq 0 && ${COUNTER[_ALL]} -gt 0 && $DOWNLOAD -eq 0 || $LISTAVAILRPMS -eq 1 ]] ; then
	showSummary
	echo This run of fou4s took $SECONDS secs. # $SECONDS is from BASH!
	[[ -n $COL_BLACK && $INVERSE -eq 0 ]] && echo -e "${COL_BLACK}*** IF YOU CAN READ THIS, try fou4s --inversecolor or set InverseColor=1 ***$COL_NORM"
fi
if [[ $AUTOMODE -eq 1 && $DOWNLOAD -eq 1 ]] ; then
	if [[ $((COUNTER[_ALL]-COUNTER[_INST])) -gt 0 ]] ; then
	[[ $USEBUILDTIME -eq 1 ]] && b=" --buildtime" || b=
		showSummary
		echo
		echo "Please run fou4s -i$b now, to install the downloaded packages."
		echo This run of fou4s $FOUVERSION took $SECONDS secs.
		logWrite 1 "Found ${COUNTER[_ALL]} update(s) (fou4s $FOUVERSION)"
	elif [[ ${COUNTER[_ALL]} -gt 0 ]] ; then
		echo This run of fou4s took $SECONDS secs.
	fi
fi

if [[ $EXPORT -eq 1 ]] ; then
	myNote 0 "Successfully exported to `basename $EXPORTFILE`"
fi

if [[ $DOINSTALL -eq 1 && $SUSECONFIG -eq 2 ]] ; then
	[[ $TESTMODE -eq 1 ]] && myError 0 "Testmode - NOT running SuSEconfig" || \
	if [[ $EUID -eq 0 ]] ; then
		myWarn 0 "Running SuSEconfig ..."
		sg root -c /sbin/SuSEconfig
	else
		myError 0 "Error: You must be root to run SuSEconfig!"
	fi
fi

# check for deleted files (by RPM) that are still in use
[[ $CHECKDELETED -eq 1 ]] && checkDeleted

if [[ $FAILCOUNT -gt 0 ]] ; then
	myWarn 0 "Warning: $FAILCOUNT updates failed. Please fix the problems and try again"
fi
exit 0
# vim: set ai ts=3 sw=3 is :
