#!/bin/bash

############                                                          ##########
# apt-diag #                                                          # v.0.2b #
################################################################################
# author: (C)2009 nofun_AT_laposte_DOT_net ## bug report, suggestion, wishlist #
####################################################### are welcome ############
# GNU General Public License v.3 or later http://www.gnu.org/copyleft/gpl.html #
################################################################################
######## homepage: http://debian-facile.org/forum/viewtopic.php?id=1285 ########
################################################################################

# Changelog:

# v.0.1 (avril 2009):
# version initiale.

# v.0.2 (mai 2009):
# correction d'un bug dans la mise a jour du cache (f_updatecache): le cache mis
# a jour avec la version precedente (0.1) a une forte probabilite de corruption.
# fonctionnalite: ajout de la presentation des derniers paquets ajoutes et
# supprimes depuis la derniere mise a jour du cache.

# v.0.2a (mai 2009):
# correction d'un bug dans la mise a jour du cache (f_updatecache): le cache mis
# a jour avec la version precedente (0.2) a une probabilite de corruption.

# v.0.2b (juin 2009):
# correction d'un bug dans la mise a jour du cache (f_updatecache): les paquets
# deja installes mais nouveaux dans une source sont maintenant ajoutes au cache
# de cette source. Cette version corrige correctement le cache mis a jour avec
# la version precedente (0.2a).

################################################################################

# apt-diag: pour que claires les sources demeurent

# apt-diag cherche a donner une vue d'ensemble des paquets installes sur un
# systeme utilisant apt (Advanced Packaging Tool du projet Debian).
# Il aide en particulier a determiner de quelle source peut provenir un paquet,
# indique quand c'est le cas la presence d'un meme paquet dans de multiples
# sources et dresse la liste des paquets n'appartenant plus a aucune source
# (ensemble des paquets obsoletes et des paquets de source inconnue).
# C'est un bon complement a 'apt-cache policy nom_du_paquet'.

# De plus, apt-diag maintient un cache personnel des paquets par source, ce qui
# lui permet d'etablir la liste des paquets obsoletes en provenance de chaque
# source, information sinon indisponible. Les sources supprimees sont egalement
# archivees. Le degre d'information que fournit apt-diag est donc dependant de
# la date d'initialisation du cache, ce qui veut dire que les meilleurs
# resultats seront obtenus avec une initialisation du cache suivant directement
# l'installation du systeme.

# apt-diag n'agit pas sur le systeme de gestion des paquets, il est donc su^r
# pour le systeme hote. Le seul appel au systeme de gestion des paquets est du
# type 'dpkg -l' ou 'dpkg --get-selections'. De ce fait, apt-diag peut analyser
# un systeme autre que le systeme hote si on lui fournit les repertoires
# /var/lib/apt/lists/ et /var/lib/dpkg/ d'un syteme alternatif (a copier dans
# ~/.apt-diag/_alternate) et fonctionne parfaitement en simple utilisateur.
# apt-diag possede une fonctionnalite d'enregistrement des analyses dans un
# fichier, ce qui permet de conserver une trace ou de communiquer l'etat du
# systeme a un tiers.

# Limitations:
# tout diagnostic est faillible et sujet a des erreurs d'interpretation. Il
# appartient a l'administrateur d'evaluer les informations fournies par apt-diag
# en particulier en confrontant les resultats obtenus avec ceux que pourront lui
# procurer les outils systeme a sa disposition. La licence GNU GPL sous laquelle
# apt-diag est delivre n'offre aucune garantie de bon fonctionnement.
# apt-diag n'a pas (encore) ete teste en presence de downgrade ou d'apt-pinning
# et plus generalement demande a etre teste davantage.
# apt-diag n'est pas integre au systeme de gestion des paquets, de ce fait la
# mise a jour du cache n'est pas automatique lors de la mise a jour du systeme.
# apt-diag evalue les paquets par leur nom, l'evaluation par numero de version
# est embryonnaire et demanderait d'autres techniques de programmation. De ceci
# decoule que:
# - si un paquet a ete installe depuis une source par la suite supprimee et que
# ce paquet apparait toujours dans une autre source avec une version inferieure,
# rien de particulier n'est signale au sujet de ce paquet par apt-diag.
# - si lors de l'initialisation du cache un paquet installe est obsolete mais
# encore present avec une version inferieure dans une autre source, il n'est pas
# analyse comme obsolete.

# Merci de distribuer apt-diag avec le texte ci-dessus.
# Bonne utilisation.

################################################################################
PROGNAME=$(basename $0)

f_conv()
{
    #TestCharSet: aéèêîïà¿»×x
    grep -m 1 "TestCharSet:" $0 > /tmp/testcharset
    PRGCHARSET=`file -bi /tmp/testcharset|mawk -F "charset=" '{print $2}'|tr [a-z] [A-Z]`
    SYSCHARSET=$(locale charmap)
    ! echo $SYSCHARSET|grep -q $PRGCHARSET && echo -e "\n [>] Conversion de $PROGNAME de $PRGCHARSET vers $SYSCHARSET...\c"\
    && { iconv -f "$PRGCHARSET" -t "$SYSCHARSET"//TRANSLIT $0 -o /tmp/$PROGNAME && chmod +x /tmp/$PROGNAME\
    && grep -m 1 "TestCharSet:" /tmp/$PROGNAME > /tmp/testcharset && PRGCHARSETR=`file -bi /tmp/testcharset|mawk -F "charset=" '{print $2}'`\
    && { [ "$PRGCHARSET" = "$(echo $PRGCHARSETR|tr [a-z] [A-Z])" ] && echo " ok" || echo; } && rm -f /tmp/testcharset\
    && echo -e " [*] la version $PRGCHARSETR de $PROGNAME est disponible dans /tmp/$PROGNAME\n" && exit\
    || { echo -e "\a\n [!] un probleme est survenu pendant la conversion, sortie\n"; exit 1; }; }
}
[ -d ~/.$PROGNAME ] || { f_conv; [ -d /var/lib/apt/lists/ ] && [[ $(dpkg -l 2>/dev/null) ]]\
|| { echo -e "\a\n [!] $PROGNAME n'est pas compatible avec le système hôte, sortie\n"; exit 1; }; }

which bc &>/dev/null|| { echo -e "\a\n [>] $PROGNAME\n [!] Ce script nécessite bc pour fonctionner, veuillez l'installer:\n"
aptitude show bc; echo -e "\n [*] aptitude -(s)v install bc\n"; exit 1; }

[ "$1" = "-c" ] && ECHO=echo || CLEAR=clear ; hash grep mawk
grep -q "Ratio $(uname -n):" ~/.$PROGNAME/ratio 2&>/dev/null && RATIO=`grep "Ratio $(uname -n):" ~/.$PROGNAME/ratio|cut -d " " -f3`
################################################################################

f_clearline()
{
    [[ $1 = [br] ]] && [ -n "$3" ] && echo $2$3|grep -qx [0-9]* || return 1
    while true
    do
	local COUNTER; let COUNTER++ ; echo -ne "\\$1"
	[ $COUNTER = $2 ] && unset -v COUNTER && break
    done
    while true
    do
	let COUNTER++ ; echo -ne " "
	[ $COUNTER = $3 ] && unset -v COUNTER && break
    done
    while true
    do
	let COUNTER++ ; [ $1 = r ] && COUNTER=$3
	echo -ne "\\$1" ; [ $COUNTER = $3 ] && return
    done
}

f_exit() { read -s -n 1 -p " [¿] Quitter $PROGNAME ? <O/n>: " EXIT; [ "$EXIT" = n ] && f_clearline r 1 45 && return; echo; echo; exit; }

