#!/bin/sh
#
# $Id: rmpkg,v 1.6 1999/03/25 18:26:41 volkerding Rel $
#
# Slackware remove package script
#
# $Log: rmpkg,v $
# Revision 1.6  1999/03/25 18:26:41 volkerding
# - Use external $ROOT variable, like installpkg.
#
# Revision 1.5.1  1998/03/18 15:37:28 volkerding
# - Since removepkg is always run by root, the temp directory has been
#   moved from /tmp to a private directory to avoid symlink attacks from
#   malicious users.
#
# Revision 1.5  1997/06/26 12:09:53  franke
# - Fixed old bug in TRIGGER regex setting
# - -preserve/-copy options now preserve non-unique files
#   and empty directories also
#
# Revision 1.4  1997/06/09 13:21:36  franke
# - Package file preserve (-preserve, -copy) added.
# - Don't execute "rm -rf" lines from doinst.sh, removing links explicit.
# - Warning on no longer existing files added.
# - Warning on files changed after package installation added.
# - Intermediate file preserve (-keep) added.
# - Check for required files/links now done on a combined list.
# - Write access to /var/log/{packages,scripts} no longer necessary for -warn.
#
# Revision 1.3  1997/06/08 13:03:05  franke
# Merged with revision 1.1.1.1
#
# Revision 1.2  1996/06/01 20:04:26  franke
# Delete empty directories & formated manual pages added
#
# Revision 1.1.1.1  1995/12/18 21:20:42  volkerding
# Original Version from Slackware 3.1
#
# Revision 1.1  1995/06/05 22:49:11  volkerding
# Original Version from Slackware 3.0
#

# Copyright 1994, 1995, 1998  Patrick Volkerding, Moorhead, Minnesota 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.
#

# Make sure there's a proper temp directory:
TMP=/var/log/setup/tmp
# If the $TMP directory doesn't exist, create it:
if [ ! -d $TMP ]; then
  rm -rf $TMP # make sure it's not a symlink or something stupid
  mkdir $TMP
  chmod 700 $TMP # no need to leave it open
fi
ADM_DIR=$ROOT/var/log
PRES_DIR=$TMP/preserved_packages

cat_except() {
 ( cd "$1" && cat `ls * | sed "/^$2\$/d"` )
}

extract_links() {
 sed -n 's,^( *cd \([^ ;][^ ;]*\) *; *rm -rf \([^ )][^ )]*\) *) *$,\1/\2,p'
}

preserve_file() {
 if [ "$PRESERVE" = "true" ]; then
  F="`basename "$1"`"
  D="`dirname "$1"`"
  if [ ! -d "$PRES_DIR/$PKGNAME/$D" ]; then
    mkdir -p "$PRES_DIR/$PKGNAME/$D" || return 1
  fi
  cp -p "$ROOT/$D/$F" "$PRES_DIR/$PKGNAME/$D" || return 1
 fi
 return 0
}

preserve_dir() {
 if [ "$PRESERVE" = "true" ]; then
  if [ ! -d "$PRES_DIR/$PKGNAME/$1" ]; then
    mkdir -p "$PRES_DIR/$PKGNAME/$1" || return 1
  fi
 fi
 return 0
}

keep_files() {
 while read FILE ; do
  if [ ! -d "$ROOT/$FILE" ]; then
   if [ -r "$ROOT/$FILE" ]; then
    echo "  --> $ROOT/$FILE was found in another package. Skipping."
    preserve_file "$FILE"
   else
    if [ "$FILE" != "install/" -a "$FILE" != "install/doinst.sh" ]; then
     echo "WARNING: Nonexistent $ROOT/$FILE was found in another package. Skipping."
    fi
   fi
  else
   preserve_dir "$FILE"
  fi
 done
}

keep_links() {
 while read LINK ; do
  if [ -L "$ROOT/$LINK" ]; then
   echo "  --> $ROOT/$LINK (symlink) was found in another package. Skipping."
  else
   echo "WARNING: Nonexistent $ROOT/$LINK (symlink) was found in another package. Skipping."
  fi
 done
}

delete_files() {
 while read FILE ; do
  if [ ! -d "$ROOT/$FILE" ]; then
   if [ -r "$ROOT/$FILE" ]; then
    if [ "$ROOT/$FILE" -nt "$ADM_DIR/packages/$PKGNAME" ]; then
     echo "WARNING: $ROOT/$FILE changed after package installation."
    fi
    if [ ! "$WARN" = "true" ]; then
     echo "  --> Deleting $ROOT/$FILE"
     preserve_file "$FILE" && rm -f "$ROOT/$FILE"
    else
     echo "  --> $ROOT/$FILE would be deleted"
     preserve_file "$FILE"
    fi
   else
    echo "  --> $ROOT/$FILE does no longer exist. Skipping."
   fi
  else
   preserve_dir "$FILE"
  fi
 done
}

delete_links() {
 while read LINK ; do
  if [ -L "$ROOT/$LINK" ]; then
   if [ ! "$WARN" = "true" ]; then
    echo "  --> Deleting symlink $ROOT/$LINK"
    rm -f $ROOT/$LINK
   else
    echo "  --> $ROOT/$LINK (symlink) would be deleted"
   fi
  else
   echo "  --> $ROOT/$LINK (symlink) does no longer exist. Skipping."
  fi
 done
}

