# -*-shell-script-*-

# This contains utility functions that can be called by package scripts

####
#
# This code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright 2002,2003 Mike Hearn (mike@theoretic.com)
# Copyright 2002,2003 Hongli Lai (h.lai@chello.nl)
# Copyright 2002,2003 Curtis L Knight (knighcl@fastmail.fm)
#
####


#
# If you're looking for import() here, it's defined in makeinstaller instead
#

##
# $APKG_BUILD_SKIP_CONFIGURE
#
# When set to 1, prepareBuild() will not run configure. This, in combination with $APKG_BUILD_SKIP_CONFIGURE,
# is useful if you only want to create a package but not recompile all the source code.
# If you're not using prepareBuild, your [BuildPrepare] code should respect this variable.
#
# See also: $APKG_BUILD_SKIP_MAKE

##
# $APKG_BUILD_SKIP_MAKE
#
# When set to 1, prepareBuild() will not run make. This, in combination with $APKG_BUILD_SKIP_CONFIGURE,
# is useful if you only want to create a package but not recompile all the source code.
# If you're not using prepareBuild, your [BuildPrepare] code should respect this variable.
#
# See also: $APKG_BUILD_SKIP_CONFIGURE

##
# $ROOTNAME
#
# This variable contains the root name of the package, as given in the specfile.

##
# $SHORTNAME
#
# This variable contains the short name of the package, as given in the specfile.

##
# $DISPLAYNAME
#
# This variable contains the display name of the package, as given in the specfile.

##
# $SOFTWAREVERSION
#
# The software version of the package, as given in the specfile.

##
# $INTERFACEVERSION
#
# The interface version of the package, as given in the specfile.

##
# $PREFIX
#
# The path of the prefix to which the software is installed.