f_init()
{
    for (( s=1; s <= TSOURCES; s++ )); do unset -v RESULTF$s OBSOLETES$s; done
    unset -v DEL INEWS ISUPR NOPURGEP NOSOURCEDP OBSOLETES OBSOLETEST OBSOLETESTC OBSOLETESTR
    unset -v RESULTF RESULTFT SINGLE SOURCEDP SOURCESG SOURCESGC SOURCESN SOURCESNG TSOURCES TWIN TWINP
    echo
    CACHEDIR=~/.$PROGNAME/$MACHINE/cache ; RECFILE=~/$PROGNAME\_$MACHINE\-$(date +%Y%m%d-%H%M%S).txt
    [ -d $CACHEDIR ] || {  mkdir -p $CACHEDIR/.old/sources/.deleted/ $CACHEDIR/sources/
    echo "$MACHINE $(date "+%F %T") initialisation du cache" >> ~/.$PROGNAME/$MACHINE/history.txt; }
    [ -d ~/.$PROGNAME/_alternate/lists/ -a -d ~/.$PROGNAME/_alternate/dpkg/ ]\
    && mv -f ~/.$PROGNAME/_alternate/lists/ ~/.$PROGNAME/_alternate/dpkg/ ~/.$PROGNAME/$MACHINE/
    [ "$MACHINE" = "$(uname -n)" ] && SOURCESDIR=/var/lib/apt/lists/ INSTALLDIR=/var/lib/dpkg/\
    || SOURCESDIR=~/.$PROGNAME/$MACHINE/lists/ INSTALLDIR=~/.$PROGNAME/$MACHINE/dpkg/

    ## extraction de la liste des paquets installes sur le systeme
    INSTALLED=$(dpkg --admindir=$INSTALLDIR --get-selections |grep -w "install$" |mawk '{print $1}' |sort -d)
    NINSTALLED=$(echo $INSTALLED | wc -w)
    ## extraction et nettoyage des sources
    SOURCES=$(ls $SOURCESDIR |grep -vx lock |grep -vx partial)
    for a in _source_Sources i18n_Translation .ed$
    do
	SOURCES=$(printf '%b\n' $SOURCES | grep -v $a)
    done
    for s in $SOURCES
    do
	grep -qm 1 "^Package: " $SOURCESDIR$s || continue
	SOURCESC="$SOURCESC $s" ; let TSOURCES++
    done
    SOURCES="$SOURCESC"; unset -v SOURCESC
    echo " [»] Paquets installés: $INSTALLDIR Paquets disponibles: $SOURCESDIR"
    echo " [»] $NINSTALLED paquets installés sur $MACHINE"; echo
}

f_progress()
{
    let PROGRESS++
    [ $PROGRESS = $ALPHAPROGRESS ] && echo -ne "\b-" && return
    [ $PROGRESS = $ALPHAPROGRESS2 ] && echo -ne "\b\\" && return
    [ $PROGRESS = $ALPHAPROGRESS3 ] && echo -ne "\b|" && return
    [ $PROGRESS = $ALPHAPROGRESS4 ] && echo -ne "\b/" && PROGRESS=0
}
f_adv()
{
    trap 'break' SIGUSR1
    while true
    do
	echo -ne "\b\b\b\b.   "; sleep .5; echo -ne "\b\b\b\b..  "; sleep .5
	echo -ne "\b\b\b\b... "; sleep .5; echo -ne "\b\b\b\b    "; sleep .5
    done
}

f_benchmachine()
{
    local ALPHAPROGRESS ALPHAPROGRESS2 ALPHAPROGRESS3 ALPHAPROGRESS4 BURN COUNTER PROGRESS SAMPLE SAMPLEb TEMPS TEMPS1 TEMPS2
    ALPHAPROGRESS=60 SAMPLE="sourceinstalled" # ! la taille du mot est importante ! ne pas modifier ! SAMPLE="sourceinstalled"
    ALPHAPROGRESS2=$(($ALPHAPROGRESS*2)) ALPHAPROGRESS3=$(($ALPHAPROGRESS*3)) ALPHAPROGRESS4=$(($ALPHAPROGRESS*4))
    f_burn()
    {
	CLOCKD=$(date +%s%N)
	while true
	do
	    let COUNTER++; f_progress
	    while true
	    do
	        let COUNTERb++; f_progress
	        SAMPLEb="$SAMPLEb $(echo "$SAMPLE ")"
	        [ $COUNTERb = 100 ] && break
	    done
	    unset -v COUNTERb
	    echo $SAMPLEb|grep $SAMPLE|wc -w &>/dev/null
	    [ $COUNTER = 10 ] && break
	done
	CLOCKF=$(date +%s%N)
	let BURN++
    }
    [ "$BURN" = "" ] && echo -n " [>] Benchmarking machine...  " && f_burn
    [ "$BURN" = "1" ] && unset -v COUNTER SAMPLEb && f_burn
    [ "$BURN" = "2" ] && TEMPS1=$(($CLOCKF-$CLOCKD)) && unset -v COUNTER SAMPLEb && f_burn
    TEMPS2=$(($CLOCKF-$CLOCKD)) TEMPS=$(($TEMPS1+$TEMPS2))
    RATIO=$(echo "scale=20;$TEMPS/2/$COUNTER/240000000"|bc|mawk '{printf "%0.1f\n",$1}')
    ## ($RATIO=1 est obtenu sur mon portable Core2Duo T7250 2GHz 2Go RAM, $RATIO>1 sur une machine plus lente)
    ## le resultat peut etre faux sur un portable non branche ou si une autre application prend de la ressource
    echo "Ratio $(uname -n): $RATIO" > ~/.$PROGNAME/ratio
    f_clearline r 1 35
}

f_benchmark()
{
    [ -z "$RATIO" ] && f_benchmachine; echo -n " [>] Benchmarking...  "; local RESULTF PROGRESS COUNTER
    ## ALPHACOUNTER: determine un nombre de boucles proportionnellement inverse au nombre de paquets dans la source pondere par le ratio machine
    ## destine a adapter le temps de bench en prenant en compte $NPA et la vitesse de la machine
    PREALPHACOUNT=`echo "scale=20;1/$NPA*150000*1/(1/$NPA*(300+$NPA/2))*1.8"|bc`
    ALPHACOUNTER=`echo "scale=20;$PREALPHACOUNT/$RATIO"|bc|mawk '{printf "%0.0f\n",$1}'`
    [ `echo "scale=0; $ALPHACOUNTER<2"|bc` -eq 1 ] && ALPHACOUNTER=2
    CLOCKD=$(date +%s%N)
    for i in $INSTALLED
    do
	let COUNTER++; f_progress
	RESULTF="$RESULTF $(echo "$PRERESULT"|grep -xm 1 $i)"
	[ $COUNTER -eq $ALPHACOUNTER ] && break
    done
    CLOCKF=$(date +%s%N)
    TEMPS=$(($CLOCKF-$CLOCKD)) BENCHMASS=$(($NPA*$COUNTER)) BENCH=$(echo "scale=10;$TEMPS/$BENCHMASS"|bc)
    f_clearline r 1 30
}

f_estimate()
{
    ESTIMATE=$(echo "scale=10;$BENCH*$COMPUTEMASS"|bc)
    ESTIMATEHS=$(echo "scale=0;$ESTIMATE/1000000000"|bc)
    ESTIMATEHSBC=$(echo "scale=3;$ESTIMATE/1000000000"|bc)
    mn=0 ss=$ESTIMATEHS ms=$(echo "$ESTIMATEHSBC-$ss"|bc|cut -d '.' -f2)
    [ $ESTIMATEHS -gt 59 ] && mn=$(($ESTIMATEHS/60)) && ss=$(($ss-$mn*60))
    [ $ss -lt 10 ] && ss="0$ss"
    ESTIMATE=$(echo ""$mn"m"$ss"s"$ms"ms")
}

