#!/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