##
# prepareBuild
#
# Configure and compile the source code, and install files to a temporary build root directory.
# That build root directory will be compressed by makeinstaller, which will be the main payload for your package.
# This function can only be used if your project is using the GNU Autoconf/Automake build system.
#
# Optimization flags will be set, by default it will use "-O2 -march=i586 -mcpu=i686". The output will
# be supressed, however errors and warnings will be shown.
#
# See also: $APKG_BUILD_SKIP_CONFIGURE and $APKG_BUILD_SKIP_MAKE.
#
# Example:
# [BuildPrepare]
# prepareBuild --enable-foofeature
function prepareBuild() {
    if [[ "$APKG_BUILD_ROOT" != "" ]]; then
        build_root="$APKG_BUILD_ROOT"
    else
        build_root=`mkdir -p "${TMP}/apkg-build-root.$$"; echo "${TMP}/apkg-build-root.$$"`
    fi

    __apkg_errors_file=`echo "${TMP}/apkg-build-errors.$$"`

    trace build_root=$build_root

    local _prefix=/usr/local

    if [[ "$APKG_BUILD_SKIP_CONFIGURE" != "1" ]]; then
        outn "$intl_CONFIGURING"

        # Set x86 optimizations
        local oCFLAGS="$CFLAGS"
        local oCXXFLAGS="$CXXFLAGS"
        local TARGET
        if uname -m | grep -Ei '(i.?86|athlon)' &>/dev/null; then
            export CFLAGS='-O2 -march=i586 -mcpu=i686 -pipe'
            export CXXFLAGS='-O2 -march=i586 -mcpu=i686 -pipe'
        fi

	if locateCommand apgcc; then
	    export CC=apgcc;
	    export CXX=apg++;
	fi
	trace running configure
        if [[ "$APKG_BUILD_VERBOSE" == "0" ]]; then
            ./configure --prefix=$_prefix --disable-static --disable-dependency-tracking "$@" >/dev/null 2>"$__apkg_errors_file"
        else
            touch "$__apkg_errors_file"
	    echo
            ./configure --prefix=$_prefix --disable-static --disable-dependency-tracking "$@"
        fi
        local exitCode=$?
        CFLAGS="$oCFLAGS"
        CXXFLAGS="$oCXXFLAGS"
	trace exitCode was $exitCode
        if [[ "$exitCode" == "0" ]]; then
	    if [[ "`cat \"$__apkg_errors_file\"`" != "" ]]; then
	        [[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_CONFIGURING"; green; out "$intl_DONE_WITH_WARNINGS"; normal;
	        cat "$__apkg_errors_file";
            else
		[[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_CONFIGURING"; green; out "$intl_DONE"; normal;
	    fi
        else
            [[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_CONFIGURING"; red; echo "$intl_FAILED";
            normal; cat "$__apkg_errors_file";
            exit 1;
        fi
        
    fi

    if [[ "$APKG_BUILD_SKIP_MAKE" != "1" ]]; then
        outn "$intl_MAKING";

        # reset the errors/warnings file
        echo "" >"$__apkg_errors_file"

	trace running make
        if [[ "$APKG_BUILD_VERBOSE" != "0" ]]; then
	    echo
            touch "$__apkg_errors_file"
            make
        else
            make >/dev/null 2>"$__apkg_errors_file"
        fi

        if [[ "$?" == "0" ]]; then
            if [[ "`cat \"$__apkg_errors_file\"`" != "" ]]; then
                [[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_MAKING";
		green; out "$intl_DONE_WITH_WARNINGS";
                normal; cat "$__apkg_errors_file"; echo;
            else
                [[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_MAKING";
		green; out "$intl_DONE"; normal;
            fi
        else
            [[ "$APKG_BUILD_VERBOSE" != "0" ]] && outn "$intl_MAKING"; red; echo "$intl_FAILED";
            normal; cat "$__apkg_errors_file"; echo;
            exit 1;
        fi;
    fi

    if [[ "$APKG_BUILD_ROOT" != "" ]]; then
        trace "APKG_BUILD_ROOT set, not running make install"
    else
        echo "" >"$__apkg_errors_file";
        outn "$intl_INSTALLING" "$build_root";

	local libtool_fixed
	if [[ -f libtool ]]; then
		trace Fixing libtool...
		if [ -f "$autopackage_prefix/libexec/autopackage/fixlibtool" ]; then
			# autopackage is installed
			trace using installed directories and executing fixlibtool
			bash "$autopackage_prefix/libexec/autopackage/fixlibtool" > libtool-fixed
		elif [ -f "$autopackage_prefix/libexec/fixlibtool" ]; then
			# autopackage is not installed
			trace using cvs directories and executing fixlibtool
			bash "$autopackage_prefix/libexec/fixlibtool" > libtool-fixed
		else
			warn fixlibtool could not be found.
		fi
		chmod +x libtool-fixed
		libtool_fixed="`pwd`/libtool-fixed"
	fi

        trace running make install
        local retval
        if [[ "$APKG_BUILD_VERBOSE" != "0" ]]; then
            touch "$__apkg_errors_file"
            make install DESTDIR="$build_root" LIBTOOL="$libtool_fixed" prefix=$_prefix
        else
            make install DESTDIR="$build_root" LIBTOOL="$libtool_fixed" prefix=$_prefix >/dev/null 2>"$__apkg_errors_file"
        fi
        retval=$?

        [[ -f libtool ]] && rm -f libtool-fixed

        if [[ "$retval" == "0" ]]; then
            # eliminate warnings that libtool produces every time
            trace eliminating warnings
	    while w=`grep -n "libtool: install: warning: remember to run" "$__apkg_errors_file" | sed 's/:.*//'`; [[ "$w" != "" ]]; do
	        for i in $w; do
	            cmd="$cmd ${i}d;"
	        done
	        trace sedding $cmd
                safeSed "$__apkg_errors_file" "$cmd"
                safeSed "$__apkg_errors_file" "$cmd"
                # we do it twice to eliminate the line afterwards as well (the spurious warnings come in pairs)
            done
            if [[ "`cat \"$__apkg_errors_file\"`" != "" ]]; then
                [[ "$APKG_BUILD_VERBOSE" != "0" ]] && { echo; outn "$intl_INSTALLING" "$build_root"; }
                green; out "$intl_DONE_WITH_WARNINGS"; normal;
                cat "$__apkg_errors_file";
            else
                [[ "$APKG_BUILD_VERBOSE" != "0" ]] && { echo; outn "$intl_INSTALLING" "$build_root"; }
                green; echo "$intl_DONE"; normal;
            fi
        else
            red; echo "$intl_FAILED";
            normal; cat "$__apkg_errors_file";
            exit 1;
        fi
        rm -f "$__apkg_errors_file" 2>/dev/null;
    fi

    export _virtual_build_root="$build_root"
    export build_root=`echo "$build_root/$_prefix" | sed 's|//|/|g'` # bump it to reflect the DESTDIR voodoo above
    trace new build_root is $build_root
    return 0
}


##
# unprepareBuild
#
# Cleanup the temporary build root direcory made by prepareBuild(). You should not call this if you didn't already call prepareBuild().
#
# Example:
# [BuildUnprepare]
# unprepareBuild
function unprepareBuild() {
    # clean up after packaging
    if [[ "$APKG_BUILD_ROOT" == "" ]]; then
        outn "$intl_CLEANING"
        rm -rf "$_virtual_build_root"
        green; out "   $intl_DONE"; normal;
    fi
    unset build_root
    unset _virtual_build_root
}


##
# _scanLDLibPath <LIBRARY>
# LIBRARY: The unversioned soname of the library to search for (eg, libfoo.so)
# Outputs: A list of found libraries, seperated by newline.
#
# Scan $LD_LIBRARY_PATH for shared library.
#
# Example:
# export LD_LIBRARY_PATH=/home/mike/garnome/lib
# _scanLDLibPath libglib.so
# # Outputs matches for example:
# #  /home/mike/garnome/lib/libglib-2.0.so
# #  /home/mike/garnome/lib/libglib-2.0.so.0
# #  /home/mike/garnome/lib/libglib-2.0.so.0.200.0
### START SCANLDLIBPATH
### Function is substituted from apkg-script-utils.
function _scanLDLibPath() {
    oIFS="$IFS"
    IFS=":"
    local paths
    paths=( $LD_LIBRARY_PATH )
    IFS="$oIFS"    
    local finalresult=""
    local result=""
    for (( i=0; i<${#paths[*]}; i++ )); do
        if [[ "${paths[$i]}" != "" ]]; then
            result=`ls -1 "${paths[$i]}/${1}"* 2> /dev/null`
            finalresult=`echo -e "$finalresult\n$result"`
        fi
    done
    echo "$finalresult"  | grep "\(.\)" | sort | uniq
}
### END SCANLDLIBPATH


# ------------------------------------------------------------

function _testForLib_munge() {
    while read; do
	echo $REPLY | awk -F. '{print $1 "." $2}'
    done
}

##
# testForLib [-v] [-i] LibraryName
# -v: Verbose mode. Print each version number out on stdout.
# -i: Print each unique interface version found on stdout.
# LibraryName: The library to check for. This should be an unversioned soname, for instance libfoo.so
#
# Check for the existance of a library. If -v is specified the version
# number of each version of the library will be printed in a space
# separated list on stdout. If -i is specified each version number will
# be truncated to fit the A.B form required of interface versions, and duplicates
# will be stripped. It is an error to specify both.
#
# Outputs: If -v is specified, a list of found library version numbers.
# Returns: 0 = passed, 1 = library not present.
#
# Example:
# $ testForLib libfoo.so && echo "libfoo.so was found on the system!"
# $ ls /usr/lib/libstdc++.so.*
# /usr/lib/libstdc++.so.2.7.2.8
# /usr/lib/libstdc++.so.2.8.0
# /usr/lib/libstdc++.so.2.9.dummy
# /usr/lib/libstdc++.so.5.0.3
# /usr/lib/libstdc++.so.5 -> libstdc++.so.5.0.3
# $ testForLib -v libstdc++.so
# 2.7.2.8 2.8.0 2.9.dummy 5.0.3
### START TESTFORLIB
### Function is substituted from apkg-script-utils.
function testForLib() {
    pushOptE; set +e;
    local verbose=false
    local verbose_i=false
    local lib=""
    
    while [[ "$1" != "" ]]; do
	case $1 in
	    -v ) verbose=true; shift;;
	    -i ) verbose_i=true; shift;;
	    *  ) lib=$1; shift;;
	esac
    done

    if $verbose && $verbose_i; then
	err "the -i and -v options are mutually exclusive"
	popOptE
	return 1
    fi

    if [[ "$lib" == "" ]]; then
	err must specify the library to check for
	popOptE
	return 1
    fi

    local cacheresult
    cacheresult=$( /sbin/ldconfig -p | grep "${lib}\(\.[[:digit:]\.]*\)\?$" | awk 'BEGIN {FS=" => "} { print $2 }' | while read; do test -f "$REPLY" && echo $REPLY; done )
    trace cacheresult=$cacheresult
    local libpathresult=`_scanLDLibPath "${lib}"`
    trace libpathresult=$libpathresult
    local liblist=`echo -e "${libpathresult}\n${cacheresult}" | grep "\(.\)" | sort | uniq`
    local result="";
    if [[ "$liblist" == "" ]]; then warn "$1 not found"; popOptE; return 1; fi; # not found in cache
    trace liblist=$liblist
    
    if ! $verbose && ! $verbose_i; then
	popOptE
	return 0
    fi

    # we want to loop over the results until we find a library that matches the required version
    (
    echo "$liblist" | while read; do
	trace REPLY=.${REPLY}.
        local lib_resolved=$( _resolveLink "$REPLY" )
	trace lib_resolved=$lib_resolved
        if $verbose || $verbose_i; then
            echo "$lib_resolved" | sed 's/^.*\.so.//';
        fi	
    done
    ) | sort | uniq | if $verbose_i; then _testForLib_munge; else cat; fi

    popOptE;
    return 0;
}
### END TESTFORLIB

# -------------------------------------------------------------

# called when the install script is done
function _installScriptFinished() {
    trace called
    _createDBEntry
    echo "$DISPLAYNAME" >>"$working_dir/display-names"
    echo "$installsize" >>"$working_dir/install-sizes"
    echo "$FILETOTAL" >>"$working_dir/file-totals"
    terminateFE;
    exit 0;
}

function _prepareScriptFinished() {
    trace called
    echo "$ROOTNAME" >> "$working_dir/prepared-rootnames"
}

##
# _decompressPayload
#
# Switches to the payload directory, and decompresses the included tarball while sending progress bar data to the ui
# Deletes the payload data aftewards.
function _decompressPayload() {

    # we have to change directories here to work around an obscure
    # misfeature of tar or bzip (one of them doesn't like the format
    # of our root names and tries to do DNS lookups and stuff)

    pushd "$payload_dir" >/dev/null

    # $FILETOTAL was loaded from package environment file
    tar -x --bzip2 -vf "payload.tar.bz2"  |  ( while read a; do
        (( filecount++ ));
        progressBar "$filecount" "$FILETOTAL" "$intl_PROGRESSBAR_EXTRACTING_FILES" 1;
    done; )

    rm payload.tar.bz2

    popd >/dev/null
}


# -------------------------------------------------------------


##
# mkdirs --nolog --session <DIRECTORY>
# --nolog: do not log the function call.
# --session: log into session file.
# DIRECTORY: directory to create.
# RETURNS: 0, if directory exists or is created.
# RETURNS: >0, if not created with return code from mkdir.
#
# Create directories within the filesystem and will return error codes if directories
# are not created.
#
# Removal of these directories will occur from within the removeFile function such
# that mkdirs function will not be logged.
#
# Example:
# mkdirs /home/mike/some/new/dir
#   --> creates some/new/dir
#
function mkdirs() {
    pushOptE; set +e;

    local nolog="false"
    local session="false"
    while [[ "$1" != "" ]]; do
        case "$1" in
            "--nolog")      nolog="true"; shift;;
            "--session")    session="true"; shift;;
            *) break;;
        esac
    done

    local dir="$1"
    if [ -d "$dir" ]; then
        warn "$dir" dir already exists
        popOptE
        return 0
    fi

    mkdir -p "$dir"
    local r=$?

    # session or nolog as requested
    if ! $session; then
        logCommand --session "removeDir `escapeFilename \"${dir}\"`"
    fi
    if ! $nolog; then
        logDir "$dir"
    fi
    
    # use of -p always gives a $?==0 so test
    # that the directory exists now
    if [ ! -d "$dir" ]; then
        err "$dir" dir was not created
        popOptE
        return $r
    else
        trace created dir "$dir"
        popOptE
        return 0
    fi
}


# -------------------------------------------------------------

# apkg-log contains list of things we have created (files and directories)
#   files [...] destination
# output is for example  red
#      mkdir /one
#      mkdir /one/two
#      cp /one/two/three
#      cp /one/two/four
#      mkdir /one/two/five
#


##
# copyFiles [--silent] [--nobackup] [--nolog] <SOURCE> [<SOURCE> ...] <DIRECTORY>
# --silent: do not generate UI output.
# --nobackup: do not backup files or directory if they exist in DIRECTORY
# --nolog: do not log the function call.
# SOURCE: file or directory to copy.
# DIRECTORY: destination directory to copy the source files to.
#
# Copy one or more files to DESTINATION DIRECTORY. The filenames are logged
# and will be automatically removed at uninstallation. Wildcards can be used
# when function is called with file arguments.
#
# If the --silent flag is passed, no UI output will be generated. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nobackup flag is passed, function  will not create backups. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nolog flag is passed, no logging output will be generated. This option
# is primarily meant for internal usage from the other APIs.
#
# This function can make a backup if the destination directory contains
# the same file or directory name. The original file or directory will be moved
# to the BACKUP environment variable location.
#
# Example:
# copyFiles hello.txt world.txt "$PREFIX/doc"
#   copy files to destination --> "$PREFIX/doc"
#
# copyFiles libexec/bin/*.exe "$PREFIX/libexec"
#   copy *.exe files in directory to destination   --> "$PREFIX/libexec"
#
# copyFiles libexec/bin/* "$PREFIX/libexec"
#   copy files in directory to destination         --> "$PREFIX/libexec"
#
# copyFiles libexec/bin "$PREFIX/libexec"
#   recursive copy of directory bin to destination --> "$PREFIX/libexec/bin"
#
function copyFiles() {

    trace called with $@
    pushOptE; set +e;

    local nobackup="false"
    local nolog="false"
    local silent="false"
    local file_count=0 backup_count=0
    while [[ "$1" != "" ]]; do
        case "$1" in
            "--nobackup")   nobackup="true"; shift;;
            "--nolog")      nolog="true"; shift;;
            "--silent")     silent="true"; shift;;
            *) break;;
        esac
    done

    if [ $# -le 1 ]; then
        err must have at least two arguments: SOURCE and DIRECTORY
        popOptE
        return 1
    fi

    # copy everything but last argument to an array
    # and create directory location from that last argument
    local i
    local location
    local -a args
    local count
    local -a files

    args=("$@")
    count=${#args[@]}
    (( count-- ))
    i=0
    while (( $i < count )); do
        files[$i]="${args[$i]}"
        (( i++ ))
    done
    location="${args[i]}"
    if [[ ${location:0:1} != "/" ]]; then
        location="`pwd`/${args[i]}"
    fi

    # send outputStatus the destination location such that if $location is the
    # same information, it will not be shown
    if ! $silent; then
        __outputStatus_status="$location"
        outputStatus $( out "$intl_INSTALLING_FILE" "$location" )
    fi
    
    # fail if destination directory name exists as a filename - protect filesystem from giving this error
    if [ -f "$location" ]; then
        local f=`basename "$location"`
        local d=`dirname "$location"`
        err an existing filename \"$f\" matches the destination directory at \"$d\"
        popOptE;
        return 1;
    fi
    [ ! -d "$location" ] && mkdirs "$location"
    
    local oPWD="$PWD"
    local index=0
    while (( "$index" < "$count" )); do
        local element=`echo "${files[$index]}"`
        local directory=`dirname "$element"`
        local filename=`basename "$element"`
        #trace dir:$directory file:$filename element:$element
        cd "$directory"

        # backup file if it exists. mike - i disabled this for now as
        # the logic is clearly wrong here w.r.t directories like
        # locale where we want some kind of overlay system
        if false && [ -e "$location/$filename" ] && ! $nobackup; then
            trace backing up file to $autopackage_db_location/$BACKUP/$location/$filename
            [ ! -d "$autopackage_db_location/$BACKUP/$location" ] && mkdirs --nolog --session "$autopackage_db_location/$BACKUP/$location"
            mv -f "$location/$filename" "$autopackage_db_location/$BACKUP/$location/$filename"
            logCommand "recoverFile `escapeFilename \"$location/$filename\"`"
            (( backup_count++ ))
        fi

        if [ -f "$filename" ]; then
            # copy single file
            cp -f "$filename" "$location/$filename"
            ! $nolog && logFile "$location/$filename"
            (( file_count++ ))
        else
            # make filename `.' so ls will respond correctly
            if [[ "$filename" == "*" ]]; then
                filename="."
            fi
            ls_1R=`ls -1R "$filename"`
            # interate over ls_1R such that $location/$directory/$line make the destination filename
            for line in $ls_1R; do
                #trace line=$line
                if [ `expr index "$line" ':'` -gt "0" ]; then
                    directory=`echo "$line" | sed 's|:||'`
                elif [[ "$line" != "" ]] && [ ! -d "$directory/$line" ]; then
                    # make directory if necessary
                    [ ! -d `dirname "$location/$directory/$line"` ] && mkdirs --nolog --session `dirname "$location/$directory/$line"`
                    # copy file
                    #trace copied to $location/$directory/$line
                    cp -f "$directory/$line" "$location/$directory/$line"
                    ! $nolog && logFile "$location/$directory/$line"
                    (( file_count++ ))
                fi
            done
        fi

        (( index++ ))
        cd "$oPWD"
    done
    trace copied $file_count files and backup $backup_count files
    popOptE
    return 0

}


##
# copyFile [--silent] [--nobackup] [--nolog] <FILENAME-FROM> <FILENAME-TO>
# --silent: do not generate UI output.
# --nobackup: do not backup files if files exist in DIRECTORY
# --nolog: do not log the function call.
# FILENAME-FROM: path to the source file
# FILENAME-TO: path to the destination file or directory
#
# The copyFile function allows you to copy a single file to a given destination filename.
# It can be used when you want to rename a file while also copying it. This
# function creates a backup file if the destination already exists. It produces no GUI output
#
# If the --silent flag is passed, no UI output will be generated. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nobackup flag is passed, function will not create backups. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nolog flag is passed, no logging output will be generated. This option
# is primarily meant for internal usage from the other APIs.
#
# Example:
# copyFile share/welcome-en.png "$PREFIX/share/welcome.png"
# copyFile share/welcome-en.png "$PREFIX/share"              --> "$PREFIX/share/welcome-en.png"
function copyFile() {
    pushOptE; set +e;
    trace called with $1 $2

    local nobackup="false"
    local nolog="false"
    local silent="false"
    local file_count=0 backup_count=0
    while [[ "$1" != "" ]]; do
        case "$1" in
            "--nobackup")   nobackup="true"; shift;;
            "--nolog")      nolog="true"; shift;;
            "--silent")     silent="true"; shift;;
            *) break;;
        esac
    done
    
    if [[ "$#" != "2" ]]; then
        err must have two arguments: SOURCE and DESTINATION
        popOptE
        return 1
    fi

    if [ ! -e "$1" ]; then
        err SOURCE does not exist: $1
        popOptE
        return 1
    elif [ ! -f "$1" ]; then
        err SOURCE should be a file: $1
        popOptE
        return 1
    fi

    local filename_from="$1"
    local filename_to="$2"

    if [[ ${filename_to:0:1} != "/" ]]; then
        filename_to="`pwd`/$filename_to"
    fi

    # filename_to is a directory, so use the same filename from filename_from :)
    if [ -d "$filename_to" ]; then
        filename_to="$filename_to/`basename $filename_from`"
    fi
    local filename_to_dir=`dirname "$filename_to"`

    # send outputStatus the destination location such that if $filename_to_dir is the
    # same information, it will not be shown
    if ! $silent; then
        __outputStatus_status="$filename_to_dir"
        outputStatus $( out "$intl_INSTALLING_FILE" "$filename_to_dir" )
    fi

    # fail if destination directory name exists as a filename - protect filesystem from giving this error
    if [ -f "$filename_to_dir" ]; then
        local f=`basename "$filename_to_dir"`
        local d=`dirname "$filename_to_dir"`
        err an existing filename \"$f\" matches the destination directory at \"$d\"
        popOptE
        return 1
    fi
    [ ! -d "$filename_to_dir" ] && mkdirs "$filename_to_dir"
    
    trace filename_from=$filename_from
    trace filename_to=$filename_to
    trace filename_to_dir=$filename_to_dir

    # backup file if it exists - see above
    if false && [ -e "$filename_to" ] && ! $nobackup; then
        trace backing up file to $autopackage_db_location/$BACKUP/$filename_to
        [ ! -d "$autopackage_db_location/$BACKUP/$filename_to_dir" ] && mkdirs --nolog --session "$autopackage_db_location/$BACKUP/$filename_to_dir"
        mv -f "$filename_to" "$autopackage_db_location/$BACKUP/$filename_to"
        ! $nolog && logCommand "recoverFile `escapeFilename \"$filename_to\"`"
        (( backup_count++ ))
    fi

    # copy single file
    cp -f "$filename_from" "$filename_to"
    ! $nolog && logFile "$filename_to"
    (( file_count++ ))

    trace copied $file_count files and backup $backup_count files
    popOptE
    return 0
}


##
# recoverFile <FILENAME>
# FILENAME: file or directory to move back into filesystem.
# Returns: 0 always.
#
# Recovers file or directory from the package's BACKUP environment
# variable directory and moves it back into the filesystem.<p>
#
# recoverFile() is the companion to the copyFile() functions. During a
# copy process if the file exists, the previous file is moved to
# $autopackage_db/$BACKUP/<mimic filesystem>. The $BACKUP variable is set
# as `$ROOTNAME/backup'.
#
# See also: copyFiles(), copyFile(), linkFile().
#
# Example:
# copyFiles "foo" "/some/directory" collides with a pre-existing file
# # uninstall log would have the entry
# # recoverFile /some/directory/foo   and the colliding file
# # would be copied to $autopackage_db/$BACKUP/some/directory/foo
#
function recoverFile() {

    trace called with $@
    
    if [ ! -e "$autopackage_db_location/$BACKUP/$1" ]; then
        warn $autopackage_db_location/$BACKUP/$1 does not exist to recover
    else
        trace recovering $autopackage_db_location/$BACKUP/$1
        local location=`dirname "$1"`
        copyFiles --silent --nolog --nobackup "$autopackage_db_location/$BACKUP/$1" "$location"
    fi

    trace finished
    return 0

}


##
# linkFile  [--silent] [--nobackup] [--nolog] <TARGET> <LINK_NAME>
# --silent: do not generate UI output.
# --nobackup: do not backup files if files exist in DIRECTORY
# --nolog: do not log the copyFiles function call.
# TARGET: filename on which to link to.
# LINK_NAME: name of the link to the TARGET.
# Returns: 0 on success, 1 on error.
#
# Create the symbolic link LINK_NAME which points to TARGET.
# This link will be recorded in the uninstall log.
#
# This function is exactly like 'ls -s', except that it also
# adds a record to the uninstall log.
#
# If the --silent flag is passed, no UI output will be generated. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nobackup flag is passed, function will not create backups. This option
# is primarily meant for internal usage from the other APIs.
#
# If the --nolog flag is passed, no logging output will be generated. This option
# is primarily meant for internal usage from the other APIs.
function linkFile() {
    pushOptE; set +e;
    trace called with $@

    local silent="false"
    local nolog="false"
    local nobackup="false"
    local file_count=0 backup_count=0
    while [[ "$1" != "" ]]; do
        case "$1" in
            "--silent")     silent="true"; shift;;
            "--nolog")      nolog="true"; shift;;
            "--nobackup")   nobackup="true"; shift;;
            *) break;;
        esac
    done

    local filename_from="$1"
    local filename_to="$2"

    if [[ ${filename_to:0:1} != "/" ]]; then
        filename_to="`pwd`/$filename_to"
    fi
    local filename_to_dir=`dirname "$filename_to"`

    # send outputStatus the destination location such that if $filename_to_dir is the
    # same information, it will not be shown
    if ! $silent; then
        __outputStatus_status="$filename_to_dir"
        outputStatus $( out "$intl_INSTALLING_LINK" "$filename_to" "$filename_from")
    fi

    [ ! -d "$filename_to_dir" ] && mkdirs "$filename_to_dir"
    
    # backup LINK_NAME if it exists
    # mike - disabled, see above
    if false && [ -e "$filename_to" ] && ! $nobackup; then
        trace backing up file to $autopackage_db_location/$BACKUP/$filename_to
        [ ! -d "$autopackage_db_location/$BACKUP/$filename_to_dir" ] && mkdirs --nolog --session "$autopackage_db_location/$BACKUP/$filename_to_dir"
        mv -f "$filename_to" "$autopackage_db_location/$BACKUP/$filename_to"
        ! $nolog && logCommand "recoverFile `escapeFilename \"$filename_to\"`"
        (( backup_count++ ))
    fi

    ln -sf "$filename_from" "$filename_to" &>/dev/null
    r="$?"
    if [[ "$r" != "0" ]]; then
        popOptE
        return 1
    else
        ! $nolog && logFile "$2"
        popOptE
        return 0
    fi

}


