#!/bin/bash
# Copyright (C) 2007-2010 Matias A. Fonzo, Santiago del Estero, AR
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# Localización % Idioma:
TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=remove
VERSION=2.2
# Funciones #
# Una función para mostrar mensajes normales:
msg() { local LC_ALL ; printf '%s\n' "$@"; }
# Una función para mensajes de advertencia:
warn() { local LC_ALL; printf '%b\n' "$@" >&2; }
usage() {
msg $"Removes one or more packages installed in your system." \
"" \
$"Usage: remove [options] package_name.tlz ..." \
"" \
$"Options:" \
$" -h, --help Show this help and exit." \
$" -v, --version Show the version of the program." \
$" -k, --keep Keep temporary files on the" \
$" temporary directory (debugging)." \
$" -q, --quiet Supress output messages." \
$" -w, --warn Warn about of the files and" \
$" directories that will be removed." \
""
}
version() {
msg "remove $VERSION" \
"Copyright (C) 2007-2010 Matias A. Fonzo ." \
"License GPLv3+: GNU GPL version 3 or later:" \
"" \
"This is free software: you are free to change and redistribute it." \
"There is NO WARRANTY, to the extent permitted by law."
}
# Opciones:
while (( $# )); do
case "$1" in
-k|--keep)
OPT=KEEP
shift
;;
-q|--quiet)
OPT=QUIET
shift
;;
-w|--warn)
OPT=WARN
shift
;;
-[h?]|--help)
usage
exit 0
;;
-[vV]|--version)
version
exit 0
;;
-*)
warn $"${0##*/}: Invalid option: $1"
exit 1
;;
*)
break;
esac
done
# Si no hay argumentos, llama a la función de ayuda:
(( $# == 0 )) && { usage ; exit 0; }
# Comprobamos la variable de entorno ROOT:
if [[ -n $ROOT && ! -d $ROOT ]]; then
warn $"${0##*/}: ROOT=${ROOT}: Invalid directory"
exit 1;
fi
umask 022
# Base de datos por defecto:
DB="${ROOT}/var/db"
# Nos aseguramos de que la parte de la base de datos exista:
for directory in \
pkg/removed \
pkg/removed/pre-post \
pkg/removed/post-install \
pkg/removed/description
do
if [[ ! -d ${DB}/$directory ]]; then
mkdir -p "${DB}/$directory"
fi
done
# Creación de un directorio temporal seguro:
TMP="${DB}/tmp"
if [[ ! -d $TMP ]]; then
mkdir -p -m 700 "$TMP"
fi
# Crea los archivos temporales de forma segura:
S=$RANDOM$$
# Hace que algunos comandos corran más rápido:
export LC_ALL=C
# Más Funciones #
# Una función para reflejar el nombre base:
_basename() { local name ; name=${1##*/} ; printf "${name%$2}"; }
cat_except() {
( cd "$1"
for file in * ; do
if [[ -f $file ]]; then
arr+=("$file")
if (( ${#arr[*]} > 1 ))
then TWO=yes ; break;
fi
fi
done
if [[ $TWO ]]; then
find . -maxdepth 1 -type f ! -name "$2" -exec cat '{}' +
fi
)
}
move_install() {
local dirName fileName
dirName="$1"
fileName="$2"
if [[ -r ${DB}/pkg/${dirName}/$fileName ]]; then
( cd ${DB}/pkg/$dirName
install -m 0644 $fileName ${DB}/pkg/removed/$dirName && \
rm -f $fileName
)
fi
}
empty_dir() {
shopt -s nullglob dotglob
files=( $@ )
if (( ${#files[*]} == 0 )); then
shopt -u nullglob dotglob
return 0;
else
shopt -u nullglob dotglob
return 1;
fi
}
keep_lnk() {
while read link ; do
if [[ -L ${ROOT}/$link ]]; then
if [[ $OPT != WARN ]]; then
msg $"${ROOT}/${link}: Symlink found in another package. Dodging."
else
msg $"LINK -> ${ROOT}/$link"
fi
else
warn $"WARNING: ${ROOT}/${link}: Symlink non-existent, found in another package. Dodging."
fi
done
}
keep_file() {
while read file ; do
[[ -f ${ROOT}/$file ]] || continue;
if [[ -r ${ROOT}/$file ]]; then
if [[ $OPT != WARN ]]; then
msg $"${ROOT}/${file}: File found in another package. Dodging."
else
if [[ ${ROOT}/$file -nt ${DB}/pkg/$pkgname ]]; then
warn $"WARNING: ${ROOT}/${file}: Changed after package installation."
fi
msg $"FILE -> ${ROOT}/$file"
fi
else
warn $"WARNING: ${ROOT}/${file}: File non-existent, found in another package. Dodging."
fi
done
}
extract_lnk() { awk '$2 == "Link:" { print $3 }' | cut -f 1 -d ' '; }
delete_lnk() {
while read link ; do
if [[ -L ${ROOT}/$link ]]; then
if [[ $OPT != WARN ]]; then
if [[ $OPT != QUIET ]]; then
rm -v -rf ${ROOT}/$link
else
rm -rf ${ROOT}/$link
fi
else
msg $"LINK -> ${ROOT}/$link"
fi
else
msg $"${ROOT}/${link}: Symlink no longer exists. Dodging."
fi
done
}
delete_file() {
while read file ; do
[[ -d ${ROOT}/$file ]] && continue;
if [[ -r ${ROOT}/$file || -L ${ROOT}/$file ]]; then
if [[ $OPT != WARN ]]; then
if [[ $OPT != QUIET ]]; then
rm -v -rf ${ROOT}/$file
else
rm -rf ${ROOT}/$file
fi
else
if [[ ${ROOT}/$file -nt ${DB}/pkg/$pkgname ]]; then
warn $"WARNING: ${ROOT}/${file}: Changed after package installation."
fi
msg $"FILE -> ${ROOT}/$file"
fi
else
msg $"${ROOT}/${file}: No longer exists. Dodging."
fi
done
}
delete_dir() {
sort -r | while read dir ; do
[[ -d ${ROOT}/$dir ]] || continue;
if [[ $OPT != WARN ]]; then
if empty_dir ${ROOT}/${dir}* ; then
if [[ $OPT != QUIET ]]; then
rmdir -v ${ROOT}/$dir
else
rmdir ${ROOT}/$dir
fi
else
warn $"rmdir: ${ROOT}/${dir}: Directory not empty."
fi
else
msg $"DIR -> ${ROOT}/$dir"
fi
done
}
# Loop:
for package in "$@" ; do
pkgname=$(_basename $package .tlz)
# Simple package_name notación.
#
# Esto comprueba si está disponible el paquete en la
# base de datos, caso contrario, lo intenta buscar
# por su nombre-largo buscando una coincidencia:
if [[ ! -f ${DB}/pkg/$pkgname ]]; then
if ls ${DB}/pkg/${pkgname}* > /dev/null 2>&1; then
for long_package in ${DB}/pkg/${pkgname}* ; do
name=${long_package##*/}
if printf "$name" | grep -qwo "$pkgname" ; then
pkgname=$name
fi
done
fi
fi
# Si el paquete está disponible, manos a la obra:
if [[ -f ${DB}/pkg/$pkgname ]]; then
# Imprime un mensaje según la opción:
if [[ $OPT != WARN ]]; then
msg "" $"< Removing ${DB}/pkg/$pkgname ..." ""
else
msg "" "% Scanning ${DB}/pkg/$pkgname ..." ""
fi
# Hora del show !:
if [[ -r ${DB}/pkg/post-install/$pkgname ]]; then
cat_except ${DB}/pkg/post-install $pkgname | extract_lnk | sort -u > ${TMP}/linklist$S
extract_lnk < ${DB}/pkg/post-install/$pkgname | sort -u > ${TMP}/deletelnk$S
( cd $TMP
sort deletelnk$S linklist$S | uniq -d | keep_lnk
sort deletelnk$S linklist$S linklist$S | uniq -u | delete_lnk
)
fi
cat_except ${DB}/pkg $pkgname | sort -u > ${TMP}/filelist$S
sort ${DB}/pkg/$pkgname ${TMP}/filelist$S | uniq -d | keep_file
sort ${DB}/pkg/$pkgname ${TMP}/filelist$S ${TMP}/filelist$S | \
uniq -u > $TMP/uniq$S
delete_file < ${TMP}/uniq$S
delete_dir < ${TMP}/uniq$S
# Si la opción KEEP no está activada, borramos los archivos temporales:
if [[ $OPT != KEEP ]]; then
( cd $TMP
rm -f linklist$S deletelnk$S filelist$S uniq$S
)
fi
# Si la opción WARN no está activada, movemos los
# archivos de pre/post instalación y descripción:
if [[ $OPT != WARN ]]; then
# Archivo de pre-post instalación:
move_install "pre-post" "$pkgname"
# Archivo de post-instalación:
move_install "post-install" "$pkgname"
# Archivos de descripción:
if [[ -d ${DB}/pkg/description ]]; then
( cd ${DB}/pkg/description
for file in ${pkgname}-?? ; do
if [[ -f $file ]]; then
mv $file ${DB}/pkg/removed/description
fi
done
)
fi
# Mueve el registro del paquete al directorio de removidos:
mv ${DB}/pkg/$pkgname ${DB}/pkg/removed
fi
else
warn "${DB}/pkg/${pkgname}: No such package"
CODE=1
continue;
fi
done
exit $CODE