#!/bin/sh
# Copyright 1999  Patrick Volkerding, Moorhead, Minnesota, USA
# Copyright 2001, 2002, 2003  Slackware Linux, Inc., Concord, California, USA
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Modified to handle either old 8.3 or new package-version-arch-build.tgz
# packages, Sat Nov 17 14:25:58 PST 2001 volkerdi
#
# Rewritten to clean out _all_ old packages of a given basename, not just
# the first one found, Thu Apr  4 01:01:05 PST 2002 volkerdi
#
# Added --install-new and --reinstall, Fri May 31 14:11:14 PDT 2002 volkerdi
# Added --dry-run, Sat Apr 26 18:13:29 PDT 2003
#
# Wed May 18 20:20:29 EEST 2005, Lasse Collin <lasse.collin@slackware.fi>
# - Use special optimization options when calling installpkg.
# - Removed --no-paranoia to simplify calls to installpkg
# - Removed --verbose, no one uses it
# - Added short command line options aliases and also --warn == --dry-run
# - Check for corrupted packages before upgrading
# - Support FTP and HTTP URLs
# - Miscellaneous cleanups

# Load shared functions:
. /usr/share/pkgtools/shared_functions.sh
# Note that we do not disable pathname expansion or set IFS in upgradepkg. ;-)

usage() {
  cat << EOF

Usage: upgradepkg newpackage [newpackage2 ... ]
       upgradepkg oldpackage%newpackage [oldpackage2%newpackage2 ... ]

Options:
    -w, --warn          Show which packages would be installed or upgraded
                        but don't actually perform the upgrades.

    -i, --install-new   Install new packages in addition to upgrading
                        existing ones.

    -r, --reinstall     Upgrade even if the same version is already installed.

    -q, --quiet         Hide all messages except wget download information.

    -R, --root dir      Use a different root directory. This option overrides
                        the \$ROOT environment variable.

    -K, --keep-dotnew   Do not delete the list of *.new files. The list is
                        created in /var/log/setup/tmp/dotnew. This options is
                        probably useful only when called from other scripts.
                        Use only if you know what you are doing!

    -N, --no-ldconfig   Don't run ldconfig after installing the package. Use
                        only if you know what you are doing!

EOF
  exit 0
}

# Parse options:
[ $# = 0 ] && usage
ARGS=$(getopt -n upgradepkg -o riqwR:NKh \
  -l reinstall,install-new,quiet,warn,dry-run,root:,no-ldconfig,keep-dotnew,help -- "$@")
[ $? != 0 ] && exit 99
eval set -- "$ARGS"
unset ARGS REINSTALL INSTALL_NEW QUIET DRY_RUN NOLDCONFIG KEEP_DOTNEW
while : ; do
  case "$1" in
    -r|--reinstall)       # Reinstall packages even if the installed one
      REINSTALL="yes"     # is the same version.
      ;;
    -i|--install-new)     # Install packages that do not already have an installed
      INSTALL_NEW="yes"   # version. The usual default is to skip them.
      ;;
    -q|--quiet)           # Don't show any messages except errors and wget messages.
      QUIET="yes"
      ;;
    -w|--warn|--dry-run)  # Output a report about which packages would be installed
      DRY_RUN="yes"       # or upgraded but don't actually perform the upgrades.
      ;;
    -R|--root)                     # Install somewhere else than / filesystem.
      check_root_dir_exists "$2"   # --root overrides ROOT environment variable.
      ROOT=$2
      shift 1
      ;;
    -N|--no-ldconfig)     # Pass --no-ldconfig to installpkg (experts only)
      NOLDCONFIG="--no-ldconfig"
      ;;
    -K|--keep-dotnew)     # Do not delete nor show the list of new config
      KEEP_DOTNEW=yes     # files (*.new). The file is stored to $TMP/dotnew.
      ;;
    -h|--help)
      usage
      ;;
    --)                   # End of options
      shift 1
      break
      ;;
    *)
      exit_getopt_error
      ;;
  esac
  shift 1
