#!/bin/sh
#
# Copyright (c) 2003,2004 ALT Linux Ltd.
# Copyright (c) 2003,2004 Stanislav Ievlev <inger@altlinux.org>
#
# alternatives-update - utility from alternatives subsystem
# updates current status of alternatives symlinks
#
# 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.
#

if [ "$0" = ./alternatives-update ]; then
	. ./functions
	lib_dir="$(/bin/pwd)"
else
	. /usr/share/alternatives/functions
fi

#fake_dir=/tmp

exit_handler()
{
	local rc=$?
	trap - 0
	rm -rf -- $AVAIL $NEW $CHANGES
	exit $rc
}

trap exit_handler 0 1 2 3 9 13

#setup new alternative symlinks
new_alternative()
{
#    echo "setup_new:$1"
    from=$1
    to=$(cat $AVAIL|grep "^$from	"|cut -f2)
    [ -f "$to" -o -d "$to" ] || return #skip non-existence files or directories
    int_link=$(echo $from|alternatives_encode)

    #move if external name is not symlink
    [ -e $fake_dir/$from -a ! -h $fake_dir/$from ] && mv $fake_dir/$from $fake_dir/$from.alternatives_save
    ln -snf $links_dir/$int_link $fake_dir/$from #create symlink from external name to internal
    ln -snf "$to" "$links_dir/$int_link"
}

#remove obsolete symlinks
remove_alternative()
{
#    echo "remove_alternative:$1"
    from=$1
    int_link=$(echo $from|alternatives_encode)
    [ -h $fake_dir/$from ] && rm -f $fake_dir/$from #remove external only if it is symlink
    rm -f "$links_dir/$int_link" #always remove internal links
}

#try to relink symlinks if needed
#FIXME: update also an external link if we need it
update_alternative()
{
    from=$1
    to=$2
    int_link=$(echo $from|alternatives_encode)
    ret=0
    points_to=$(readlink $links_dir/$int_link)
    if [ "$points_to" != "$to" ]; then
	ln -snf "$to" "$links_dir/$int_link"
	ret=1
    fi
    ext_link=$(readlink $fake_dir/$from)
    if [ ! -h $fake_dir/$from -o "$ext_link" != "$links_dir/$int_link" ]; then
	ln -nsf "$links_dir/$int_link" "$fake_dir/$from"
	ret=1
    fi
    return $ret
}

print_usage()
{
	[ "$1" = 0 ] || exec >&2
	cat <<EOF
Usage: $PROG [options]
or: $PROG --ignore [candidates...]

alternatives-update syncs alternatives subsystem state  with  database:
creates nessesary symlinks, deletes old one, fix broken.

Default  mode is only sync.

When you use --ignore, alternatives subsystem will ignore 
selected candidates when it's made a choice.

For example, you have some packages sub1, sub2 and sub3.  All  of  them
have  candidates for one alternative and also require some common package.
You can place database with all candidates to common package.
This allows you to made only update requests from packages.
All register/unregister procedures will be done by common package.

Valid options are:
  -h, --help	display help screen
  -v, --version	display version information
  -g, --ignore  ignore candidates
		     				     
Report bugs to <inger@altlinux.org>
EOF
	[ -n "$1" ] && exit "$1" || exit
}

TEMP=`getopt -n $PROG -o h,v,g -l help,version,ignore -- "$@"` || print_usage
eval set -- "$TEMP"

do_ignore=0
while :; do
	case "$1" in
		-h|--help) print_usage 0
			;;
		-v|--version) print_version
			;;
		-g|--ignore) do_ignore=1
			;;
		--) shift; break
			;;
		*) Fatal "unrecognized option: $1"
			;;
	esac
	shift
done


#autofix of invalid manual alternatives
#echo "invalid manual alternatives"
cat $manual_file|
while read i
do
    name="${i#*	}"
    name=${name%%	*}
    [ -f "$name" -o -d "$name" ] || alternatives-auto ${i%%	*} #file not exist in filesystem
    fgrep -qs "	$name	" /etc/alternatives/auto/* || alternatives-auto ${i%%	*} #file not exist in configs
done

#FIXME: bound cycle with number of lines in alternatives
REPEAT=1
while [ $REPEAT -eq 1 ]
do
#    echo "update cycle"
    REPEAT=0

    AVAIL=`mktemp -t $PROG.avail.XXXXXX`
    CHANGES=`mktemp -t $PROG.changes.XXXXXX`
    NEW=`mktemp -t $PROG.new.XXXXXX`

    #calculate two lists: available configs and available symlinks
    get_list $*|sort -u >$AVAIL
    ls -1 $links_dir|alternatives_decode|sort -u>$NEW

    #calculate files to update
    cat $AVAIL|cut -f1|sort -u|comm - $NEW| sed $sed_options 's,.*,&		,'>$CHANGES
    
#    echo "need to create:"
    for i in $(cat $CHANGES|cut -f1|sed $sed_options '/[[:print:]]/! d')
    do
    	new_alternative $i
    	REPEAT=1
    done
    
    #calculate files to remove
#    echo "need to remove:"
    for i in $(cat $CHANGES|cut -f2|sed $sed_options '/[[:print:]]/! d')
    do
    	remove_alternative $i
    	REPEAT=1
    done

    #all other to update
#    echo "try to update:"
    cat $CHANGES|cut -f3|sed $sed_options '/[[:print:]]/! d'|join - $AVAIL|
    (REPEAT=0
	while read i
	do
	    update_alternative $i #note: we really pass _two_ args here
	    res=$?
    	    [ $REPEAT -eq 0 ] && REPEAT=$res
	done;
    exit $REPEAT)
    res=$?
    [ $REPEAT -eq 0 ] && REPEAT=$res
    
#    echo "end of cycle rep = $REPEAT"
    rm -f $AVAIL $NEW $CHANGES
done