delete_dirs() {
 sort -r | \
 while read DIR ; do
  if [ -d "$ROOT/$DIR" ]; then
    if [ ! "$WARN" = "true" ]; then
      if [ `ls -a "$ROOT/$DIR" | wc -l` -eq 2 ]; then
        echo "  --> Deleting empty directory $ROOT/$DIR"
        rmdir $ROOT/$DIR
      else
        echo "WARNING: Unique directory $ROOT/$DIR contains new files"
      fi
    else
     echo "  --> $ROOT/$DIR (dir) would be deleted if empty"
    fi
  fi
 done
}

delete_cats() {
 sed -n 's,/man\(./[^/]*$\),/cat\1,p'  | \
 while read FILE ; do
   if [ -f "$ROOT/$FILE" ]; then
     if [ ! "$WARN" = "true" ]; then
       echo "  --> Deleting $ROOT/$FILE (fmt man page)"
       rm -f $ROOT/$FILE
     else
       echo "  --> $ROOT/$FILE (fmt man page) would be deleted"
     fi
   fi
 done
}

# Conversion to 'comm' utility by Mark Wisdom.
# is pretty nifty! :^)
remove_packages() {
 for package_name in $* 
 do
  PKGNAME=$package_name
  if [ ! -r $PKGNAME ]; then
    PKGNAME=`basename $PKGNAME`
  fi
  echo
  if [ -r $ADM_DIR/packages/$PKGNAME ]; then
   if [ ! "$WARN" = true ]; then
    echo "Removing package $ADM_DIR/packages/$PKGNAME..."
   fi
   if fgrep "./" $ADM_DIR/packages/$PKGNAME 1> /dev/null 2>&1; then
    TRIGGER="^\.\/"
   else
    TRIGGER="FILE LIST:"
   fi
   if [ ! "$WARN" = true ]; then
    echo "Removing files:"
   fi
   sed -n "/$TRIGGER/,/^$/p" < $ADM_DIR/packages/$PKGNAME | \
    fgrep -v "FILE LIST:" | sort -u > $TMP/delete_list
   # Pat's new-new && improved pre-removal routine.
   cat_except $ADM_DIR/packages $PKGNAME | sort -u > $TMP/required_list
   if [ -r $ADM_DIR/scripts/$PKGNAME ]; then
    extract_links < $ADM_DIR/scripts/$PKGNAME | sort -u > $TMP/del_link_list
    cat_except $ADM_DIR/scripts $PKGNAME | extract_links | \
     sort -u > $TMP/required_links
    mv $TMP/required_list $TMP/required_files
    sort -u $TMP/required_links $TMP/required_files >$TMP/required_list
    comm -12 $TMP/del_link_list $TMP/required_list | keep_links
    comm -23 $TMP/del_link_list $TMP/required_list | delete_links
   fi
   comm -12 $TMP/delete_list $TMP/required_list | keep_files
   comm -23 $TMP/delete_list $TMP/required_list > $TMP/uniq_list
   delete_files < $TMP/uniq_list
   delete_dirs < $TMP/uniq_list
   delete_cats < $TMP/uniq_list
   if [ ! "$KEEP" = "true" ]; then
    rm -f $TMP/delete_list $TMP/required_files $TMP/uniq_list
    rm -f $TMP/del_link_list $TMP/required_links $TMP/required_list
   fi
   if [ "$PRESERVE" = "true" ]; then
    if [ -r $ADM_DIR/scripts/$PKGNAME ]; then
     if [ ! -d "$PRES_DIR/$PKGNAME/install" ]; then
      mkdir -p "$PRES_DIR/$PKGNAME/install"
     fi
     cp -p $ADM_DIR/scripts/$PKGNAME $PRES_DIR/$PKGNAME/install/doinst.sh
    fi
   fi
   if [ ! "$WARN" = "true" ]; then
    for DIR in $ADM_DIR/removed_packages $ADM_DIR/removed_scripts ; do
     if [ ! -d $DIR ] ; then mkdir -p $DIR ; chmod 755 $DIR ; fi
    done
    mv $ADM_DIR/packages/$PKGNAME $ADM_DIR/removed_packages
    if [ -r $ADM_DIR/scripts/$PKGNAME ]; then
     mv $ADM_DIR/scripts/$PKGNAME $ADM_DIR/removed_scripts
    fi
   fi
  else
   echo "No such package: $ADM_DIR/packages/$PKGNAME. Can't remove."
  fi
 done
}

while : ; do
 case "$1" in
  -copy) WARN=true; PRESERVE=true; shift;;
  -keep) KEEP=true; shift;;
  -preserve) PRESERVE=true; shift;;
  -warn) WARN=true; shift;;
  -*) echo "Usage: `basename $0` [-copy] [-keep] [-preserve] [-warn] packagename ..."; exit 1;;
  *) break
 esac
done

if [ "$WARN" = "true" ]; then
 echo "Only warning... not actually removing any files."
 if [ "$PRESERVE" = "true" ]; then
  echo "Package contents is copied to $PRES_DIR."
 fi
 echo "Here's what would be removed (and left behind) if you"
 echo "removed the package(s):"
 echo
else
 if [ "$PRESERVE" = "true" ]; then
  echo "Package contents is copied to $PRES_DIR."
 fi
fi

remove_packages $*