done
[ $# = 0 ] && exit_no_packages_specified
check_is_run_by_root

initialize_variables_and_package_database
[ "$KEEP_DOTNEW" = "" -a -e "$TMP/dotnew" ] && rm -f "$TMP/dotnew"
ERRCODE=0
PKG_COUNT=0
PKG_COUNT_TOTAL=$#
NEW_KERNEL_INSTALLED=no

# Main processing loop:
while [ $# != 0 ]; do

PKG_COUNT=$(($PKG_COUNT + 1))

# Figure out the names of the old and new packages:
OLD=`echo "$1" | cut -f 1 -d '%'`
NEW=`echo "$1" | cut -f 2 -d '%'`
shift 1
INCOMINGDIR=$(dirname "$NEW")
TYPE=$(package_type "$NEW")
OLD=$(package_fullname "$OLD")
NEW=$(package_fullname "$NEW")

# Check and fix the old package name:
SHORT=`package_basename "$OLD"`
if [ ! -r "$ROOT/var/log/packages/$OLD" ]; then
  if ls "$ROOT/var/log/packages/$SHORT"* 1> /dev/null 2> /dev/null ; then
    for installed_package in "$ROOT/var/log/packages/$SHORT"* ; do
      if [ "`package_basename "$installed_package"`" = "$SHORT" ]; then # found one
        OLD=`basename "$installed_package"`
        break
      fi
    done
  fi
fi

# Test to see if both the old and new packages are where we expect them
# to be -- skip to the next package (or package pair) if anything's wrong:
if [ ! -r "$ROOT/var/log/packages/$OLD" ]; then
  if [ "$INSTALL_NEW" != "yes" ]; then
    if [ "$DRY_RUN" = "yes" ]; then
      echo "$OLD would not be upgraded (no installed package named $SHORT)."
    else
      if [ "$QUIET" != "yes" ]; then
        echo
        echo "Error:  there is no installed package named $OLD."
        echo "        (looking for $ROOT/var/log/packages/$OLD)"
        echo
      fi
    fi
    ERRCODE=1
  else # --install-new was given, so install the new package:
    if [ "$DRY_RUN" = "yes" ]; then
      echo "$NEW would be installed (new package)."
    else
      [ "$QUIET" != "yes" ] && cat << EOF

+==============================================================================
| $PKG_COUNT/$PKG_COUNT_TOTAL: Installing new package $NEW.$TYPE
+==============================================================================

EOF
      if [ "$QUIET" = "yes" ]; then
        installpkg --upgradepkg-install-new --quiet "$INCOMINGDIR/$NEW.$TYPE"
      else
        installpkg --upgradepkg-install-new "$INCOMINGDIR/$NEW.$TYPE"
      fi
      echo "$NEW" | is_kernel_packages && NEW_KERNEL_INSTALLED=yes
    fi
  fi
  continue
fi

# Check that the new package file exists. URLs are assumed exist always:
if ! is_url_package "$INCOMINGDIR/$NEW.$TYPE" && [ ! -r "$INCOMINGDIR/$NEW.$TYPE" ]; then
  if [ "$DRY_RUN" = "yes" ]; then
    echo "$NEW incoming package not found (command line)."
  else
    echo
    echo "Error: incoming package $INCOMINGDIR/$NEW.$TYPE not found."
    echo
  fi
  ERRCODE=1
  continue
fi

# Unless --reinstall was given, compare the package names
# and skip any exact matches:
if [ "$REINSTALL" != "yes" -a "$OLD" = "$NEW" ]; then
  if [ "$DRY_RUN" = "yes" ]; then
    echo "$NEW would be skipped (already installed)."
  else
    [ "$QUIET" != "yes" ] && cat << EOF

+==============================================================================
| $PKG_COUNT/$PKG_COUNT_TOTAL: Skipping package $NEW (already installed)
+==============================================================================

EOF
  fi
  continue
fi

SHORT=`package_basename "$OLD"`
if [ "$DRY_RUN" = "yes" ]; then
  echo -n "$NEW would upgrade: "
  for installed_package in "$ROOT/var/log/packages/$SHORT"* ; do
  if [ "`package_basename "$installed_package"`" = "$SHORT" ]; then
    echo -n "$(package_fullname "$installed_package")"
  fi
  done
  echo
  continue
fi

# Print a banner for the current upgrade:
[ "$QUIET" != "yes" ] && cat << EOF

+==============================================================================
| $PKG_COUNT/$PKG_COUNT_TOTAL: Upgrading $OLD using $NEW.$TYPE
+==============================================================================

EOF

# Test package integrity first before doing anything else:
if [ "$QUIET" != "yes" ]; then
  echo "Testing package integrity..."
  installpkg --upgradepkg-integrity "$INCOMINGDIR/$NEW.$TYPE"
else
  installpkg --quiet --upgradepkg-integrity "$INCOMINGDIR/$NEW.$TYPE"
fi
if [ $? != 0 ]; then
  echo
  echo "Error: package $INCOMINGDIR/$NEW.$TYPE is corrupt."
  echo
  continue
fi
echo "$NEW" | is_kernel_packages && NEW_KERNEL_INSTALLED=yes

# Showtime.  Let's do the upgrade.  First, we will rename all the
# installed packages with this basename to make them easy to remove later:
TIMESTAMP=`date +%Y-%m-%d,%T`
for installed_package in "$ROOT/var/log/packages/$SHORT"* ; do
  if [ "`package_basename "$installed_package"`" = "$SHORT" ]; then
    mv "$installed_package" "${installed_package}-upgraded-$TIMESTAMP"
  fi
done
for installed_script in "$ROOT/var/log/scripts/$SHORT"* ; do
  if [ "`package_basename "$installed_script"`" = "$SHORT" -a -r "$installed_script" ]; then
    mv "$installed_script" "${installed_script}-upgraded-$TIMESTAMP"
  fi
done

# Next, the new package is pre-installed:
[ "$QUIET" != "yes" ] && echo "Pre-installing..."
installpkg --quiet --upgradepkg-preinstall --no-ldconfig "$INCOMINGDIR/$NEW.$TYPE"
if [ $? != 0 ]; then
  echo
  echo "Fatal error. This should never happen. (Disk full?)"
  echo "Press enter to continue."
  echo
  read REPLY
  exit 1
fi

# Now, the leftovers from the old package(s) can go.  Pretty simple, huh? :)
( cd "$ROOT/var/log/packages"
  for rempkg in *-$TIMESTAMP ; do
    if [ "$QUIET" != "yes" ]; then
      removepkg --upgradepkg-quiet "$rempkg" # This is more verbose...
    else
      removepkg "$rempkg" > /dev/null        # ...than this.
    fi
  done
)

# Again!  Again!
# Seriously, the reinstalling of a package can be crucial if any files
# shift location, so we should always reinstall as the final step:
if [ "$QUIET" = "yes" ]; then
  installpkg $NOLDCONFIG --quiet --upgradepkg-finalize "$INCOMINGDIR/$NEW.$TYPE"
else
  echo "Finalizing package upgrade..."
  installpkg $NOLDCONFIG --upgradepkg-finalize "$INCOMINGDIR/$NEW.$TYPE"
fi

[ "$QUIET" != "yes" ] && echo -e "Package $OLD upgraded with new package\n$INCOMINGDIR/$NEW.$TYPE."
ERRCODE=0

done

if [ "$DRY_RUN" != "yes" -a "$QUIET" != "yes" ]; then
  echo
  # Show *.new files from the installed packages:
  if [ -f "$TMP/dotnew" ]; then
    echo "Remember to check the new configuration files:"
    cat "$TMP/dotnew"
    echo
  fi
fi
[ "$KEEP_DOTNEW" != "yes" -a -e "$TMP/dotnew" ] && rm -f "$TMP/dotnew"

if [ "$QUIET" != "yes" -a "$NEW_KERNEL_INSTALLED" = "yes" ]; then
  show_new_kernel_message
  echo
fi

exit $ERRCODE