f_mkcache()
{
    trap 'jobs -p f_adv &>/dev/null && kill $(jobs -p f_adv); echo -e \
    "\a\n$PROGNAME > WARNING! The disk cache might be left in an inconsistent state!"; f_exit' 2 3 9 15
    ## determine s'il y a des nouvelles sources a traiter:
    for s in $SOURCES; do [ -f $CACHEDIR/sources/$s ] && continue; SOURCESNEWS="$SOURCESNEWS $s"; let TSOURCESM++; done
    ## determine si des sources ont ete desactivees, dans ce cas les archive:
    ## NB: les depots designes par les noms de code des versions (ex. lenny) seront archives au changement de version,
    ## ceux utilisant la terminologie stable, testing, unstable,... seront traites par f_updatecache
    for s in $(ls $CACHEDIR/sources/)
    do
	printf '%b\n' $SOURCES|grep -qxm 1 $s &>/dev/null || { mv -f $CACHEDIR/sources/$s $CACHEDIR/.old/sources/.deleted/$s; DEL="$DEL $s"; }
    done
    [ -n "$DEL" ] && echo && for d in $DEL; do echo " [>] Archivage de la source supprimée $d";\
    echo "$MACHINE $(date "+%F %T") source supprimée $d archivée" >> ~/.$PROGNAME/$MACHINE/history.txt; done
    [ -z "$TSOURCESM" -a -n "$DEL" ] && { unset -v DEL; echo; echo >> ~/.$PROGNAME/$MACHINE/history.txt
    read -s -n 1 -p " [-] valider pour continuer... "; f_clearline r 1 35; $CLEAR; return; }; [ -z "$TSOURCESM" ] && return
    echo
    echo " [>] Création du cache initial de paquets installés pour les nouvelles sources:"; echo
    time\
    for a in $SOURCESNEWS
    do
	## supprime la source du repertoire .deleted si elle a ete reactivee
	[ -f $CACHEDIR/.old/sources/.deleted/$a ] && rm -f $CACHEDIR/.old/sources/.deleted/$a
	## ecrit dans $CACHEDIR/sources/$a les noms des paquets installes qui ont une occurrence dans $SOURCESDIR$a
	local TIMEFORMAT=" »»»  réelle: %lR" PROGRESS CSOURCES; let CSOURCES++
	echo " [»] Traitement de la source ($CSOURCES/$TSOURCESM) $a"
	PRERESULT=$(grep "^Package: " $SOURCESDIR$a | mawk '{print $2}') NPA=$(echo $PRERESULT | wc -w) COMPUTEMASS=$(($NPA*$NINSTALLED))
	echo " [»] $NPA paquets dans la source, $NINSTALLED paquets installés sur $MACHINE"
	[ -z "$RATIO" ] && f_benchmachine
	### ALPHAPROGRESS: alteration proportionnelle de l'indicateur de progresssion. Utilise le nombre inverse de $NPA, pondere par $RATIO
	PREALPHAPROG=`echo "scale=20;1/$NPA*(200+$NPA)*10"|bc`
	[ `echo "scale=20; $PREALPHAPROG<=11"|bc` -eq 1 ]\
	&& ALPHAPROGRESS=`echo "scale=20;(($PREALPHAPROG-10)*10+$PREALPHAPROG-10)/$RATIO"|bc|mawk '{printf "%0.0f\n",$1}'`\
	|| ALPHAPROGRESS=`echo "scale=20;$PREALPHAPROG/$RATIO"|bc|mawk '{printf "%0.0f\n",$1}'`
	[ `echo "scale=0; $ALPHAPROGRESS<1"|bc` -eq 1 ] && ALPHAPROGRESS=1
	[ `echo "scale=0; $ALPHAPROGRESS>20"|bc` -eq 1 ] &&\
	ALPHAPROGRESS=`echo "scale=20;20+(-1/$ALPHAPROGRESS+$ALPHAPROGRESS)/$NPA/1*$NPA/10"|bc|mawk '{printf "%0.0f\n",$1}'`
	[ `echo "scale=0; $ALPHAPROGRESS>30"|bc` -eq 1 ] && ALPHAPROGRESS=30
	ALPHAPROGRESS2=$(($ALPHAPROGRESS*2)) ALPHAPROGRESS3=$(($ALPHAPROGRESS*3)) ALPHAPROGRESS4=$(($ALPHAPROGRESS*4))
	f_benchmark
	f_estimate
	[ "$ESTIMATEHS" -lt 120 ] && echo -n " [»] durée estimée de traitement: $ESTIMATE  "\
	|| echo -n " [»] durée estimée de traitement: $ESTIMATE (début $(date +%T))  "
	time\
	for WORKAROUND in 1
	do
	    for b in $INSTALLED
	    do
		f_progress
		RESULTF="$RESULTF $(echo "$PRERESULT"|grep -xm 1 $b)"
	    done
	    f_clearline b 1 1
	done
	printf '%b\n' $RESULTF|sort -d > $CACHEDIR/sources/$a
	echo "$MACHINE $(date "+%F %T") nouvelle source $a ajoutée" >>~/.$PROGNAME/$MACHINE/history.txt
	RESULTFC=$(echo $RESULTF | wc -w)
	[ $RESULTFC -ne 0 ] && echo " [*] $RESULTFC occurrences" || echo " [×] pas d'occurrence"
	echo ; unset -v PROGRESS RESULTF
	[ $TSOURCESM = $CSOURCES ] && local TIMEFORMAT=" [»] durée totale de traitement: %lR" && unset -v DEL SOURCESNEWS TSOURCESM
    done
    echo >>~/.$PROGNAME/$MACHINE/history.txt; echo; echo; read -s -n 1 -p " [-] valider pour continuer... "; f_clearline r 1 35; $CLEAR
}

