#!/bin/bash
# Last update: 16 May 2012
# by Emanuele Tomasi aka targzeta <targzeta@gmail.com>

# Copyright (C) 2010-2012 Emanuele Tomasi aka targzeta <targzeta@gmail.com>
# 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

# Esegue un'analisi dei pacchetti installati sul PC a partire dal file FILE_LIST
# di un repository Slackware

# -h per l'help

SCRIPT_NAME=${0##*/} # Nome dello script
SCRIPT_AUTHOR="targzeta <targzeta@gmail.com>" # Nome autore <email>

# Esce eliminando la directory temporanea
function _exit
{
    rm -r $TMP_DIR

    exit $1
}

# Stampa l'help dello script
function _help
{
    cat <<EOF
Use: ${SCRIPT_NAME} [-h|-n|-m|-s|-v] [root_directory]

For every package in ROOT_DIRECTORY/var/log/packages this script
searches a corresponding package in the file ./FILE_LIST and
prints out the name of the package on the standard output if it needs
an upgrade (they have different version).

OPTIONS:
   -h     print this help and exits
   -n     print the name of packages not installed
   -m     print the name of packages which are missing in ./FILE_LIST or
          filtered by ~/.analyze_SlackPkg file
   -s     print the name of packages with same version
   -v     show also current version of package

ROOT_DIRECTORY is / by default, or the value of ROOT enviroment variable,
or the value of root_directory passed in command line. If both variable ROOT
and root_directory are set, root_directory is used.

by ${SCRIPT_AUTHOR}
EOF
}

# Controlla l'esistenza di tutti i programmi dati i loro nomi come argomento.
# Se un programma non viene trovato, allora, stampa il nome del o dei programmi
# mancanti ed esce.  Si usa il programma which per controllare che esistono i
# programmi passati. Si assume che which sia in /bin/.
function _check_extern_program
{
    local error=0
    local string_error

    if [ ! -x /bin/which ]
    then
        error=1
        string_error="which : programma mancante.\n"
    else
        for progr in $@
        do
            if ! /bin/which $progr >& /dev/null
            then
                let ++error
                string_error=${string_error}"${progr} : programma mancante.\n"
            fi
        done
    fi

    if (( $error ))
    then
        if (( $error == 1 ))
        then
            string_error=${string_error}"\nIl programma mancante e' necessario "
        else
            string_error=${string_error}"\nI programmi mancanti sono necessari "
        fi
        string_error=${string_error}"per la corretta esecuzione dello script."
        _exit "$string_error" 1
    fi
}

# Restituisce il nome del software di un pacchetto Slackware
#
# $1: il nome di un pacchetto Slackware
function _get_software_name
{
    echo $1 | sed 's/-[^-]\+-[^-]\+-[^-]\+$//'
}

# Controllo degli argomenti dello script in cerca di opzioni
enable getopts echo exit

# Impostazione del separatore di campi al valore di default
IFS=$'\n\t '

# Controllo dell'esistenza di tutti i programmi esterni
# (quindi non comandi interni alla bash) usati
_check_extern_program bunzip2 grep mktemp rm sed tr

# Valori di default per le opzioni
FLAG_N=0   # Se = 1, stampa i pacchetti non installati
FLAG_M=0   # Se = 1, stampa i pacchetti che non sono trovati nel file FILE_LIST
           #         (es: pacchetti di terze parti)
FLAG_S=0   # Se = 1, stampa i pacchetti che non necessitano un aggiornamento
FLAG_V=0   # Se = 1, stampa la versione dei pacchetti installati

while getopts hnmsv OPT
do
    case $OPT in
        h)
            _help
            exit 0
            ;;
        n)
            FLAG_N=1
            ;;
        m)
            FLAG_M=1
            ;;
        s)
            FLAG_S=1
            ;;
        v)
            FLAG_V=1
            ;;
        ?)
            exit 3
            ;;
    esac
done

# Controllo sulle opzioni
if (( $FLAG_N + $FLAG_M + $FLAG_S + $FLAG_V > 1 ))
then
    echo "Only one option between -n, -m, -s and -v must be set"
    exit 4
fi

while (( $OPTIND != 1 ))
do
    shift
    let OPTIND--
done

# Controllo l'esistenza del file FILE_LIST
if [ ! -f "FILE_LIST" ]
then
    echo "FILE_LIST not found in current directory,"\
         "please enter in the directory where is located FILE_LIST"
    exit 2
fi

# Setto e controllo la root directory
root=$1
root=${root:-$ROOT}
root=${root%/}
root=${root}/var/log/packages/
if [ ! -d $root ]
then
    echo "$root not found, $1 is not the root directory"
    exit 5
fi

# Creo i file temporanei
TMP_DIR=$(mktemp -dt packages.XXXXXX)
PACKAGE_FROM_FILE_LIST=${TMP_DIR}/packages_from_file_list
PACKAGE_FROM_ROOT_DIR=${TMP_DIR}/packages_from_root_dir
# Elenco dei pacchetti in FILE_LIST
sed -n '/t.z$/{s/.*\(\.\/.*\)/\1/;p;}' FILE_LIST > $PACKAGE_FROM_FILE_LIST
# Elenco dei pacchetti installati
ls ${root} > $PACKAGE_FROM_ROOT_DIR

# Catturo il segnal SIGINT inviato dal ctrl-c
trap "_exit 3" SIGINT

# File per il filtraggio dei pacchetti
FILTER_FILE=~/.analyze_SlackPkg # File di filtro
FLAG_FILTER=0  # Indica se il file di filtro esiste
[ -f $FILTER_FILE ] && FLAG_FILTER=1

# Modalita' -n
if (( $FLAG_N == 1 ))
then
    for file in $(< $PACKAGE_FROM_FILE_LIST)
    do
        software_name=$(_get_software_name ${file##*/})
        if ! grep -q "^${software_name}-[^-]\+-[^-]\+-[^-]\+$" \
            $PACKAGE_FROM_ROOT_DIR
        then
            echo $file
        fi
    done

    _exit 0
fi

# Altre modalita'
for file in $(< $PACKAGE_FROM_ROOT_DIR)
do
    software_name=$(_get_software_name ${file})
    pack=""

    # Controllo se il pacchetto deve essere filtrato
    FILTERED=0
    if (( $FLAG_FILTER )) && grep -q "${software_name}$" $FILTER_FILE
    then
        FILTERED=1 # E' stato filtrato
    else
        pack=$(grep "/${software_name}-[^-]\+-[^-]\+-[^-]\+$" \
            $PACKAGE_FROM_FILE_LIST)
    fi

    if (( $FLAG_S == 1 ))
    then
        if [[ $pack != "" ]] && [[ ${pack##*/} == ${file}.t?z ]]
        then
            echo ${pack}
        fi
    elif (( $FLAG_M == 1 ))
    then
        if [[ $pack == "" ]] && (( $FILTERED == 0 ))
        then
            echo ${file}
        elif [[ $pack == "" ]] && (( $FILTERED == 1 ))
        then
            echo -e "${file##*/} \033[32;1mfiltered by" \
                "\033[33;1m${FILTER_FILE}\033[0m"
        fi
    else
        if [[ $pack != "" ]] && [[ ${pack##*/} != ${file}.t?z ]]
        then
            if (( $FLAG_V == 1 ))
            then
                echo -e "$pack \033[32;1mthe current version is" \
                    "\033[33;1m${file##*/}\033[0m"
            else
                echo "$pack"
            fi
        fi
    fi
done

_exit 0