##
# updateEnv [--append] [--check] <VARIABLE> <PATH>
# VARIABLE: The environment variable to set.
# PATH: The path to add to it.
# --append: Append the value to the variable instead of prepending it.
# --check: If the current value of the given variable contains PATH, do nothing
#
# Prepends a path to an environment variable and add a command in the logfile to
# remove it if needed at uninstall. The modifications to the environment
# variables will be saved in various shell startup scripts.
#
# If the environment variable already contains the given path, nothing is done.
#
# Example:
# updateEnv --append --check PATH "/path/to/my/program/bin"
function updateEnv() {
    pushOptE; set +e;

    local append=false
    local check=false

    while [[ ${1:0:2} == "--" ]]; do
	if [[ $1 == "--append" ]]; then append=true;
	elif [[ $1 == "--check" ]]; then check=true;
	else warn unknown parameter $1; fi
	shift
    done

    if $check; then
	if echo ${!1} | awk " BEGIN { RS=\":\"; found=0; }
 	                      { if (\$0 == \"$2\") found=1; }
                              END { if (!found) exit 1; } "; then
	    trace check failed
	    popOptE
	    return 0
	fi
    fi
	    
    local var="$1"
    local dest="$2"

    if $append; then
        cmd="export $var=\"\$$var:$dest\""
    else
        cmd="export $var=\"$dest:\$$var\""
    fi

    if [[ `id -u` != "0" ]]; then
	files=("$HOME/.bashrc")
    else
	files=("/etc/profile")
    fi

    for file in "${files[@]}"; do
        touch "$file"
        removeLine "$file" "$cmd"
        echo "$cmd" >> "$file"
        logCommand "if dirIsEmpty `escapeFilename \"$dest\"`; then removeLine \"$file\" '$cmd'; fi;"
    done

    popOptE;
    return 0
}

# an alias
function installBin() {
    installExe "$@"
}

##
# installExe <FILENAMES>
# FILENAMES: The binaries or scripts to install.
#
# Installs an executable file to $PREFIX/bin.  $PATH will be automatically updated if $PREFIX/bin is not currently in it.
# The file will be given execution permissions. If there is already an executable with the same name in the PATH, then
# this function will build a bootstrap script so the program is guaranteed that when it does execute, PATH, LD_LIBRARY_PATH
# and MANPATH will be set correctly for that PREFIX. This function operates in terms of copyFiles(), so the notes for that
# function apply here also.
function installExe() {
    pushOptE; set +e;
    outputStatus "$intl_INSTALLING_EXE"

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set for this function to succeed"; return 1; fi;

    # Update PATH
    updateEnv --check "PATH" "$PREFIX/bin"

    # Loop through every file
    for F in "$@"; do
        local name=`basename "$F"`
        local cmd=`command -v "$name" 2>/dev/null`
        local apkg_dest=`echo "$PREFIX/bin" | sed 's|//|/|g'`

        if [[ ! -f "$F" ]]; then
            warn "installExe: file $F not found in payload.";
            continue
        fi

	# Check whether this binary can be linked
	local ferr=`LC_ALL=C LD_BIND_NOW=1 LD_TRACE_LOADED_OBJECTS=1 PATH=".:$PATH" "$F" 2>&1 >/dev/null`
	if [[ "$ferr" != "" ]]; then
		err "$F cannot be run on this system."
		return 1
	fi

        # Is such there an executable with the same name already in PATH?
        if command -v "$name" &>/dev/null \
           && [[ "$cmd" != "$apkg_dest/$name" ]]; then
            # Yes; create a bootstrap script to avoid conflicts

            warn there is already an executable with the same name in the path, building bootstrap script
            mkdirs --session "$apkg_dest"
            copyFile "$F" "$apkg_dest/.proxy.$name"
            cat <<EOF >"$apkg_dest/$name"
#!/bin/bash
# Autopackage bootstrap script
# Name:    $SHORTNAME
# Package: $ROOTNAME
# Version: $SOFTWAREVERSION
# $DISPLAYNAME

PATH="$PREFIX/bin:\$PATH"
LD_LIBRARY_PATH="$PREFIX/lib:\$LD_LIBRARY_PATH"
MANPATH="\$MANPATH:$PREFIX/share/man"

export PATH LD_LIBRARY_PATH MANPATH

exec "$apkg_dest/.proxy.$name" "\$@"
EOF
            chmod +x "$apkg_dest/$name"
            logFile "$apkg_dest/$name"
        else
            # No; copy this file & chmod +x it
            copyFiles --silent "$F" "$apkg_dest"
            chmod +x "$apkg_dest/$name"
        fi
    done
    popOptE;
}


##
# installLib <LIBRARIES>
# LIBRARIES: The file names of the libraries to install.
#
# Install shared libraries (.so files) to $PREFIX/lib. If the path to install to is not in the linker
# configuration, then it will be updated. If installing as root, /etc/ld.so.conf will be altered and
# ldconfig rerun. If installing as user, updateEnv will be called to ensure that the destination
# appears in LD_LIBRARY_PATH.
function installLib() {
    pushOptE; set +e;

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set for this function"; return 1; fi

    outputStatus "$intl_INSTALLING_LIB"
    local dest="$PREFIX/lib"

    trace dest is $dest
    if [[ `id -u` != "0" ]]; then
	trace updating LD_LIBRARY_PATH variable
	updateEnv "LD_LIBRARY_PATH" "$dest" --append
    fi

    # library file removal instruction and directory removal
    logCommand "/sbin/ldconfig -n `escapeFilename \"$dest\"`; [[ \`id -u\` == \"0\" ]] && /sbin/ldconfig; removeDir `escapeFilename \"$dest\"`;"

    # copy the libraries
    copyFiles --silent "$@" "$dest"

    # now setup the symlinks for the lib - ldconfig will remove dead symlinks for us on uninstall
    trace setting up symlinks in $dest
    /sbin/ldconfig -n "$dest"

    # make the .so symlink if one isn't already present for development....
    local l
    for l in "$@"; do
    	# libfoo.so
	local lname=$( basename "$l" | sed 's/\(\.so\).*/\1/' )
	trace lname=$lname
	# libfoo.so.0
	local soname=$( objdump -p "$l" | grep SONAME | awk '{print $2}' )

	if [[ "$soname" == "" ]]; then
	    continue
	fi

	# link libfoo.so.0 to libfoo.so
	if [[ -e "$dest/$soname" && ! -e "$dest/$lname" ]]; then
	    trace linking $dest/$soname to $dest/$lname
	    ln -s "$dest/$soname" "$dest/$lname" # make the symlink for ld
	    logFile "$dest/$lname"
	else
	    warn development symlink already present
        fi

    done

    if [[ `id -u` == "0" ]]; then
	trace we are root
    	# scan the ld.so.conf file, is this directory in the cache?
    	if ! grep "^$dest$" /etc/ld.so.conf >/dev/null; then
	    if [[ "$autopackage_update_linker_cache" == "true" ]]; then
		trace adding \$dest to ld.so.conf
	    	addLine /etc/ld.so.conf "$dest"
	    	# log it
	    	logCommand "removeLinkerCacheEntry `escapeFilename \"$dest\"`"
	    else
	    	# print a warning if installing to a prefix that isn't in the cache
	    	outputStatus "$intl_INSTALLING_PATH_NOT_IN_LINKER_CACHE";
		outputStatus "$intl_INSTALLING_PATH_NOT_IN_LINKER_CACHE_SOLUTION" "$dest";
	    fi
    	fi
	outputStatus "$intl_INSTALLING_UPDATING_LINKER";
	/sbin/ldconfig
    fi
    trace done
    popOptE;
}


##
# installMan <SECTION> <FILENAMES>
# SECTION: The section these manpages belong to.
# FILENAMES: The manpages to install.
#
# Install Unix manual pages. The manpage search path will be automatically updated if necessary.
function installMan() {
    pushOptE; set +e;
    if [[ $# -lt 2 ]]; then
        return 1
    fi

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set for this function"; return 1; fi

    local section=$1
    shift
    outputStatus $( outn "$intl_INSTALLING_MAN" "$section" )

    if [[ "$autopackage_update_manpath" == "true" ]]; then
        if [[ `id -u` == "0" ]]; then
            # add path to the correct config file, either /etc/man.config or /etc/manpath.config
            if [ -e /etc/manpath.config ] && ! grep  -cE "^MANDATORY_MANPATH $PREFIX/share/man$" /etc/manpath.config &>/dev/null; then
                addLine /etc/manpath.config "MANDATORY_MANPATH $PREFIX/share/man"
            elif [ -e /etc/man.config ] && ! grep -cE "^MANPATH +$PREFIX/share/man$" /etc/man.config &>/dev/null; then
                addLine /etc/man.config "MANPATH $PREFIX/share/man"
            fi
            logCommand "removeManPath `escapeFilename \"$PREFIX/share/man\"`"
        else
            updateEnv "MANPATH" "$PREFIX/share/man" --append
        fi
    fi

    copyFiles --silent "$@" "$PREFIX/share/man/man$section"
    popOptE;
}


##
# installInfo <FILENAMES>
# FILENAMES: The .info file(s) to install.
#
# Install GNU TeXinfo files. The info dirfile will be updated as necessary.
function installInfo() {
    pushOptE; set +e;

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set for this function"; return 1; fi

    trace called with $@

    # Check for Info directory and Info file location
    if [[ `id -u` == "0" ]]; then
        info_dir="$PREFIX/share/info"
        info_dir_file="/usr/info/dir"
        [ -e "/usr/share/info" ] && info_dir_file="/usr/share/info/dir"
    else
        info_dir="$PREFIX/share/info"
        unset info_dir_file
    fi

    trace info_dir=$info_dir, info_dir_file=$info_dir_file

    if [ "$info_dir" ]; then

        outputStatus "$intl_INSTALLING_INFO"

        for F in "$@"; do
            local base=`basename "$F"`

            copyFiles --nolog --silent "$F" "$info_dir"

            # fixup the menu imports so they point to the correct prefix
            cat "$info_dir/$base" | sed "s|%PREFIX%|$info_dir|g" > "$info_dir/$base";
            gzip --best "$info_dir/$base"

            # push a filelist entry since adding a gz extension
            logFile "$info_dir/$base.gz"
            
            # update log and uninstall instructions
            logCommand "removeInfo `escapeFilename \"$info_dir/$base.gz\"`"

            # install the info file
            pushd "$info_dir" >/dev/null
            if locateCommand -o install-info --version 2>&1 | grep Debian >/dev/null; then
		trace "debian install-info detected"
                cmd_to_run="`locateCommand -l install-info` \"$base\""
            elif test -w "$info_dir_file"; then
		trace "standard gnu install-info detected"
                cmd_to_run="`locateCommand -l install-info` --dir-file=\"$info_dir_file\" \"$base\""
            fi
	    trace about to run $cmd_to_run
            eval "$cmd_to_run"
            popd >/dev/null
            trace "$cmd_to_run"
        done

    fi
    trace done
    popOptE;
}


##
# installConfig <FILENAMES>
# FILENAMES: configuration file(s) to install.
#
# Install configuration files into appropriate directory.
#
function installConfig() {
    pushOptE; set +e;

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set in order to use installConfig"; return 1; fi

    trace called with $@

    # Set configuration (etc) directory based on user
    etc_dir=`_userConfig`

    if [ "$etc_dir" ]; then

        outputStatus "$intl_INSTALLING_CONFIG"

        for F in "$@"; do

            if [ -f "$F" ]; then

                local base=`basename "$F"`

                # copy file to database silently, write uninstall command using baselineFile function
                copyFiles --silent --nolog "$F" "$etc_dir"
                logCommand "baselineFile `escapeFilename \"$etc_dir/$base\"`"

                # Fixup absolute references by substituting in prefix
                cat "$etc_dir/$base" | sed "s|%PREFIX%|$PREFIX|g" > "$etc_dir/$base";

                # update baseline file
                getFile --baseline "$etc_dir/$base" >> "$meta_dir/baseline"
                r="$?"
                if [[ "$r" != "0" ]]; then
                    err running getFile function on \"$etc_dir/$base\"
                    return "$r"
                fi
            
            fi

        done

    fi
    trace done
    popOptE;
}


##
# removeFile <FILENAME>
# FILE: file to remove.
# Returns: 0 for all cases.
#
function removeFile() {
    pushOptE; set +e;

    rm -f "$1" >/dev/null

    popOptE
    return 0

}


##
# baselineFile <FILENAME>
# FILE: file to compare.
# Returns: 0 if FILE is processed.
# Returns: 1 if malformed function call.
#
# Process to compare two files and archive FILE if it is different
# from file information which is archived in a baseline file
# defined by $ROOTNAME/baseline.<p>
#
# baselineFile() is the companion to installConfig() function. During the
# installConfig, a MD5 of the original configuration file is added to the
# baseline file called $ROOTNAME/baseline. The logfile would have a
# baselineFile to determine how to handle this special file. During the
# uninstall, baselineFile() creates a MD5 of the configuration file to
# delete and compares it the baseline MD5. autopackage will archive
# configuration files that are different from installed files while
# deleting the configuration files if they match. So custom or modified
# configuration files will still exist in the system for reference
# purposes.<p>
#
# If files are different then FILE is archived to FILE.apkgsave .
# If FILE.apkgsave exists then FILE.apkgsave is moved to FILE.apkgsave.1
# and FILE is copied to FILE.apkgsave . With this method, if FILE
# is archived then FILE.apkgsave is the latest saved version. Archived
# version are then FILE.apkgsave.2, FILE.apkgsave.3, etc.
#
# See also: installConfig().
#
# Example:
# installConfig "etc/foo-config"
# # uninstall log would have either entry
# # baselineFile /home/user/.local/etc/foo-config
# #   or
# # baselineFile /etc/foo-config
#
function baselineFile() {
    pushOptE; set +e;

    trace called with $@

    # if not different than original file then delete
    # otherwise mv to .apkgsave if available and then version like FILE.apkgsave.1
    if _checkFile --md5 "$1"; then
        trace files match
    else
        # check if file exist and move the existing file with an appended index number
        trace archiving file $1
        if [ -f "$1.apkgsave" ]; then
           # infinite loop
           while true; do
           ((i++))
                if [ ! -e "$1.apkgsave.$i" ]; then
                    trace copying previous archive file to $1.apkgsave.$i
                    cp -f "$1.apkgsave" "$1.apkgsave.$i"
                    trace removing previous archive file $1.apkgsave
                    removeFile "$1.apkgsave"
                    break
                fi
            done
        fi
        trace copying archive file to $1.apkgsave
        cp -f "$1" "$1.apkgsave"
    fi
    removeFile "$1"
    local config_dir=`dirname "$1"`
    removeDir "$config_dir"
    trace done
    return 0
    popOptE
}


##
# requireLibC <SYMBOL> [SYMBOL..]
# SYMBOL: A string of the form "GLIBC_2.1.3", but obviously adapted to the version reference that your binary needs
#
# requireLibC allows you to ensure that the system has at least the version of glibc that your binaries require.
# glibc does not break backwards compatability (in theory), it only ever adds interfaces. The reason this is
# a separate function to require() is that glibc is a special pacakge - it is always present, and uses symbol
# versioning.
#
# You can see which tag to use by running objdump on your binaries after they are compiled using the autopackage
# tools, and looking at the bottom of the output of `objdump -p binaryname`.
#
# This function outputs to the current frontend.
#
# This function will typically be called for you by the autopackage framework before execution of the prep script.
# You should not need to call it yourself.
#
# Example:
# requireLibC GLIBC_2.0
function requireLibC() {
    pushOptE; set +e;
    if [[ "$@" == "" ]]; then return 0; fi; # we may be called with no arguments from backend.template, if no binaries are in the package
    outputTest "$intl_CHECKING_FOR_GLIBC_SYMBOL_VERSIONS"
    trace looking for $@
    local r
    r=`_checkForGlibcVersions $@` # can't use locals here due to bug in bash?
    if [[ $? != 0 ]]; then
	trace failed
	outputTestFail
	local msg=$( out "$intl_NEED_SYMBOLS" "`echo $r`" )
	outputFail "$msg"

	popOptE;
	return 1
    else
	trace passed
	outputTestPass

	popOptE;
	return 0
    fi
}


##
# _installDesktopInitialize
#
# Initializes all available base desktop directory variables.
# This is an internal function and should not be used directly in scripts.
#
# The following variables are initalized:
#
# $xdgdir                 - XDG data base directory
# $xdgdir_mime            - XDG MIME base directory
# $desktop_file_install   - XDG .desktop install/validate software
# $dmsdir                 - Debian Menu System base directory
# $gnome2dir              - GNOME 2 base directory
# $kde2dir                - KDE 2 & 3 base directory
#
function _installDesktopInitialize() {
    trace called

    if [[ "$PREFIX" == "" ]]; then err "PREFIX must be set in order to use _installDesktopInitialize()"; return 1; fi;

    # Check for XDG Desktop Menu Specification   http://freedesktop.org/Standards/menu-spec/
    if [[ `id -u` != "0" ]]; then
        if [[ "$XDG_DATA_HOME" != "" ]]; then
            xdgdir="$XDG_DATA_HOME"
        else
            xdgdir="$PREFIX/share"
        fi
    else
        xdgdir="$PREFIX/share"
    fi
    trace xdgdir=$xdgdir

    # Check for XDG Shared MIME-info Database   http://freedesktop.org/Standards/shared-mime-info-spec/
    xdgdir_mime="$xdgdir/mime"
    trace xdgdir_mime=$xdgdir_mime

    # Check for XDG 'desktop-file-utils'   http://freedesktop.org/Software/desktop-file-utils
    desktop_file_install=`locateCommand -l desktop-file-install`
    trace desktop_file_install=$desktop_file_install

    # Check for Debian Menu System [Mandrake]
    if locateCommand update-menus; then
        if [[ `id -u` != "0" ]]; then
            dmsdir="$HOME/.menu"
        else
            dmsdir="/usr/lib/menu"
        fi
    fi
    trace dmsdir=$dmsdir

    # Check for GNOME 2
    gnome2dir="`getGnome2Prefix`"
    if [[ "$gnome2dir" != "" ]]; then
        if [[ `id -u` == "0" ]]; then
            gnome2dir="$gnome2dir"
        else
            gnome2dir="$HOME/.gnome2"
        fi
    fi
    trace gnome2dir=$gnome2dir

    # Check for KDE 2 & KDE 3
    if locateCommand kde-config --prefix; then
        local kdeprefix="$lc_output"
        local kdeversion=`locateCommand -o kde-config --version | grep KDE: | awk -F'[ ]' '{ print $2 }'`
        kde2dir="$kdeprefix"

    elif locateCommand kicker; then
        kde2dir="$lc_location"
        kde2dir=`dirname "$kde2dir"`
    fi
    
    if [ "$kde2dir" ] && [[ `id -u` != "0" ]]; then
        kde2dir="$HOME/.kde"
    fi
    trace kde2dir=$kde2dir

    __installDesktop_initialized=true

}


##
# installDesktop [--no-path-adjust] <CATEGORY> <FILENAMES>
# --no-path-adjust: Do not change paths in the .desktop file to absolute paths.
# CATEGORY: The category the desktop entries belong to.
# FILENAMES: The desktop entries to install.
#
# Install a menu entry (.desktop file). This function will automatically
# detect GNOME and KDE and will copy the desktop entires to the proper
# locations.<br>
#
# By default, this function will change the paths in the TryExec, Exec
# fields to absolute filenames by prepending "$PREFIX/bin".
# For instance, "Exec=gimp" will be changed to "Exec=/home/myuser/.local/bin/gimp".<br>
#
# If you do not want installDesktop to change the paths, pass the --no-path-adjust argument.
#
# The CATEGORY parameter is used for non-vFolder/menu spec desktops such as KDE 3.1 and below,
# and possibly in future window managers like fluxbox/icewm as well. You should probably
# specify a string that would fit with the pre-KDE 3.2 menus here, such as "Graphics/Vector Graphics"
#
# Example:
# installDesktop "Network/Instant Messaging" gaim.desktop
function installDesktop() {
    pushOptE; set +e;
    
    outputStatus "$intl_INSTALLING_DESKTOP"

    # Initialize Base Desktop Directories
    if ! ${__installDesktop_initialized:-false}; then
        _installDesktopInitialize
    fi

    local adjust_path=true
    if [[ "$1" == "--no-path-adjust" ]]; then
        adjust_path=false
        shift
    fi

    local category="$1"
    shift

    for F in "$@"; do
        [ ! -f "$F" ] && err "installDesktop: $F not found in payload."
    done

    # Setup for XDG
    if [[ "$xdgdir" != "" ]]; then
        xdgdir_desktop="$xdgdir/applications"
    fi

    # Setup for DMS
    # Blocks GNOME, KDE desktops - need to block if needed - blackbox, enlightenment, WindowMaker
    if [ ! "$dmsdir" ]; then

        # Setup for GNOME 2
        if [ "$gnome2dir" ]; then
            if [[ `id -u` == "0" ]]; then
                gnome2dir_desktop="$gnome2dir/share/applications"
                # gnome2dir and xdgdir could be the same - check and clear xdg if necessary
                if [[ "$xdgdir_desktop" == "$gnome2dir_desktop" ]]; then
                    unset xdgdir_desktop
                    trace xdgdir_desktop and gnome2dir_desktop are the same, clearing xdgdir_desktop
                fi
            else
                gnome2dir_desktop="$gnome2dir/vfolders/applications"
            fi
        fi

        # Setup for KDE 2 & KDE 3
        if [ "$kde2dir" ]; then
            # Check for RedHat KDE
            if [[ -d /usr/share/applnk-redhat ]]; then
                kde2dir_desktop="$kde2dir/share/applnk-redhat"
            else
                kde2dir_desktop="$kde2dir/share/applnk"
            fi
        fi

    fi

    # XDG Desktop Menu Specification Support
    if [ "$xdgdir_desktop" ]; then
        # Install the desktop entry
        trace XDG support
        for F in "$@"; do
            local base=`basename "$F"`
            local filename="$xdgdir_desktop/$base"
            copyFiles --silent "$F" "$xdgdir_desktop"
            _setupDesktopFile $adjust_path "$filename"
        done
    fi

    # Debian Menu System Support [Mandrake]
    if [ "$dmsdir" ]; then
        trace debian menu support

        for F in "$@"; do
            local section="$category"
            
            # Mandrake requires KControl modules and KDE screensavers to be in different places
            if [ -e /etc/mandrake-release ]; then
                section=`echo $section | sed 's/System\/ScreenSavers/.hidden\/ScreenSavers/'`
                section=`echo $section | sed 's/Settings\//System\/Configuration\/KDE\//'`
            fi
            
            if [[ "$section" != "" ]]; then
                section=`escapeValue "$section"`
            else
                section=`escapeValue "/"`
            fi
            
            echo -n "?package(local.$SHORTNAME):needs=\"x11\" section=\"$section\"" > "${TMP}/apkg-debiantmp1"
            echo -n " kde_opt=\"X-Autopackage=${ROOTNAME}\\\n" > "${TMP}/apkg-debiantmp2"
            
            # Go through each line of the file
            cat "$F" |
            while read line
            do
                if [ "${line:0:1}" == "[" ]; then
                    sectionName=$line
                fi
                if [ "$sectionName" != "[Desktop Entry]" ]; then
                    echo -n "$line\\\n" >> "${TMP}/apkg-debiantmp2"
                    continue
                fi
                if [ "$line" == "[Desktop Entry]" ]; then
                    continue
                fi
                
                local key=`echo $line | awk 'BEGIN{FS="[ \t]*=[ \t]*"}; { print $1 }'`
                local value=`echo $line | awk '{ match($0, "[ \t]*=[ \t]*"); print substr($0, RSTART+RLENGTH) }' | sed 's/\"/\\\"/'`
                
                if [ "${#key}" -le "0" ]; then
                    continue
                fi
                
                case "$key" in
                    "Name" ) echo -n " title=\"$value\"" >> "${TMP}/apkg-debiantmp1" ;;
                    "Exec" ) echo -n " command=\"$value\"" >> "${TMP}/apkg-debiantmp1" ;;
                    "Icon" ) echo -n " icon=\"$value\"" >> "${TMP}/apkg-debiantmp1" ;;
                    "Comment" ) echo -n " longtitle=\"$value\"" >> "${TMP}/apkg-debiantmp1" ;;
                    "MimeType" ) echo -n " mimetypes=\"$value\"" >> "${TMP}/apkg-debiantmp1" ;;
                    "Categories" ) ;;
                    * ) echo -n "$key=$value\\\n" >> "${TMP}/apkg-debiantmp2" ;;
                esac
            done
            
            echo "\"" >> "${TMP}/apkg-debiantmp2"
            read
            mkdirs "$dmsdir"
            cat "${TMP}/apkg-debiantmp1" "${TMP}/apkg-debiantmp2" >> "$dmsdir/$SHORTNAME"
            trace added debian menu entry to "$dmsdir/$SHORTNAME"
            rm -f "${TMP}/apkg-debiantmp1" "${TMP}/apkg-debiantmp2"
            
        done

        # adding to logfile - update-menus command after removing package menu file
        logCommand "update-menus"

        # adding to logfile - delete the entire file if removing package
        logFile "$dmsdir/$SHORTNAME"

        # update desktop menus
        if locateCommand update-menus; then update-menus; fi

    fi # end of debian menus

    # GNOME 2/vfolder Support
    if [[ "$gnome2dir_desktop" != "" ]]; then
        # Install the desktop entry
        for F in "$@"; do
	    local base=`basename "$F"`
	    local filename

	    if [[ "$desktop_file_install" == "" ]]; then
	    	trace "gnome2, invoking copyFiles"
	    	copyFiles --silent "$F" "$gnome2dir_desktop"
	    	filename="$gnome2dir_desktop/$base"
	    else
	    	trace "gnome2, invoking $desktop_file_install"
	    	"$desktop_file_install" --vendor=apkg --dir="$gnome2dir_desktop" "$F"
	    	logFile "$gnome2dir_desktop/apkg-$base"
	    	filename="$gnome2dir_desktop/apkg-$base"
	    fi

	    _setupDesktopFile $adjust_path "$filename"

            # we need to mangle the ~/.gnome2/vfolders/applications.vfolder-info file
            # This hack is only needed for some user installs
            if [ `id -u` != "0" ]; then
                local categories=$( awk '/(Categories[ \t]*=[ \t]*)([^ ]*)/ {print $1}' $filename )
                if [ -z "$categories" ]; then
                    err "No categories specified in desktop file"
                else
                    categories=$( echo $categories | sed -e 's/Categories[ \t]*=[ \t]*\(.*\)$/\1/' )
                    categories=$( echo $categories | sed -e 's/;/ /g' )

                    # Determine gnome configuration prefix.
                    # I don't know if gnome-config can always be found on any
                    # gnome installation. Defaulting to /etc if not.
                    local gnome_config=`locateCommand -o gnome-config --sysconfdir`
                    if [ -n "$gnome_config" ]; then
                        local sysconfdir="$gnome_config"
                    else
                        local sysconfdir="/etc"
                    fi
                    trace gnome config sysconfdir: $sysconfdir

                    "$autopackage_prefix/libexec/autopackage/vfolder-magic" install "$HOME/.gnome2/vfolders/applications.vfolder-info" "$filename" "$sysconfdir" $categories
                    # Uninstall info
                    logCommand "`escapeFilename \"$autopackage_prefix/libexec/autopackage/vfolder-magic\"` uninstall `escapeFilename \"$HOME/.gnome2/vfolders/applications.vfolder-info\"` `escapeFilename \"$filename\"` `escapeFilename \"$sysconfdir\"`"
                fi
            fi
        done
    fi

    # KDE 2 & KDE 3 Support
    if [ "$kde2dir_desktop" ]; then

        # Change source desktop file for a KDE installation
        local desktop_entry_section=$( getSection "$F" "Desktop Entry" )
        local cat_count=$( echo "$desktop_entry_section" | grep -c '^Categories' )
        if (( cat_count == 1 )); then
            local categories_value=`echo "$desktop_entry_section" | grep '^Categories=' | awk 'BEGIN{FS="="}; { print $2 }' -`
            local i=${#categories_value}
            (( i-- ))
            if [[ ${categories_value:$i} != ";" ]]; then
                trace adding ';' to Desktop Entry Categories values in $F
                addSectionLine "$F" "Desktop Entry" "Categories=${categories_value};" "^Categories="
                # reload categories_value
                desktop_entry_section=$( getSection "$F" "Desktop Entry" )
                categories_value=`echo "$desktop_entry_section" | grep '^Categories=' | awk 'BEGIN{FS="="}; { print $2 }' -`
            fi
            if echo "$categories_value" | grep -qF "Core;" && ! echo "$categories_value" | grep -qF "KDE;"; then
                trace adding KDE to $F
                addSectionLine "$F" "Desktop Entry" "Categories=${categories_value}KDE;" "^Categories="
            fi
        elif (( cat_count > 1 )); then
            err "installDesktop: desktop file $F has more than 1 'Categories' keys in the Desktop Entry section."
        fi

        # Install the desktop entry
        if [[ "$desktop_file_install" != "" ]]; then
            trace "kde, invoking $desktop_file_install"
            for F in "$@"; do
                local base=`basename "$F"`
                logFile "$kde2dir_desktop/$category/apkg-$base"
            done
            "$desktop_file_install" --vendor=apkg --dir="$kde2dir_desktop/$category" --add-category="$category" "$@"

            # added for compatibility to XDG DMS
            for F in "$@"; do
                local base=`basename "$F"`

                #removeSectionLine "$kde2dir_desktop/$category/apkg-$base" "Desktop Entry" "^Categories="
                _setupDesktopFile $adjust_path "$kde2dir_desktop/$category/apkg-$base"
            done
        else
            trace "kde, invoking copyFiles"
            copyFiles --silent "$@" "$kde2dir_desktop/$category"

            # added for compatibility to XDG DMS
            for F in "$@"; do
                local base=`basename "$F"`

                #removeSectionLine "$kde2dir_desktop/$category/$base" "Desktop Entry" "^Categories="
                _setupDesktopFile $adjust_path "$kde2dir_desktop/$category/$base"
            done
        fi
    fi


    # keep the paths around for the summary screen at the end
    echo "$@" | while read; do
	if [[ ${REPLY:0:1} == "." ]] || [[ ${REPLY:0:1} != "/" ]]; then
	    # make absolute
	    REPLY="`pwd`/$REPLY"
	fi
	echo "$REPLY" >>"$working_dir/desktop-files"
    done
    popOptE;
}