f_updatecache()
{
    mv -f $CACHEDIR/Installed $CACHEDIR/.old/ 2>/dev/null; printf '%b\n' $INSTALLED > $CACHEDIR/Installed
    [ -f $CACHEDIR/.old/Installed ] || { echo; return; }
    echo -e " [>] Mise à jour du cache    \c"; f_adv &
    ICHGE=$(echo $(diff -y --suppress-common-lines $CACHEDIR/.old/Installed $CACHEDIR/Installed))
    echo $ICHGE|grep -q "<" && ICHGE=`echo $ICHGE|mawk '{gsub("<","");print $0}'`
    echo $ICHGE|grep -q ">" && ICHGE=`echo $ICHGE|mawk '{gsub(">","");print $0}'`
    echo $ICHGE|grep -q "|" && ICHGE=`echo $ICHGE|mawk '{gsub("\|","");print $0}'`
    for i in $ICHGE
    do
	grep -qx $i $CACHEDIR/Installed && INEWS="$INEWS $i" || ISUPR="$ISUPR $i"
    done
    [ -n "$INEWS" ] && printf '%b\n' $INEWS > $CACHEDIR/Linews; [ -n "$ISUPR" ] && printf '%b\n' $ISUPR > $CACHEDIR/Lisupr
    f_updt()
    {
	mv -f $CACHEDIR/sources/* $CACHEDIR/.old/sources/ 2>/dev/null
	for s in $(ls $CACHEDIR/.old/sources/)
	do
	    ## supprime du cache les occurrences desinstallees
	    for r in $ISUPR
	    do
		grep -vx "$r" $CACHEDIR/.old/sources/$s > $CACHEDIR/sources/$s; cp -f $CACHEDIR/sources/$s $CACHEDIR/.old/sources/$s
	    done
	    PRERESULT=$(grep "^Package: " $SOURCESDIR$s | mawk '{print $2}')
	    cp -f $CACHEDIR/.old/sources/$s $CACHEDIR/sources/$s
	    ## ajoute au cache les nouvelles occurrences installees si elles apparaissent dans la source sauf si elles y sont deja
	    for n in $INEWS
	    do
		grep -qxm 1 "$n" $CACHEDIR/sources/$s &>/dev/null && continue
		echo "$PRERESULT"|grep -xm 1 "$n" >> $CACHEDIR/sources/$s
	    done
	    printf '%b\n' $(echo $(sort -d $CACHEDIR/sources/$s)) > $CACHEDIR/sources/$s
	done
    }
    [ -z "$ISUPR" -a -z "$INEWS" ] || f_updt
    ## ajoute au cache les occurrences anciennement installees mais nouvelles dans la source (par exemple nouvellement presentes dans security)
    for s in $(ls $CACHEDIR/sources/)
    do
	PRERESULT=$(grep "^Package: " $SOURCESDIR$s | mawk '{print $2}')
	SOCC=$(printf '%b\n' $PRERESULT $INSTALLED|sort -d|uniq -d)
	NEWSOCC=$(printf '%b\n' $SOCC $(cat $CACHEDIR/sources/$s)|sort -d|uniq -u)
	[ -n "$NEWSOCC" ] && {
	for o in $NEWSOCC
	do
	    grep -qxm 1 "$o" $CACHEDIR/sources/$s &>/dev/null && continue
	    echo "$o" >> $CACHEDIR/sources/$s
	done
	printf '%b\n' $(echo $(sort -d $CACHEDIR/sources/$s)) > $CACHEDIR/sources/$s; }
    done
    rm -f $CACHEDIR/.old/sources/* 2>/dev/null; kill -s SIGUSR1 $(jobs -p f_adv); f_clearline r 1 35
    ## il est a noter que les paquets obsoletes (plus presents dans les sources) sont conserves dans le cache et analysables comme obsoletes
}

f_selectsources()
{
    unset -v RESUME TSOURCES
    ## formatage des sources pour la presentation, comptage des sources, presentation
    for a in $SOURCES
    do
	[ "$(ls -sh $SOURCESDIR$a | mawk '{print $1}')" = "0" ]\
	&& SOURCESP="$SOURCESP $(ls -sh $SOURCESDIR$a | mawk '{print $1"B"}') $a"\
	|| SOURCESP="$SOURCESP $(ls -sh $SOURCESDIR$a | mawk '{print $1}') $a"
	let TSOURCES++
    done
    echo " [>] $TSOURCES sources de paquets actuellement sélectionnées pour analyse:"
    echo ; unset -v TSOURCES
    printf ' [%b]\t» %b\n' $SOURCESP
    echo
    read -p " [¿] Sélectionner ou éliminer (préfixe «-») un motif pour filtrer, valider pour terminer: " MOTIF
    ## si le MOTIF est vide, SOURCESS n'est pas initialisee par la boucle d'inclusion. On l'initialise a SOURCESS=$SOURCES
    [ "$MOTIF" = "" ] && SOURCESS=$SOURCES
    until [ "$MOTIF" = "" ]
    do
	MOTIFS="$MOTIFS $MOTIF"
	read -p " [¿] continuer la sélection des sources à analyser, valider pour terminer: " MOTIF
    done
    ## s'il n'y a que des eliminations, SOURCESS n'est pas initialisee par la boucle d'inclusion. On l'initialise a SOURCESS=$SOURCES
    for m in $MOTIFS ; do MOTIFC="$(echo $m | cut -c 1) $MOTIFC" ; done
    MCM=$(printf '%b\n' $MOTIFC | grep -c "-") MC=$( printf '%b\n' $MOTIFC | grep -c "")
    [ "$MCM" = "$MC" ] && SOURCESS=$SOURCES
    ## boucle d'inclusion. La boucle interne permet de ne pas inclure une source deja presente (doublon)
    for MOTIF in $MOTIFS
    do
	MOTIFC=$(echo $MOTIF | cut -c 1)
	[ "$MOTIFC" != "-" ] && SELECT=$(printf '%b\n' $SOURCES | grep $MOTIF) || continue
	for s in $SELECT
	do
	    [[ $(printf '%b\n' "$SOURCESS" | grep -w "$s") ]] && continue
	    SOURCESS="$SOURCESS $s"
	done
    done
    ## boucle d'exclusion
    for MOTIF in $MOTIFS
    do
	MOTIFC=$(echo $MOTIF | cut -c 1)
	[ "$MOTIFC" = "-" ] && MOTIF=$(echo $MOTIF | cut -c 2-) && SOURCESS=$(printf '%b\n' $SOURCESS | grep -v $MOTIF)
    done
    SOURCES=$SOURCESS
    echo
    ## exclusion des fichiers qui ne sont pas des descripteurs de paquets
    echo " [>] Analyse des sources:"
    [ "$SOURCES" != "" ] && NSOURCES=$(printf '%b\n' $SOURCES | grep -c "") || NSOURCES=0
    for a in $SOURCES
    do
	if ! grep -qm 1 "^Package: " $SOURCESDIR$a
	then
	    [ "$(ls -sh $SOURCESDIR$a | mawk '{print $1}')" = "0" ]\
	    && b=$(ls -sh $SOURCESDIR$a | mawk '{print $1"B"}')\
	    || b=$(ls -sh $SOURCESDIR$a | mawk '{print $1}')
	    echo " [×] non pertinente: $a [$b]"
	    continue
	fi
	SOURCESC="$SOURCESC $a" ; let TSOURCES++
    done
    SOURCES="$SOURCESC"; unset -v MOTIFC MOTIFS SOURCESC SOURCESP SOURCESS
    [ "$SOURCES" = "" ] && TSOURCES=0
    [ "$TSOURCES" != "0" ]\
    && SOURCES=$(printf '%b\n' $SOURCES|sort -d) SOURCES=$(echo $SOURCES)\
    && echo " [»] $TSOURCES sources sur $NSOURCES à traiter dans la sélection:" && printf ' [*] [%b]\n' $SOURCES\
    || echo " [»] pas de sources à traiter dans la sélection"
    echo
    while true; do let COUNT++; echo ${FUNCNAME[$COUNT]}|grep -qx main && MAIN=$COUNT && unset -v COUNT && break; done
    RESUME=* ; echo " [-] Aide - Accueil - Quitter <h/w/q>"
    while true
    do
	case "$RESUME" in
	    q|Q) f_exit; RESUME=*;;
	    h) 	less $0; RESUME=*;;
	    w) read -s -n 1 -p " [¿] Retourner à l'accueil ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 40 || { $ECHO; break $MAIN; };;
	    p) 	[ "$TSOURCES" != "0" ] && $CLEAR && echo && f_selectsources || RESUME=r;;
	    l|L|"") [ "$TSOURCES" != "0" ] && break || [ "$RESUME" = "" ] && RESUME=r || RESUME=*;;
	    r|R) $CLEAR; f_init; f_selectsources;;
	    *) 	[ "$TSOURCES" != "0" ]\
		&& read -s -n 1 -p " [¿] Préciser encore la sélection - Recommencer - Lancer l'analyse <p/r/L>: " RESUME\
		&& f_clearline r 1 104
		[ "$TSOURCES" = "0" ] && read -s -n 1 -p " [¿] Recommencer <R>: " RESUME && f_clearline r 1 50;;
	esac
    done
}

f_occm()
{
    while true
    do
	read -s -n 1 -p " [¿] Par la suite traiter aussi les sources sans occurrences ? <o/N>: " SELECT
	f_clearline r 1 75
	case "$SELECT" in
	    q|Q) f_exit;;
	    h) 	less $0;;
	    w) read -s -n 1 -p " [¿] Retourner à l'accueil ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 40 || { $ECHO; break 2; };;
	    o|O)  OCCLESS=0; unset -v SELECT; $CLEAR; echo; break;;
	    n|N|"") OCCLESS=1; unset -v SELECT; $CLEAR; echo; break;;
	esac
    done
}

f_squeezecache()
{
    for s in $SOURCES
    do
	local CSOURCES CSOURCESM CSOURCESS ; let CSOURCES++ ; let CSOURCESS++
	RESULTF=$(cat $CACHEDIR/sources/$s) RESULTFC=$(echo $RESULTF | wc -w)
	## eval interprete l'item suivant dans la liste comme une commande s'il y a un espace
	RESULTF=`echo $RESULTF|mawk '{gsub(" ","¿");print $0}'`
	## attribution d'une variable dynamique au resultat individuel - utilisation de eval
	## identification par le n° de traitement $CSOURCES, attribution du contenu de RESULTF a la nouvelle variable
	## la nouvelle variable recoit le nom de la source en alias, permet la concordance n°/nom
	[ $RESULTFC -ne 0 ] && eval RESULTF$CSOURCES="$RESULTF" && eval alias RESULTF$CSOURCES=$s\
	&& echo -e " [*] $RESULTFC occurrences\t$s"
	[ $RESULTFC -eq 0 -a $OCCLESS -eq 0 ] && eval RESULTF$CSOURCES="$RESULTF" && eval alias RESULTF$CSOURCES=$s\
	&& echo -e " [×] pas d'occurrence\t$s" && let CSOURCESM++
	[ $RESULTFC -eq 0 -a $OCCLESS -eq 1 ] && let CSOURCES-- && echo -e " [×] pas d'occurrence\t$s"
	unset -v RESULTF
    done
    echo ; TSOURCES=$CSOURCES ; [ -z $CSOURCESM ] && CSOURCESM=0
    echo " [»] $(($CSOURCES - $CSOURCESM)) sources sur $CSOURCESS présentent des occurrences de paquets installés sur $MACHINE"
    echo ; unset -v CSOURCESM CSOURCESS CSOURCES
    ## concatenation des resultats individuels - utilisation de la substitution de variable {!RESULTF}
    for (( n=1; n <= TSOURCES; n++ ))
    do
	RESULTF=RESULTF$n ## RESULTF se substitue a la variable dynamique dont le contenu est appele par {!RESULTF}
	RESULTFP=`echo ${!RESULTF}|mawk '{gsub("¿"," ");print $0}'` ; RESULTFP=$(echo $RESULTFP)
	RESULTFT="$RESULTFT $RESULTFP" ## concatenation, doublons compris elimines par sort -u par la suite
	SOURCESN="$SOURCESN $n" ## extrait les numeros des sources disponibles de 1 a n sources
    done
    SOURCESN=$(echo $SOURCESN) SOURCESNP=`echo $SOURCESN|mawk '{gsub(" ","\&|");print $0}'`
    RESULTFTS=$(printf '%b\n' $RESULTFT | sort -d) RESULTFTU=$(echo $(printf '%b\n' $RESULTFT | sort -u))
    RESULTFTC=$(echo $RESULTFT|wc -w) RESULTFTUC=$(echo $RESULTFTU|wc -w) RESULTFTCC=$(($RESULTFTC-$RESULTFTUC))
    UNKNOWNS=$(printf '%b\n' $INSTALLED $RESULTFTU|sort -d|uniq -u) UNKNOWNC=$(echo $UNKNOWNS|wc -w)
    NOPURGE=`dpkg --admindir=$INSTALLDIR -l|grep "^rc "|mawk '{print $2}'` NOPURGEC=$(echo $NOPURGE|wc -w)
    LINEWSC=$(cat $CACHEDIR/Linews 2>/dev/null|wc -w); LISUPRC=$(cat $CACHEDIR/Lisupr 2>/dev/null|wc -w)
    LINEWS=$(printf ' [%b]' $(cat $CACHEDIR/Linews 2>/dev/null)) LISUPR=$(printf ' [%b]' $(cat $CACHEDIR/Lisupr 2>/dev/null))
    echo
    echo " [*] paquets installés sur $MACHINE: $NINSTALLED"
    echo " [*] occurrences installées sur $MACHINE: $RESULTFTUC"
    echo " [*] paquets installés non analysés: $(($NINSTALLED-$RESULTFTUC))"
    echo " [*] croisements d'occurrences: $RESULTFTCC"
    echo " [*] occurrences totales: $RESULTFTC"
    echo
}

f_postproc()
{
    f_fmt() { fmt -w $(tput cols); }
    f_tee() { [ $RECORD = 1 ] && tee -a $RECFILE || tee; }
    f_less() { [ $LESS = 1 ] && less -m~ || tee; }
    f_pager()
    {
	read -s -n 1 -p " [¿] afficher les occurrences avec Less - directement - ne pas afficher <L/d/n>: " PAGER
	f_clearline r 1 80 ; [ "$PAGER" = "d" -o "$PAGER" = "n" ] || PAGER=L ; PAGERM="\c"
	[ "$PAGER" = L ] && LESS=1 PAGERM="      [affichage dans le pager]" || LESS=0; [ "$PAGER" = n ] && PAGERM="      -------------------------"
    }

    f_dores()
    {
	for S in $S
	do
	    RESULTF=RESULTF$S RESULTFALIAS=$(eval alias RESULTF$S | cut -d "'" -f2) ; [ "$1" = "-a" ] && return
	    RESULTF=`echo ${!RESULTF}|mawk '{gsub("¿"," ");print $0}'` ; RESULTF=$(echo $RESULTF)
	    RESULTFC=$(echo $RESULTF|wc -w) ; [ "$1" = "-r" ] && return
	    OBSOLETES=OBSOLETES$S; OBSOLETES=`echo ${!OBSOLETES}|mawk '{gsub("¿"," ");print $0}'`
	    OBSOLETES=$(echo $OBSOLETES); OBSOLETESC=$(echo $OBSOLETES|wc -w)
	done
    }
    f_obs()
    {
	[ -n "$OBSOLETESTC" ] && return
	echo -n " [»] traitement    "; trap 'kill $(jobs -p f_adv); echo; exit' 2 3 9 15; f_adv &
	for S in $SOURCESN
	do
	    f_dores -r; unset -v OBSOLETES
	    for p in $RESULTF
	    do
		grep -qxm 1 "Package: $p" $SOURCESDIR$RESULTFALIAS\
		|| OBSOLETES="$OBSOLETES¿$p" OBSOLETEST="$OBSOLETEST $p"
	    done
	    eval OBSOLETES$S="$OBSOLETES"
	done
	OBSOLETEST=$(printf '%b\n' $OBSOLETEST|sort -u) OBSOLETESTC=$(echo $OBSOLETEST|wc -w)
	for o in $OBSOLETEST
	do
	    unset -v COUNTER NOBS
	    for S in $SOURCESN
	    do
		f_dores -r
		let COUNTER++; [ $COUNTER -eq 1 ]\
		&& LOCI=$(grep -xnm 1 "Package: $o" $INSTALLDIR/status|cut -d: -f1) LOCIF=$(($LOCI+15))\
		&& VERI=`mawk 'NR==LOCI,NR==LOCIF {print $0}' LOCI=$LOCI LOCIF=$LOCIF $INSTALLDIR/status|grep -m 1 "^Version: "`\
		&& OBSOLETESTR="$OBSOLETESTR $o"
		LOC=$(grep -xnm 1 "Package: $o" $SOURCESDIR$RESULTFALIAS); [ -z "$LOC" ] && continue
		LOC=$(echo $LOC|cut -d: -f1) LOCF=$(($LOC+15))
		VER=`mawk 'NR==LOC,NR==LOCF {print $0}' LOC=$LOC LOCF=$LOCF $SOURCESDIR$RESULTFALIAS|grep -m 1 "^Version: "`
		[ "$VERI" = "$VER" ] && NOBS=1
	    done
	    [ -n "$NOBS" ] && OBSOLETESTR=$(printf '%b\n' $OBSOLETESTR|grep -vx $o)
	done
	OBSOLETESTRC=$(echo $OBSOLETESTR|wc -w); unset -v COUNTER; kill -s SIGUSR1 $(jobs -p f_adv); trap 2 3 9 15; f_clearline r 1 22
    }
    f_twin()
    {
	[ -n "$TWIN" -o -n "$SINGLE" ] && return
	echo -n " [»] traitement    "; trap 'kill $(jobs -p f_adv); echo; exit' 2 3 9 15; f_adv &
	for u in $RESULTFTU
	do
	    unset -v COUNTER
	    for s in $RESULTFTS
	    do
		[[ "$u" != "$s" ]] && continue
		let COUNTER++
	    done
	    [[ "$COUNTER" < 2 ]] && SINGLE="$SINGLE $u" && continue
	    TWIN="$TWIN $u"
	    TWINP="$TWINP $COUNTER"¿"$u"
	done
	TWINC=$(echo $TWIN|wc -w) SINGLEC=$(echo $SINGLE|wc -w)
	TWINP=`printf ' %b\n' $TWINP|sort -d|mawk '{gsub("¿","×  ");print $0}'`
	SINGLEP=$(printf ' [%b]' $SINGLE) ; TWIND=$(printf '%b\n' $TWIN)
	unset -v COUNTER; kill -s SIGUSR1 $(jobs -p f_adv); trap 2 3 9 15; f_clearline r 1 22
    }

    f_displayg()
    {
	{ echo; echo " [>] [$MACHINE] Occurrences globales:"; }|f_tee
	[ "$SOURCESG" = "" ] &&\
	for S in $SOURCESN;do f_dores -r;[ $RESULTFC -ne 0 ] && SOURCESG="$SOURCESG $RESULTFALIAS" && SOURCESNG="$SOURCESNG $S" && let SOURCESGC++
	done
	[ "$SOURCESG" = "" ] && { echo " [×] Aucune des sources analysées n'a d'occurrence installée sur $MACHINE"; echo -e "\n\n"; }|f_tee && return
	{ echo " [»] $SOURCESGC sources analysées ont des occurrences installées sur $MACHINE:"; echo; }|f_tee
	for S in $SOURCESNG; do f_dores -r; echo " [$S] $RESULTFALIAS [$RESULTFC occurrences]"|f_tee; TTLOCC=$(($TTLOCC+$RESULTFC)); done
	f_obs
	NOSOURCEDC=$(($OBSOLETESTRC+$UNKNOWNC)) SOURCEDC=$(($RESULTFTUC-$OBSOLETESTRC))
	{ echo " [*] total: $TTLOCC occurrences"; echo; echo " [*] $NINSTALLED paquets installés sur $MACHINE"
	echo " [*] $NOSOURCEDC paquets installés non présents dans les sources analysées"
	echo " [*] dont $OBSOLETESTRC paquets obsolètes et $UNKNOWNC paquets d'origine inconnue"
	echo " [*] $SOURCEDC paquets installés présents dans les sources analysées:"; echo; }|f_tee ; unset -v TTLOCC
	f_pager
	[ "$SOURCEDP" = "" ] && SOURCEDP=$(printf ' [%b]' $(printf '%b\n' $OBSOLETESTR $RESULTFTU|sort -d|uniq -u))
	[ "$NOSOURCEDP" = "" ] && NOSOURCEDP=$(printf ' [%b]' $(printf '%b\n' $OBSOLETESTR $UNKNOWNS|sort -d))
	[ "$NOPURGEP" = "" ] && NOPURGEP=$(printf ' [%b]' $NOPURGE)
	[ $PAGER = L ] && { printf '\n%b\n' " [*] $SOURCEDC paquets installés présents dans les sources analysées:" "$SOURCEDP"|f_fmt
	[ $NOSOURCEDC -gt 0 ] && printf '\n%b\n' " [*] $NOSOURCEDC paquets installés non présents dans les sources analysées:" "$NOSOURCEDP"|f_fmt
	[ $LINEWSC -gt 0 ] && printf '\n%b\n' \
	" [*] $LINEWSC derniers paquets installés (information mise à jour le $(date -r $CACHEDIR/Linews|cut -d'(' -f1)\b):" "$LINEWS"|f_fmt
	[ $LISUPRC -gt 0 ] && printf '\n%b\n' \
	" [*] $LISUPRC derniers paquets supprimés (information mise à jour le $(date -r $CACHEDIR/Lisupr|cut -d'(' -f1)\b):" "$LISUPR"|f_fmt
	[ $NOPURGEC -gt 0 ] && printf '\n%b\n' " [*] $NOPURGEC paquets supprimés de $MACHINE mais non purgés:" "$NOPURGEP"|f_fmt; }|f_tee |f_less
	[ $PAGER = d ] && { echo "$SOURCEDP"|f_fmt; [ $NOSOURCEDC -gt 0 ] &&\
	printf '\n%b\n' " [*] $NOSOURCEDC paquets installés non présents dans les sources analysées:" "$NOSOURCEDP"|f_fmt
	[ $LINEWSC -gt 0 ] && printf '\n%b\n' \
	" [*] $LINEWSC derniers paquets installés (information mise à jour le $(date -r $CACHEDIR/Linews|cut -d'(' -f1)\b):" "$LINEWS"|f_fmt
	[ $LISUPRC -gt 0 ] && printf '\n%b\n' \
	" [*] $LISUPRC derniers paquets supprimés (information mise à jour le $(date -r $CACHEDIR/Lisupr|cut -d'(' -f1)\b):" "$LISUPR"|f_fmt
	[ $NOPURGEC -gt 0 ] && printf '\n%b\n' " [*] $NOPURGEC paquets supprimés de $MACHINE mais non purgés:" "$NOPURGEP"|f_fmt; } |f_tee
	[ $PAGER = n ] && echo -e "$PAGERM"|f_tee || echo -e "$PAGERM"; { echo ; echo; }|f_tee
	[ $TSOURCES -eq 1 ] && { [ $RECORD = 1 ] && echo -e " [-] la séquence a été enregistrée dans $RECFILE\n\n" || echo; } && return

	f_twin
	[ $TWINC -eq 0 ] && { echo " [×] aucun croisement d'occurrence de paquets installés sur $MACHINE entre les sources analysées"
	echo -e "\n\n"; }|f_tee && return
	{ echo " [*] $SINGLEC occurrences uniques";echo " [*] $RESULTFTCC croisements d'occurrences";echo " [*] $TWINC occurrences croisées:"; }|f_tee
	f_pager
	[ $PAGER = L ] && { printf '\n%b\n%b\n\n%b' " [*] $RESULTFTCC croisements d'occurrences" " [*] $TWINC occurrences croisées:" "$TWINP"
	printf '\n\n\n%b\n\n%b' " [*] $SINGLEC occurrences uniques:" "$SINGLEP"|f_fmt; }|f_tee |f_less && echo -e "\n$PAGERM"
	[ $PAGER = d ] && { echo -e "\n$TWINP";echo -e "\n";echo -e " [*] $SINGLEC occurrences uniques:\n";echo "$SINGLEP"|f_fmt; }|f_tee
	[ $PAGER = n ] && echo -e "\n$PAGERM"|f_tee
	[ $RECORD = 1 ] && echo -e "\n\n [-] la séquence a été enregistrée dans $RECFILE\n\n" || echo -e "\n\n"
    }

    f_display()
    {
	{ echo; echo " [>] [$MACHINE] Occurrences par sources:"; echo; }|f_tee
	{ for (( S=1; S <= $TSOURCES; S++ )); do f_dores -r; echo $SELECTS|grep -w $S &>/dev/null
	[ $? -eq 0 ] && echo " [$S] $RESULTFALIAS [$RESULTFC occurrences]" || echo " [×]";done; }|f_tee
	f_pager
	[ $PAGER != n  ] && f_obs &&\
	for S in $SELECTS
	do
	    f_dores
	    echo -e "\n [*] $RESULTFC occurrences installées présentes dans le cache de la source [$RESULTFALIAS]:"
	    [ $RESULTFC -ne 0 ] && echo && printf ' [%b]' $RESULTF|f_fmt; echo
	    [ $OBSOLETESC -ne 0 ] && echo " [*] dont $OBSOLETESC occurrences obsolètes:" && printf ' [%b]' $OBSOLETES|f_fmt && echo
	done |f_tee|f_less ; echo|f_tee
	[ $PAGER = n ] && echo -e "$PAGERM\n"|f_tee || echo -e "$PAGERM\n"; echo|f_tee
	{ printf " [»] occurrences uniques et croisées des sources"; printf ' [%b]' $SELECTS; echo ":"; }|f_tee
	f_pager
	f_sort()
	{
	    f_twin
	    for S in $SELECTS
	    do
		f_dores -r
		for r in $RESULTF
		do
		    echo "$TWIND"|grep -qxm 1 "$r" &>/dev/null && MULTIX="$MULTIX $r" && continue
		    UNIX="$UNIX $r"
		done
		UNIXC=$(echo $UNIX|wc -w) MULTIXC=$(echo $MULTIX|wc -w)
		echo -e "\n [$S] $RESULTFALIAS [$(($UNIXC+$MULTIXC)) occurrences]" ; echo
		[ $UNIXC -ne 0 ] && echo " [*] $UNIXC occurrences uniques:" && printf ' [%b]' $UNIX|f_fmt && echo
		[ $MULTIXC -ne 0 ] && echo " [*] $MULTIXC occurrences croisées:" && printf ' [%b]' $MULTIX|f_fmt && echo
		unset -v UNIX MULTIX
	    done |f_tee |f_less ; echo -e "\n$PAGERM\n\n"
	    [ $RECORD = 1 ] && echo -e " [-] la séquence a été enregistrée dans $RECFILE\n\n" || echo
	}
	[ $PAGER != n  ] && f_sort || echo -e "\n$PAGERM\n\n\n"|f_tee
    }
    f_displaym()
    {
	echo; echo " [>] [$MACHINE] Occurrences par sources:"; echo
	for ((S=1;S<=TSOURCES;S++)); do f_dores -r; echo " [$S] $RESULTFALIAS [$RESULTFC occurrences]"; done; echo
	while true
	do
	    read -p " [¿] afficher les occurrences pour $SOURCESNP (défaut &) <$SOURCESN>: " SELECTS
	    case "$SELECTS" in
		q|Q) f_exit;;
		h)  less $0;;
		"") SELECTS=$SOURCESN ; $CLEAR ; f_display ; break;;
		*)  SELECTS=$(printf '%b\n' $SELECTS|sort -u) doit=1
		    for s in $SELECTS; do echo $SOURCESN|grep -w $s &>/dev/null; [ $? -ne 0 ] && doit=0; done
		    [ $doit -ne 0 ] && $CLEAR && f_display && break;;
	    esac
	done
    }

    f_package()
    {
	{ echo -e "\n [>] [$MACHINE] Analyse par paquets:\n"; echo " [»] analyse des occurrences croisées entre les sources analysées:"; }|f_tee
	f_twin
	f_x()
	{
	    { echo " [*] $RESULTFTCC croisements d'occurrences"
	    echo " [*] $TWINC occurrences croisées:"; echo; }|f_tee
	    f_pager
	    [ $PAGER = n  ] && echo -e "$PAGERM\n"|f_tee && return
	    for x in $TWIN
	    do
		[ -z "$COUNT" -a $PAGER = L ] && printf '%b\n%b\n\n'\
		" [*] $RESULTFTCC croisements d'occurrences" " [*] $TWINC occurrences croisées:"
		let COUNT++; echo " [$COUNT] [$x]"; unset -v COUNTER
		for S in $SOURCESN
		do
		    f_dores -r; let COUNTER++
		    [ $COUNTER -eq 1 ]\
		    && LOCI=$(grep -xnm 1 "Package: $x" $INSTALLDIR/status|cut -d: -f1) LOCIF=$(($LOCI+15))\
		    && VERI=`mawk 'NR==LOCI,NR==LOCIF {print $0}' LOCI=$LOCI LOCIF=$LOCIF $INSTALLDIR/status|grep -m 1 "^Version: "`\
		    && STATI=`mawk 'NR==LOCI,NR==LOCIF {print $0}' LOCI=$LOCI LOCIF=$LOCIF $INSTALLDIR/status|grep -m 1 "^Status: "`\
		    && echo -e " $VERI $STATI"\
		    && for d in $(ls $CACHEDIR/.old/sources/.deleted/)
		    do grep -qxm 1 "$x" $CACHEDIR/.old/sources/.deleted/$d && echo " présent dans la source supprimée $d"; done
		    LOC=$(grep -xnm 1 "Package: $x" $SOURCESDIR$RESULTFALIAS)
		    [ -z "$LOC" ] && { printf '%b\n' $RESULTF|grep -qxm 1 $x && echo " obsolète dans $RESULTFALIAS"; true; } && continue
		    LOC=$(echo $LOC|cut -d: -f1) LOCF=$(($LOC+15))
		    VER=`mawk 'NR==LOC,NR==LOCF {print $0}' LOC=$LOC LOCF=$LOCF $SOURCESDIR$RESULTFALIAS|grep -m 1 "^Version: "`
		    printf ' %b %b\n' "$VER" "dans $RESULTFALIAS"
		done
		echo
	    done |f_tee |f_less ; unset -v COUNT COUNTER; echo -e "$PAGERM\n"
	}
	[ $TWINC -eq 0 ]&&echo -e " [×] aucun croisement d'occurrences de paquets installés sur $MACHINE entre les sources analysées\n"|f_tee||f_x

	echo -e "\n\n [»] analyse des paquets installés non présents dans les sources analysées:"|f_tee
	f_u()
	{
	    echo " [*] $UNKNOWNC paquets installés non présents dans les sources analysées:"|f_tee
	    f_pager; [ $PAGER = n  ] && echo -e "\n$PAGERM\n"|f_tee && return
	    { COUNT=0 TUNKNOWNS=$UNKNOWNS; [ $PAGER = L ] && echo " [*] $UNKNOWNC paquets installés non présents dans les sources analysées:"
	    for u in $UNKNOWNS
	    do
		unset -v COUNTER
		for d in $(ls $CACHEDIR/.old/sources/.deleted/)
		do
		    grep -qxm 1 "$u" $CACHEDIR/.old/sources/.deleted/$d && { TUNKNOWNS=$(printf '%b\n' $TUNKNOWNS|grep -vx $u); let COUNTER++
		    [ $COUNTER -eq 1 ] && { let COUNT++; echo -e "\n [$COUNT] [$u]"; }; echo " origine possible: source supprimée [$d]"; }
		done

	    done
	    [ $COUNT -gt 0 -a $COUNT -lt $UNKNOWNC ] && { echo -e \
	    "\n [×] les données dont dispose $PROGNAME sont trop récentes pour déterminer l'origine des $(($UNKNOWNC-$COUNT)) autres paquets:"
	    printf ' [%b]' $TUNKNOWNS|f_fmt; }\
	    || { [ $COUNT -eq 0 ]&&echo " [×] les données dont dispose $PROGNAME sont trop récentes pour déterminer l'origine des $UNKNOWNC paquets:"
	    printf ' [%b]' $TUNKNOWNS|f_fmt; }; }|f_tee |f_less; unset -v COUNT COUNTER; echo -e "\n$PAGERM\n"
	}
	[ $UNKNOWNC -eq 0 ] && echo -e " [×] aucun paquet d'origine inconnue installé sur $MACHINE\n"|f_tee || f_u

	echo -e "\n\n [»] analyse des occurrences obsolètes dans le cache des sources analysées:"|f_tee
	f_obs
	f_o()
	{
	    echo " [*] $OBSOLETESTC occurrences obsolètes:"|f_tee
	    f_pager; [ $PAGER = n  ] && echo -e "\n$PAGERM\n"|f_tee && return
	    { OBS=$OBSOLETESTC; echo; [ $PAGER = L  ] && { echo " [*] $OBSOLETESTC occurrences obsolètes:"; echo; }
	    for o in $OBSOLETEST
	    do
		let COUNT++; unset -v COUNTER NOBS
		for S in $SOURCESN
		do
		    f_dores -r
		    let COUNTER++; [ $COUNTER -eq 1 ] && echo " [$COUNT] [$o]"\
		    && LOCI=$(grep -xnm 1 "Package: $o" $INSTALLDIR/status|cut -d: -f1) LOCIF=$(($LOCI+15))\
		    && VERI=`mawk 'NR==LOCI,NR==LOCIF {print $0}' LOCI=$LOCI LOCIF=$LOCIF $INSTALLDIR/status|grep -m 1 "^Version: "`\
		    && STATI=`mawk 'NR==LOCI,NR==LOCIF {print $0}' LOCI=$LOCI LOCIF=$LOCIF $INSTALLDIR/status|grep -m 1 "^Status: "`\
		    && echo " $VERI $STATI" && OBSR="$OBSR $o"
		    LOC=$(grep -xnm 1 "Package: $o" $SOURCESDIR$RESULTFALIAS)
		    [ -z "$LOC" ] && { printf '%b\n' $RESULTF|grep -qxm 1 $o && echo " obsolète dans $RESULTFALIAS"; true; }\
		    && continue
		    LOC=$(echo $LOC|cut -d: -f1) LOCF=$(($LOC+15))
		    VER=`mawk 'NR==LOC,NR==LOCF {print $0}' LOC=$LOC LOCF=$LOCF $SOURCESDIR$RESULTFALIAS|grep -m 1 "^Version: "`
		    printf ' %b %b\n' "$VER" "dans $RESULTFALIAS" ; [ "$VERI" = "$VER" ] && NOBS=1
		done
		[ -n "$NOBS" ] && { echo " [¡] la version installée de $o n'est PAS obsolète"; let OBS--; OBSR=$(printf '%b\n' $OBSR|grep -vx $o); }
		echo
	    done
	    [ $OBS -eq 0 ] && echo " [×] aucun des paquets installés sur $MACHINE n'est obsolète"\
	    || { echo -e "\n [*] $OBS paquets installés sur $MACHINE sont obsolètes:"; printf ' [%b]' $OBSR|f_fmt; echo; }
	    [ $UNKNOWNC -ne 0 ]\
	    && echo " [¡] il est possible que les $UNKNOWNC paquets non présents dans les sources analysées soient obsolètes"
	    } |f_tee |f_less; unset -v COUNT COUNTER; echo -e "\n$PAGERM\n"
	}
	[ $OBSOLETESTC -eq 0 ] && { echo " [×] aucune occurrence obsolète de paquets installés sur $MACHINE dans les sources analysées"
	[ $UNKNOWNC -ne 0 ] &&\
	echo -e " [¡] il est possible que les $UNKNOWNC paquets non présents dans les sources analysées soient obsolètes\n\n"; } |f_tee || f_o
	[ $RECORD = 1 ] && echo -e "\n [-] la séquence a été enregistrée dans $RECFILE\n\n" || echo -e "\n\n"
    }

    echo
    [ $TSOURCES -eq 0 -a $OCCLESS -eq 1 ]\
    && echo " [>] Aucune des sources traitées ne présente d'occurrence en concordance avec les paquets installés sur $MACHINE"\
    && echo " [»] aucune analyse à effectuer" && echo && read -s -n 1 -p " [-] valider pour continuer... " && f_clearline r 1 35 && return 2
    echo " __________________________________________________________________________"
    echo " [-] Enregistrer la prochaine séquence - Aide - Accueil - Quitter <r/h/w/q>" ; RECORD=0 REC=""
    while true
    do
	read -s -n 1 -p " [¿]$REC [$MACHINE]: Analyse globale - Par sources - Par paquets <g/s/p>: " SELECT
	f_clearline r 1 110
	case "$SELECT" in
	    q|Q) f_exit;;
	    h) less $0;;
	    w) read -s -n 1 -p " [¿] Retourner à l'accueil ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 40 || { $ECHO; return; };;
	    r) REC=" [R]" RECORD=1;echo -e "\n\n $(date)" >>$RECFILE;;
	    g) $CLEAR; f_displayg;break;;
	    s) $CLEAR; f_displaym;break;;
	    p) $CLEAR; f_package;break;;
	esac
    done
    f_postproc
}

f_home()
{
    [ -d ~/.$PROGNAME/$(uname -n) ] || { $CLEAR; echo -e "\n [>] Initialisation de $PROGNAME\n"; MACHINE=$(uname -n)
    f_init; f_mkcache; f_updatecache; trap 2 3 9 15; return; }
    [ -d ~/.$PROGNAME/_alternate ] || { mkdir -p ~/.$PROGNAME/_alternate/
    echo "Copiez ici les répertoires /var/lib/apt/lists/ et /var/lib/dpkg/ d'un système alternatif que vous souhaitez analyser"\
    > ~/.$PROGNAME/_alternate/readme.txt; }

    $CLEAR; echo -e "\n\n\n\n\n\n"
    for w in Advanced  Packaging Tool DIAGnostic
    do
	NL=$(echo ${#w}) NLA=$NL; echo -n " "
	for ((l=1;l<=NL;l++))
	do
	    L=$(echo $w|cut -c $l); echo $L|grep -q [A-Z] && L="\033[1m$L\033[0m"
	    for ((la=NLA;la>=0;la--))
	    do
		for ((s=la;s>=1;s--)); do SPACE="$SPACE "; done
		echo -ne "$SPACE$L "; NSPACE=$(echo ${#SPACE})
		unset -v SPACE; sleep 0.01; [ $NSPACE -eq 0 ] && break
		for ((b=1;b<=NSPACE+2;b++)); do echo -ne "\b"; done
	    done
	    NLA=$(($NLA-1))
	    let INTER++; for ((s=2;s<=INTER/2;s++)); do echo -n " "; done
	done
	echo; unset -v INTER
    done
    echo -e "\n\n\n\n\n\n\n\n\n\n\n\n\n\n [>] $PROGNAME"; echo

    [ -d ~/.$PROGNAME/_alternate/lists/ -a -d ~/.$PROGNAME/_alternate/dpkg/ ] && { echo " [»] un système alternatif a été détecté"
    [ -w ~/.$PROGNAME/_alternate/lists/ -a -w ~/.$PROGNAME/_alternate/dpkg/ ]\
    || { echo -e "\a\n [!] la configuration alternative n'a pas les droits en écriture, sortie\n"; exit 1; }
    [[ $(ADM=~; dpkg --admindir=$ADM/.$PROGNAME/_alternate/dpkg/ -l 2>/dev/null) ]]\
    || { echo -e "\a\n [!] le répertoire ~/.$PROGNAME/_alternate/dpkg/ n'est pas une configuration valide pour dpkg, sortie\n"; exit 1; }
    read -p " [¿] nom du système: " MACHINE; f_clearline r 1 100
    MACHINE=$(echo $MACHINE|tr -d ".%$&|<>*\'\"# /\\" 2>/dev/null|tr "[A-Z]" "[a-z]"|sed 's/^-//')
    [ $(echo ${#MACHINE}) -gt 35 ] && echo " [!] $MACHINE: ce nom est déraisonnablement long..." && sleep 3 && f_clearline r 1 120 && return
    [ -d ~/.$PROGNAME/$MACHINE ] && echo " [!] $MACHINE: ce nom de système est déjà utilisé..." && sleep 3 && f_clearline r 1 100 && return
    read -s -n 1 -p " [¿] ce nom est-il correct: $MACHINE ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 100 && return
    $CLEAR; echo -e "\n [>] Initialisation du cache pour $MACHINE\n"; f_init; f_mkcache; f_updatecache; trap 2 3 9 15; return; }

    echo " [»] systèmes analysables:"
    echo " [1] $(uname -n)"; COUNTER=1 MACHINES=1 MACHINESP=1; unset -v MACHINESUP MACHINESUPP
    for m in $(ls ~/.$PROGNAME/)
    do
	[ "$m" = "$(uname -n)" -o "$m" = "_alternate" ] && continue; [ -d ~/.$PROGNAME/$m ] || continue
	let COUNTER++; alias $COUNTER=$m; MACHINES="$MACHINES $COUNTER" MACHINESP="$MACHINESP/$COUNTER"
	MACHINESUP="$MACHINESUP $COUNTER" MACHINESUPP="$MACHINESUPP/$COUNTER"; echo " [$COUNTER] $m"
    done
    echo; unset -v COUNTER; echo " [-] Aide - Accueil - Quitter <h/w/q>"
    [ -n "$MACHINESUP" ] && MACHINESUPP=$(echo $MACHINESUPP|sed 's/^\///') &&\
    while true
    do
	read -s -n 1 -p " [¿] Sélectionner un système - Supprimer un système (défaut 1) <$MACHINESP/->: " SELECT
	f_clearline r 1 100
	case "$SELECT" in
	    q|Q) f_exit;;
	    h) 	less $0;;
	    w) read -s -n 1 -p " [¿] Recommencer ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 30 || { $ECHO; return; };;
	    -)	read -s -n 1 -p " [¿] sélectionner un système pour SUPPRESSION <$MACHINESUPP>: " SELECTM; f_clearline r 1 100
		echo $MACHINESUP|grep -w $SELECTM &>/dev/null\
		&& { read -s -n 1 -p " [¿] supprimer $(alias $SELECTM|cut -d "'" -f2) de $PROGNAME ? <o/N>: " DEL; f_clearline r 1 100
		[ "$DEL" = o ] && rm -fr ~/.$PROGNAME/$(alias $SELECTM|cut -d "'" -f2); unset -v DEL; return; };;
	    ""|1) MACHINE=$(uname -n); break;;
	    *) 	echo $MACHINES|grep -w $SELECT &>/dev/null && MACHINE=$(alias $SELECT|cut -d "'" -f2) && break;;
	esac
    done || MACHINE=$(uname -n); for m in $MACHINESUP; do unalias $m &>/dev/null; done
    while true
    do
	read -s -n 1 -p " [¿] [$MACHINE]: travailler sur l'ensemble des sources ? <O/n>: " SELECT
	f_clearline r 1 100
	case "$SELECT" in
	    q|Q) f_exit;;
	    h) less $0;;
	    w) read -s -n 1 -p " [¿] Recommencer ? <O/n>: " RESUME; [ "$RESUME" = n ] && f_clearline r 1 30 || { $ECHO; return; };;
	    o|O|"") $CLEAR; f_init; f_mkcache; f_updatecache; trap 2 3 9 15; OCCLESS=1; f_squeezecache; f_postproc; break;;
	    n) $CLEAR; f_init; f_mkcache; f_updatecache; trap 2 3 9 15; f_selectsources; f_occm; f_squeezecache; f_postproc; break;;
	esac
    done
}

while true; do f_home; done