##
# _setupDesktopFile
#
# Final setup process for .desktop file for use by desktop
# Modify and or add 'X-Autopackage' key to desktop files and add action menu
# to desktop files. Modify 'Exec' key to enable relocation of packages
function _setupDesktopFile() {
    local adjust_path="$1"
    local F="$2"
    
    if $adjust_path; then
        trace Adjusting path for $F...
        local bindir=$( escapeValue "$PREFIX/bin/" )
        grep -q '^TryExec=/' "$F" || safeSed "$F" "s/^TryExec=/TryExec=$bindir/"
        grep -q '^Exec=/' "$F" || safeSed "$F" "s/^Exec=/Exec=$bindir/"
    fi

    # ensure there is an ending ';' in the 'Categories' values from Desktop Entry section - if not rewrite the entry
    local desktop_entry_section=$( getSection "$F" "Desktop Entry" )
    local cat_count=$( echo "$desktop_entry_section" | grep -c '^Categories' )
    if (( cat_count == 1 )); then
        categories_value=`echo "$desktop_entry_section" | grep '^Categories=' | awk 'BEGIN{FS="="}; { print $2 }' -`
        trace categories_value is $categories_value
        local i=${#categories_value}
        (( i-- ))
        if [[ ${categories_value:$i} != ";" ]]; then
            trace adding ';' to Desktop Entry Categories values in $F
            addSectionLine "$F" "Desktop Entry" "Categories=${categories_value};" "^Categories="
            # reload categories_value
            desktop_entry_section=$( getSection "$F" "Desktop Entry" )
            categories_value=`echo "$desktop_entry_section" | grep '^Categories=' | awk 'BEGIN{FS="="}; { print $2 }' -`
        fi
        if ! echo "$categories_value" | grep -qF "X-Red-Hat-Base;"; then
            trace adding X-Red-Hat-Base to $F...
            addSectionLine "$F" "Desktop Entry" "Categories=${categories_value}X-Red-Hat-Base;" "^Categories="
        fi
    elif (( cat_count > 1 )); then
        err "installDesktop: desktop file $F has more than 1 'Categories' keys in the Desktop Entry section."
    fi

    addSectionLine "$F" "Desktop Entry" "X-Autopackage=${ROOTNAME}" "^X-Autopackage="

    # add action menu text to .desktop file
    outn "$DESKTOP_ACTION_UPDATE" "$SHORTNAME" >> "$F";
    outn "$DESKTOP_ACTION_VERIFY" "$SHORTNAME" >> "$F";
    outn "$DESKTOP_ACTION_REMOVE" "$SHORTNAME" >> "$F";

    # gather previous 'Actions' values from Desktop Entry section and prepend new action values
    action_value=`getSection "$F" "Desktop Entry" | grep '^Actions=' | awk 'BEGIN{FS="="}; { print $2 }' -`
    addSectionLine "$F" "Desktop Entry" "Actions=Apkg-Update;Apkg-Verify;Apkg-Remove;$action_value" "^Actions="
}


##
# installIcon <SOURCES>
# SOURCES: Icon files or directories to install.
# Returns: 0 on success, 1 on error.
#
# Install an icon to appropriate places. This function can be used in combination with installDesktop().
#
# See also: getIconDirs(), freedesktop.org Base Directory Specification and freedesktop.org Icon Theme Specification.
#
# Example:
# The application FooBar installs foobar.desktop, which contains this line for the icon:
#   Icon=foobar
# The application ships foobar.png and wants it to be used as a default icon for foobar.desktop, except if the current
# icon theme provides it's own icon for foobar. The following two commands will take care of everything:
#
# installIcon foobar.png
# installDesktop foobar.desktop
#
# Install a theme by defining a source directory:
# installIcon ./share/theme/hicolor
function installIcon() {
	pushOptE; set +e;
	local F D

	for F in "$@"; do

		# filename should be installed into the same base location as directory
		# but desktop implementations make that impossible at the present moment
		#
		# fixme: with XDG_* variables, directories and filenames should be
		# installed to the same base directory location
		if [ -f "$F" ]; then
			__outputStatus_status="file"
			outputStatus "$intl_INSTALLING_ICON"

			if [ ! "${_icondirs[@]}" ]; then
				declare -a _icondirs
				local oIFS="$IFS"
				IFS=$'\n'
				_icondirs=(`getIconDirs`)
				IFS="$oIFS"
			fi

			for D in "${_icondirs[@]}"; do
				copyFiles --silent "$@" "$D"
			done

		elif [ -d "$F" ]; then
			__outputStatus_status="directory"
			outputStatus "$intl_INSTALLING_ICON_THEME"

			# XDG Icon Theme Specification
			# XDG Base Directory Specification
			if [ ! "$icondir" ]; then
				if [[ `id -u` != "0" ]]; then
					if [[ "$XDG_DATA_HOME" != "" ]]; then
						icondir="$XDG_DATA_HOME/icons"
					else
					# Backwards Compatibility
						icondir="$HOME/.icons"
					fi
				else
					# $PREFIX should be /usr/local/share/ or /usr/share/ if
					# $XDG_DATA_DIRS is either not set or empty. Otherwise
					# $PREFIX should be one of the directories in $XDG_DATA_DIRS
					# for the users so the installation can be found.
					icondir="$PREFIX/share/icons"
				fi
			fi

			copyFiles --silent "$F" "$icondir"

		else

			err source \"$F\" was not found.
			popOptE
			return 1

		fi
	
	done

	popOptE
	return 0
}


# ##
# # installSound <SOURCES>
# # SOURCES: Sound files or directories to install.
# # Returns: 0 on success, 1 on error.
# #
# # Install files to proper desktop sound directory.
# #
# # See also: freedesktop.org Base Directory Specification and freedesktop.org Icon Theme Specification.
# #
# # Example:
# # installSound ./share/sounds/exit.ogg
# # #   --> <sound-dir>/$SHORTNAME/exit.ogg
# #
# # Install a theme by defining a source directory:
# # installSound ./share/sounds/theme-name
# # #   --> <sound-dir>/theme-name
# 
# # Premise is that sound will be similar to the handling of icons.
# # Implementation represented from XDG Icon Theme Specification   http://freedesktop.org/Standards/icon-theme-spec/
# # Implementation for XDG Base Directory Specification   http://freedesktop.org/Standards/basedir-spec/
# # function installSound() {
# 	pushOptE; set +e;
# 	local F
# 
# 	if [ ! "$sounddir" ]; then
# 		if [[ `id -u` != "0" ]]; then
# 			if [[ "$XDG_DATA_HOME" != "" ]]; then
# 				sounddir="$XDG_DATA_HOME/sounds"
# 			else
# 			# Backwards Compatibility
# 				sounddir="$HOME/.sounds"
# 			fi
# 		else
# 			# $PREFIX should be /usr/local/share/ or /usr/share/ if
# 			# $XDG_DATA_DIRS is either not set or empty. Otherwise
# 			# $PREFIX should be one of the directories in $XDG_DATA_DIRS
# 			# for the users so the installation can be found.
# 			sounddir="$PREFIX/share/sounds"
# 		fi
# 	fi
# 
# 	for F in "$@"; do
# 
# 		if [ -f "$F" ]; then
# 
# 			__outputStatus_status="file"
# 			outputStatus "$intl_INSTALLING_SOUND"
# 			copyFiles --silent "$F" "$sounddir/$SHORTNAME"
# 
# 		elif [ -d "$F" ]; then
# 
# 			__outputStatus_status="directory"
# 			outputStatus "$intl_INSTALLING_SOUND_THEME"
# 			copyFiles --silent "$F" "$sounddir"
# 
# 		else
# 
# 			err source \"$F\" was not found.
# 			popOptE
# 			return 1
# 
# 		fi
# 
# 	done
# 
# 	popOptE
# 	return 0
# }


##
# getIconDirs
# Outputs: a list of path, seperated by newlines.
#
# Get the paths in which icons are installed to.
#
# See also: installIcon()
function getIconDirs() {
	if [[ `id -u` == "0" ]]; then
		local gnome2prefix
		local kdeprefix

		if gnome2prefix=`getGnome2Prefix`; then
			echo "$gnome2prefix/share/pixmaps"
		fi
		if kdeprefix=`getKdePrefix`; then
			echo "$kdeprefix/share/icons"
		fi

		if [[ "$gnome2prefix" == "" && "$kdeprefix" == "" ]]; then
			echo "/usr/share/pixmaps"
		fi

	else
		echo "$HOME/.icons/gnome/48x48/apps"
		echo "$HOME/.kde/share/icons"
		if [[ "$XDG_DATA_HOME" != "" && "$XDG_DATA_HOME" != "$HOME/.icons/gnome/48x48/apps" && "$XDG_DATA_HOME" != "$HOME/.kde/share/icons" ]]; then
			echo "$XDG_DATA_HOME/share/icons"
		fi
	fi
	return 0
}


##
# getGnome2Prefix
# Outputs: A directory name, or nothing when GNOME 2 is not found.
# Returns: 0 when found, 1 when not found.
#
# Get the prefix where GNOME 2 is installed to.
function getGnome2Prefix() {
    pushOptE; set +e;
    local gnome2dir
    if locateCommand pkg-config --exists gnome-vfs-2.0; then
        # workaround because pkg-config does not give any error code on package check call or variable output call
        # "$lc_location" --variable=prefix gnome-vfs-2.0
        gnome2dir=`"$lc_location" --variable=prefix gnome-vfs-2.0`
        if [[ "$gnome2dir" != "" ]]; then
            echo "$gnome2dir"
            popOptE; return 0;
        fi
    fi
    if locateCommand gnome-panel; then
        gnome2dir=`dirname "$lc_location"`
        gnome2dir=`dirname "$gnome2dir"`
        echo "$gnome2dir"
        popOptE; return 0;
    else
        trace gnome2 not found
        popOptE; return 1;
    fi
}


##
# installGnome2AppEntry <FILENAMES>
# FILENAMES: The .applications files to install.
#
# Install GNOME 2 application entry files (.applications).
function installGnome2AppEntry() {
    pushOptE; set +e
    outputStatus "$intl_INSTALLING_GNOME2_APP_ENTRY"
    local dir
    if [[ `id -u` == "0" ]]; then
        if dir=`getGnome2Prefix`; then
            dir="$dir/share/application-registry"
        else
            trace gnome2 not found, returning
            popOptE; return 0
        fi
    else
        dir="$HOME/.gnome/application-info"
    fi
    trace dir=$dir
    copyFiles --silent "$@" "$dir"
    popOptE; return 0
}


##
# installGnome2Mime <FILENAMES>
# FILENAMES: The .mime or .keys files to install.
# Returns: 0 on success, 1 on error.
#
# Install GNOME 2 MIME entries (.mime/.keys).
function installGnome2Mime() {
    pushOptE; set +e
    outputStatus "$intl_INSTALLING_GNOME2_MIME"
    local dir
    if [[ `id -u` == "0" ]]; then
        if getGnome2Prefix > /dev/null; then
            dir="`getGnome2Prefix`/share/mime-info"
        else
            popOptE; return 0
        fi
    else
        dir="$HOME/.gnome/mime-info"
    fi
    copyFiles --silent "$@" "$dir"
    popOptE; return 0
}


##
# getKdePrefix
# Outputs: A directory name, or nothing when KDE 2 or 3 are not found.
# Returns: 0 when found, 1 when not found.
#
# Get the PREFIX where KDE 2 or 3 are installed to.
function getKdePrefix() {
    if locateCommand kicker; then
        dirname "`dirname \"$lc_location\"`"
        return 0
    else
        return 1
    fi
}


##
# installKdeMimeApp --no-adjust-path <DESKTOP-FILES...>
# --no-path-adjust: Do not change paths in the .desktop file to absolute paths.
# DESKTOP-FILES: a list of .desktop files.
#
# KDE uses .desktop files to associate applications with MIME types. This
# function installs that kind of .desktop files. No menu items will be created.
#
# By default, this function will change the paths in the TryExec, Exec
# fields to absolute filenames by prepending "$PREFIX/bin".
# For instance, "Exec=gimp" will be changed to "Exec=/home/myuser/.local/bin/gimp".<br>
#
# If you do not want installKdeMimeApp to change the paths, pass the --no-path-adjust argument.
function installKdeMimeApp() {
        pushOptE; set +e
	local adjust_path=true
	if [[ "$1" == "--no-adjust-path" ]]; then
		adjust_path=false
		shift
	fi

	outputStatus "$intl_INSTALLING_KDE_MIMEAPP"
	if [[ `id -u` == "0" ]]; then
		local kdedir
		if kdedir=`getKdePrefix`; then
			if [[ -d /usr/share/applnk-redhat ]]; then
				copyFiles --silent "$@" "$kdedir/share/applnk-redhat/.hidden" || return 1
				for F in "$@"; do
					F=`basename "$F"`
					_setupDesktopFile $adjust_path "$kdedir/share/applnk-redhat/.hidden/$F"
				done
				popOptE; return 0
			else
				copyFiles --silent "$@" "$kdedir/share/applnk/.hidden" || return 1
				for F in "$@"; do
					F=`basename "$F"`
					_setupDesktopFile $adjust_path "$kdedir/share/applnk/.hidden/$F"
				done
				popOptE; return 0
			fi
		else
                        warn no kde found
			popOptE; return 0
		fi
	else
		if [[ -d /usr/share/applnk-redhat ]]; then
			copyFiles --silent "$@" "$HOME/.kde/share/applnk-redhat/.hidden" || return 1
			for F in "$@"; do
				F=`basename "$F"`
				_setupDesktopFile $adjust_path "$HOME/.kde/share/applnk-redhat/.hidden/$F"
			done
			popOptE; return 0
		else
			copyFiles --silent "$@" "$HOME/.kde/share/applnk/.hidden"
			for F in "$@"; do
				F=`basename "$F"`
				_setupDesktopFile $adjust_path "$HOME/.kde/share/applnk/.hidden/$F"
			done
			popOptE; return 0
		fi
	fi
        
        popOptE;
        return 0
}


##
# installMime <FILENAMES>
# FILENAMES: xml Mime file(s) to install.
#
# Install mime-info xml files into appropriate directory.
#
# Implementation is represented from XDG Shared MIME Database Specification   http://freedesktop.org/bin/view/Standards/shared-mime-info-spec/
#
# Example:
# installMime share/mime/packages/foobar.xml
function installMime() {

    pushOptE; set +e;

    outputStatus "$intl_INSTALLING_MIME"
    trace called with $@

    # Initialize Base Desktop Directories
    if ! ${__installDesktop_initialized:-false}; then
        _installDesktopInitialize
    fi

    copyFiles --silent "$@" "$xdgdir_mime/packages"

    outputStatus "$intl_INSTALLING_UPDATING_MIME"
    if locateCommand update-mime-database; then
        "$lc_location" "$xdgdir_mime" &>/dev/null
    else
        warn "update-mime-database was not found so mime database was not updated."
    fi

    logCommand "[ ! -d `escapeFilename \"$xdgdir_mime/packages\"` ] && rm -rf `escapeFilename \"$xdgdir_mime\"` && removeDir `escapeFilename "$xdgdir_mime"`; update-mime-database `escapeFilename \"$xdgdir_mime\"` &>/dev/null || true;"
    trace done

    popOptE
    return 0

}


# -------------------------------------------------------------


##
# removeDir <DIRECTORY>
# DIRECTORY: The directory to remove.
# Returns: 0 always.
#
# Recursively check if DIRECTORY is empty, and remove it if it is.
# Checks for any directory in the DIRECTORYs path.
#
# See also: dirIsEmpty()
#
# Example:
# mkdir -p a/deep/directory
# touch a/file
# pruneDir a/deep/directory
# ls a/deep   # =>  "a/deep: no such file or directory"
# ls a/file   # =>  exists
function removeDir() {
    trace called with $@
    local location="$1"

    # find the first existing directory
    while [ ! -d "$location" ]; do
        trace does not exist $location
        location=`dirname "$location"`
        if [[ "$location" == "/" ]]; then
            return 0
        fi
    done

    pushd "$location" >/dev/null
    while dirIsEmpty . --no-recurse; do
        local a=`basename "$(pwd)"`
        cd ..
        rmdir "$a"
        trace removed `pwd`/$a
    done
    popd >/dev/null
    return 0
}


# called from an uninstall script to eliminate a linker cache entry
function removeLinkerCacheEntry() {
    local files=`echo "$1"/*.so "$1"/*.so.*`
    if [[ "$files" == "" ]]; then
        trace $1 empty, removing from ld.so.conf
        local entry=`escapeValue "$1"`
        safeSed /etc/ld.so.conf "/^$entry$/d"
    fi
}

# $1 = path name
function removeManPath() {
    if ! dirIsEmpty "$1"; then # we don't want to remove the manpath if other programs are using it
        return 1
    fi

    if [ -w /etc/man.config ]; then
      safeSed /etc/man.config "/^MANPATH `escapeValue \"$1\"`$/d"
    fi
    if [ -w /etc/manpath.config ]; then
      safeSed /etc/manpath.config "/^MANDATORY_MANPATH `escapeValue \"$1\"`$/d"
    fi

    return 0
}

function removeInfo() {
    # ARGH!!! install-info is junk, so manually edit the dir file to avoid things being screwed up
    # Check for Info directory and Info file location
    info_dir_location=/usr/info/dir
    [ -e /usr/share/info/dir ] && info_dir_location=/usr/share/info/dir
    f=`echo "$1" | sed 's/\.info.gz//'`
    # if root has not previously installed an info file from a package
    if [[ `id -u` == "0" ]]; then
        # this grep fails for a user because it does not exist yet :: FIXME
        # just protected from user with a id check above- C.
        [ -e "$info_dir_location" ] && l=`grep -n "$f" "$info_dir_location" | sed 's/:.*//'`
        if [[ "$l" != "" ]]; then
            # to avoid buffer conflicts we write to a variable then overwrite
            temp_buffer=`cat "$info_dir_location" | sed "${l}d"`
            echo "$temp_buffer" > "$info_dir_location"
        fi
    fi
}

# -------------------------------------------------------------

##
# _findGlibcVersions <DIRECTORY>
# DIRECTORY: The directory to scan.
# Outputs: A list of Glibc version symbols, separated by whitespace.
#
# Scan a directory recursively for binaries and generate a list
# of Glibc version symbols that the binaries require.
function _findGlibcVersions()
{
    # Check if files are ELF executables and output their
    # symbols
    function __scanFile()
    {
	while read; do
	    if LC_ALL=C file "$REPLY" | LC_ALL=C grep -q ELF; then
		objdump -p "$REPLY" | LC_ALL=C grep -E '0x[0-9]+.*GLIBC_' | sed 's/.*GLIBC_//'
	    fi
	done
    }

    function __addSymbol()
    {
	while read; do
	    echo -n "$REPLY "
	done
    }

    local symbols=$( find "$1" -perm -754 -type f | __scanFile | sort | uniq | __addSymbol )
    echo "$symbols" | sed 's/ $//'
    unset __scanFile
    unset __addSymbol
}

##
# _checkForGlibcVersions <VERSION-SYMBOL> [VERSION-SYMBOL...]
# VERSION-SYMBOL: An entry in the binaries verneed table, for instance GLIBC_2.3.1
# Outputs: Missing symbols, if any.
# Returns: non-zero if one or more of the given symbols are missing, zero if all are found.
#
# Checks the system C library for the given version symbols. The missing symbols, if any
# will be output to stdout.
function _checkForGlibcVersions() {
    dumpverdefs /lib/libc.so.6 $@
}

##
# getPythonLibDir
# Outputs: A directory path.
# Returns: 0 on success, non-zero on failure (for example, if Python is not installed).
#
# Outputs the directory where Python modules should be stored.
#
# Example:
# getPythonLibDir   # => /usr/lib/python2.2/site-packages
function getPythonLibDir() {
    python -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib()" 2>/dev/null
    return $?
}

##
# testForPythonModule
function testForPythonModule() {
    pushOptE; set +e;
    if locateCommand python; then
	trace python found, testing
        local r
	python -c "import $1" 2>/dev/null;
        r=$?
        popOptE; return $r
    else
	warn python not found, failing
	return 1
    fi
}

##
# logFile <FILENAME>
# FILENAME: filename that will be logged.
#
# Add FILENAME to the uninstall file log. This file will be
# automatically removed during uninstallation.
function logFile() {
	echo "$1" >> "$apkg_filelist"
	local logfile_dir=`dirname "$1"`
	logCommand --session "removeFile `escapeFilename \"$1\"`"
	logCommand --session "removeDir `escapeFilename \"$logfile_dir\"`"
}

##
# logDir <DIRECTORY>
# DIRECTORY: directory that will be logged.
#
# Add DIRECTORY to the uninstall directory log. This directory will be
# automatically removed during uninstallation, if it's empty.
function logDir() {
	echo "$1" >> "$apkg_uninstalldirs"
	logCommand --session "removeDir `escapeFilename \"$1\"`"
}

##
# logCommand --session <MESSAGE>
# --session: log only to session file.
# MESSAGE: string that will be logged.
#
# Add MESSAGE to the log. MESSAGE will be executed during uninstallation.
function logCommand() {

	# allow for temporary state to be captured
	# and not write to any other logging files
	if [ "$1" == "--session" ]; then
	    shift
	    echo "$@" >> "$_AUTOPACKAGE_SESSION"
	    return
	else
	    echo "$@" >> "$_AUTOPACKAGE_SESSION"
	    echo "$@" >> "$apkg_logfile"
	    return
	fi

}

##
# createBootstrapScript <BINARY> <FILENAME> [ADDITIONAL-COMMANDS]
# BINARY: the filename of an application.
# FILENAME: the filename to which the bootstrap script is written.
# Returns: 0 on success, 1 on error.
#
# Create a bootstrap script for BINARY. A bootstrap script is a script
# which sets up various environment variables (such as $PATH, $LD_LIBRARY_PATH, etc.),
# then run BINARY. That way it ensures BINARY can actually be run.
#
# If ADDITIONAL-COMMANDS is given, it will be included in the bootstrap script. The
# commands in this option will be run just before BINARY is started.
#
# FILENAME will be added to the uninstall log.
function createBootstrapScript() {
	local binary="$1"
	local filename="$2"
	local commands="$3"

	if [[ "$binary" == "" || "$filename" == "" ]]; then
		err "Usage: createBootstrapScript <BINARY> <FILENAME>"
		return 1
	fi

	local dir=`dirname "$filename"`
	if [[ ! -d "$dir" ]]; then
		#mkdirs "$dir" || return 1
		return 1
	fi

	cat <<EOF >"$filename"
#!/bin/bash
# Autopackage bootstrap script
# Name:    $SHORTNAME
# Package: $ROOTNAME
# Version: $SOFTWAREVERSION
# $DISPLAYNAME

export PATH="$PREFIX/bin:\$PATH"
export LD_LIBRARY_PATH="$PREFIX/lib:\$LD_LIBRARY_PATH"
export MANPATH="$PREFIX/share/man:\$MANPATH"
$commands

exec "$binary" "\$@"
EOF
	[[ "$?" != "0" ]] && return 1

	chmod +x "$filename"
	logFile "$filename"
	return $?
}


##
# installGConfSchema <FILENAMES>
# FILENAMES: Location of GCONF schema files.
#
# Installs given schema FILENAMES to the GConf database.
#
# Example:
# installGConfSchema "foobar.schemas"
function installGConfSchema() {

	pushOptE; set +e;
	local F

	outputStatus "$intl_INSTALLING_GCONF_SCHEMA"
	trace called with $@

	if [[ "$#" != "1" ]]; then
		err must have one argument: FILENAME
		return 1;
	fi

	# User:
	# GCONF_CONFIG_SOURCE= gconftool-2 --makefile-install-rule libgnomesu.schemas
	# Root:
	# GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source` gconftool-2 --makefile-install-rule libgnomesu.schemas

	if locateCommand gconftool-2 || locateCommand gconftool-1; then

		if [[ "$GCONF_CONFIG_SOURCE" == "" ]]; then
			export GCONF_CONFIG_SOURCE=`"$lc_location" --get-default-source`
		fi

		# copy file to Gconf base directory, install, and log
		etc_dir=`_userConfig`
		if [[ `id -u` == "0" ]]; then
			copyFiles --silent --nolog --nobackup "$@" "$etc_dir/gconf/schemas"
			for F in "$@"; do
				F=`basename "$F"`
				"$lc_location" --makefile-install-rule "$etc_dir/gconf/schemas/$F" > /dev/null
				logCommand "removeGConfSchema `escapeFilename \"$etc_dir/gconf/schemas/$F\"`"
			done

		# install-schema-file does not work and makefile-install-rule requires write access
		# to /etc. GConf does not seem to pick up user installed schemas. -C

		else

			# gconf goes not use XDG user config directory specification
			# probe for user defined gconf directory
			local user_schema_dir
			if [ -f "/etc/gconf/2/path" ]; then
				user_schema_dir="`cat /etc/gconf/2/path | grep readwrite | sed 's/xml:readwrite://; s|(|{|; s|)|}|'`/schemas"
			elif [ -f "/etc/gconf/1/path" ]; then
				user_schema_dir="`cat /etc/gconf/1/path | grep readwrite | sed 's/xml:readwrite://; s|(|{|; s|)|}|'`/schemas"
			fi
			user_schema_dir=`eval echo $user_schema_dir`
			trace located gconf user schema directory: $user_schema_dir
			if [[ "$user_schema_dir" != "" ]]; then
				copyFiles --silent --nolog --nobackup "$@" "$user_schema_dir"
				for F in "$@"; do
					F=`basename "$F"`
					"$lc_location" --install-schema-file="$user_schema_dir/$F" > /dev/null
					logCommand "removeGConfSchema `escapeFilename \"$user_schema_dir/$F\"`"
				done
			else
				warn user gconf schema directory was not determined and schema was not installed.
			fi

		fi

	fi

	popOptE
	return 0

}


##
# removeGConfSchema <FILENAME>
# FILENAME: Absolute location of GCONF schema file.
#
# Removes given schema FILENAME from the GConf database.
#
# Example:
# removeGConfSchema /home/user/.gconf/schemas/foobar.schema
function removeGConfSchema() {

	trace called with $1
	local schema="$1"

	if [ ! -f "$schema" ]; then
		warn GConf schema file does not exist: $schema
		return 1
	fi

	pushOptE; set +e;

	local basekey=`cat "$schema"  | grep -e '<key>.*</key>' | awk 'BEGIN { FS="<key>" } { print $2 }' \
		| awk 'BEGIN { FS="</key>" } { print $1 }' | awk 'BEGIN { FS="/" } { print "/" $3 "/" $4 }' | uniq`

	if [[ "$basekey" == "" ]]; then
		warn BASEKEY should not be empty
	fi

	trace basekey=$basekey

	if locateCommand gconftool-2 || locateCommand gconftool-1; then

		if [[ "$GCONF_CONFIG_SOURCE" == "" ]]; then
			export GCONF_CONFIG_SOURCE=`"$lc_location" --get-default-source`
		fi

		if [[ `id -u` == "0" ]]; then
			"$lc_location" --makefile-uninstall-rule "$schema" > /dev/null
		else
			# unset all keys relating to schema
			"$lc_location" --unapply-schema "$basekey"
		fi
		removeFile "$schema"
		removeDir `dirname "$schema"`

	fi

	popOptE
	return 0

}


# -------------------------------------------------------------

