#!/bin/sh
set -e

analyserParametres()
{
	local i
	DOSSIER=
	FILTRE=
	ECRABOUILLE=
	SSH=
	OPTIONS_SSH=
	PARAMS_RSYNC=
	OPTIONS_RSYNC=
	SOURCE=$HOME
	SOURCES="/"
	if rsync --version | grep -q xattrs # Version 3, sans doute, avec gestion des xattrs et tout par la communauté.
	then
		OPTIONS_RSYNC=AX
	else # Version Apple.
		OPTIONS_RSYNC=E
	fi
	compression=z
	if [ -e /sbin/adbd ]
	then
		compression=zz
	fi
	OPTIONS_RSYNC="a$compression$OPTIONS_RSYNC"
	while [ $# -gt 0 ] ; do
		case "$1" in
			--prune-empty-dirs) PARAMS_RSYNC="$PARAMS_RSYNC $1" ;;
			-x) OPTIONS_RSYNC="x$OPTIONS_RSYNC" ;;
			-v) OPTIONS_RSYNC="v$OPTIONS_RSYNC" ;;
			-e) ECRABOUILLE=1 ;;
			-r) shift ; SEUIL_RECUP="$1" ;;
			--reprendre) shift ; REPRENDRE="$1" ;;
			-d) shift ; SOURCE="$1" ;;
			-l) shift ; P_LIEN="$1." ;;
			--bete) bete=1 ;;
			--sans-acl) bebete=1 ;;
			--ssh) shift ; SSH="$1" ;;
			+*) SOURCES="$SOURCES `echo "$1" | sed -e 's/^+//' -e 's#/*$#/#'`" ;;
			*)
				[ -z "$DOSSIER" ] && case "$1" in # Tout ce que l'on reconnaît avant le DOSSIER est une option à passer à ssh.
					-p)
						OPTIONS_SSH="$OPTIONS_SSH $1 $2" # À FAIRE: avec utils.params.sh.
						shift ; shift
						continue
						;;
				esac
				for i in DOSSIER FILTRE
				do
					eval test -z \$$i && eval $i=\""$1"\" && break
				done
				;;
		esac
		shift
	done
	[ -z "$bete" ] || OPTIONS_RSYNC="`echo "$OPTIONS_RSYNC" | sed -e 's/a/vrltD/g'`"
	[ -z "$bete" ] || OPTIONS_RSYNC="`echo "$OPTIONS_RSYNC" | tr -d AXE`"
	[ -z "$bebete" ] || OPTIONS_RSYNC="`echo "$OPTIONS_RSYNC" | tr -d A`"
}

auSecours()
{
	nom=`basename "$0"`
	cat >&2 << TERMINE
# $nom
# Sauvegarde du \$HOME
# © Guillaume Outters 2005-2013

Utilisation: $nom [-v] [-e] [--bete] [--sans-acl] [-d <dossier>] [-r (<n min>|<suffixe>)] [-l <plien>] [--reprendre <date>] [-p <port>|--ssh <ssh>] [<ip>:]<dossier>[/<préfixe>.] <filtre>
  -v:                           blablateux.
  -e:                           écrabouille. La nouvelle sauvegarde écrase
                                directement <dossier>, plutôt que d'en créer une
                                incrémentale à côté avec dans le nom <préfixe>.
  --bete:                       destination disque bête (NTFS, etc.): pas de
                                gestion des permissions.
  --sans-acl:                   destination semi-intelligente (UFS, etc.):
                                permissions, mais pas d'ACL.
  -d <dossier>:                 dossier source, plutôt que \$HOME.
  -r (<n min>|<suffixe>):       récupère une ancienne sauvegarde, s'il en existe
                                plus de n min. Si le paramètre n'est pas
                                numérique, il est vu comme un suffixe: on
                                cherchera à recycler n'importe quel répertoire
                                ayant ce suffixe.
  -l <plien>:                   lie ses fichiers à ceux de la dernière
                                sauvegarde de préfixe <plien>. Par défaut
                                <préfixe>.
  --reprendre <date>            reprend la sauvegarde du <date> si elle a été
                                interrompue prématurément.
  --ssh <ssh>                   commande SSH à passer en -e à rsync.
  -p <port>:                    complément d'<ip>.
  <ip>:                         adresse IP de destination, pour une sauvegarde à
                                distance.
  <dossier>:                    dossier de destination, façon scp (relatif au
                                \$HOME ou absolu). Attention à ce qu'il ne
                                termine pas par un point (ce qui dénote un
                                préfixe de sauvegarde).
  <préfixe>:                    préfixe sur la destination qui identifiera la
                                provenance. Par défaut, `hostname -s | tr '[A-Z]' '[a-z]'`.
  <filtre>:                     filtre rsync, séparé par des points-virgules
                                (ex.: '+ /tmp/glub;- /tmp/').
La sauvegarde est faite par rsync --link-dest, pour les connaisseurs.
TERMINE
exit 0
}

analyserParametres "$@"
PANO="`echo "$DOSSIER" | cut -d : -f 1`"
DOSSIER="`echo "$DOSSIER" | cut -d : -f 2-`"

# Local ou distant?

fauxsshlocal()
{
	shift # On se fiche du nom de la machine.
	"$@"
}

rsyncssh()
{
	rsync -e "$SSH" "$@"
}

affleger()
{
	# https://stackoverflow.com/a/13154756/1346819
	"$@" | grep -E '^deleting|[^/]$'
}

[ -z "$SSH" ] && SSH="ssh $OPTIONS_SSH" # BOGUE: ce faisant, on ne peut avoir d'espace dans les OPTIONS_SSH.
RSYNC="rsyncssh $PARAMS_RSYNC"
if [ "$PANO" = "$DOSSIER" ]
then
	SSH=fauxsshlocal
	RSYNC="rsync $PARAMS_RSYNC"
	case "$DOSSIER" in
		/*) true ;;
		*)  PANOP="$HOME/" ;; # Un chemin absolu est nécessaire si on travaille en local, car sinon il y aura confusion entre le répertoire de travail $SOURCE et la racine de destination $HOME.
	esac
else
	PANOP="$PANO:"
fi

# Chemin spécifié ou à déterminer?

case "$DOSSIER" in
	*.)
		PREFIXE="`echo "$DOSSIER" | sed -e 's#.*/##'`"
		DOSSIER="`dirname "$DOSSIER"`"
		;;
	*)
	PREFIXE="`hostname -s | tr '[A-Z]' '[a-z]'`." # Par défaut; un tr derrière, car Mac OS X hésite chez moi: un jour en majuscules, l'autre en minuscules.
		;;
esac

[ -z "$PANO" -o -z "$PREFIXE" ] && auSecours

# Préparation du terrain distant.

echo 'cd "$HOME" && cd "'"$DOSSIER"'" && chmod 700 "'"$PREFIXE"'"*/' | $SSH "$PANO" sh # Il se peut que nous ayons rendu illisibles tous les répertoires, afin d'éviter à updatedb ou mdfind de les parcourir.

# Détermination de la vraie destination.

if [ -z "$ECRABOUILLE" ]
then
	if [ -z "$REPRENDRE" ]
	then
dest="$PREFIXE`date +%Y-%m-%d`"
	else
		dest="$PREFIXE$REPRENDRE"
		echo 'cd "$HOME" && cd "'"$DOSSIER"'" ; rm -f "'"$dest"'/.ouf.complet"' | $SSH "$PANO" sh 
	fi
	if [ ! -z "$SEUIL_RECUP" ]
	then
		# SEUIL_RECUP numérique: on recycle la nième sauvegarde (ou plus ancienne).
		if echo "x$SEUIL_RECUP" | grep -q '^x[0-9][0-9]*$'
		then
		premier="`echo 'cd "$HOME" && cd "'"$DOSSIER"'" && mkdir -p "'"$PREFIXE"'" && touch -t 197910100826 "'"$PREFIXE"'" && ls -d1 "'"$PREFIXE"'"*/ | grep -v "^'"$PREFIXE"'[^0-9]" | sort -r | sed -e '\''s#/$##'\'' -e 1,'"$SEUIL_RECUP"'d | tail -1' | $SSH "$PANO" sh`"
		# SEUIL_RECUP alphabétique: on recycle la plus ancienne sauvegarde ainsi suffixée.
		else
			premier="`echo 'cd "$HOME" && cd "'"$DOSSIER"'" && mkdir -p "'"$PREFIXE.$SEUIL_RECUP"'" && touch -t 197910100826 "'"$PREFIXE.$SEUIL_RECUP"'" && ls -d1 "'"$PREFIXE"'"*".'"$SEUIL_RECUP"'"/ | sed -e '\''s#/$##'\'' | grep -v "^'"$PREFIXE.$SEUIL_RECUP"'" | head -1' | $SSH "$PANO" sh`"
		fi
	fi
	[ -z "$P_LIEN" ] && P_LIEN="$PREFIXE"
	dernier="`echo 'cd "$HOME" && cd "'"$DOSSIER"'" && mkdir -p "'"$P_LIEN"'" && touch -t 197910100826 "'"$P_LIEN"'" && ls -d1 "'"$P_LIEN"'"*/.ouf.complet | grep -v "^'"$P_LIEN"'[^0-9]" | tail -1 | sed -e "s#/.ouf.complet##"' | $SSH "$PANO" sh`"
	if [ -z "$REPRENDRE" ]
	then
num="`echo 'cd "$HOME" && cd "'"$DOSSIER"'" ; mkdir '"$dest"' 2> /dev/null && exit 0 ; c=1 ; until mkdir '"$dest"'.$c 2> /dev/null ; do c=\`expr $c + 1\` ; done ; echo $c' | $SSH "$PANO" sh`"
[ "$num" = "" ] || dest="$dest.$num"
	fi
	LIEN=--link-dest="../$dernier/"
	if [ ! -z "$premier" ]
	then
		postdest="$dest"
		dest="$premier"
	fi
else
	echo 'cd "$HOME" && cd "'"$DOSSIER"'" ; mkdir -p '"$dest"' 2> /dev/null ; rm -f "'"$dest"'/.ouf.complet" ; touch "'"$dest"'"' | $SSH "$PANO" sh 
	dest="."
	LIEN=
fi

# Copie.

if [ -d "$SOURCE" ]
then
	SOURCEREL="."
	cd "$SOURCE"
else
	SOURCEREL="$SOURCE"
	SOURCE=.
	case "$SOURCEREL" in
		*:) SOURCEREL="$SOURCEREL." ;;
		*/) SOURCEREL="`echo "$SOURCEREL" | sed -e 's#//*$##'`" ;;
	esac
fi

( if [ -z "$FILTRE" ] ; then cat ; else echo "$FILTRE" | tr ';' '\012' ; fi ; echo "- /.ouf.complet" ) > /tmp/temp.$$.filtre

# Transforme une liste de - /home/totor/plurk en - /plurk lorsque $1 vaut /home/totor/
filtrePourSousRacine()
{
	# À FAIRE: savoir reconnaître aussi les **.
	expSource="`echo "$source" | sed -e 's#\([^/][^/]*\)#(\1|\\\\\\\\*)#g' -e 's#^#^#' -e 's#/$#(/|$)#'`"
	(
		# Les autres sources sont exclues (car elles seront traitées par un autre rsync).
		for autreSource in $SOURCES
		do
			[ "$autreSource" = "$source" ] || echo "- $autreSource"
		done
		# Le filtre d'origine
		cat /tmp/temp.$$.filtre
	) | awk \
	'
		# Pour la source courante, seules les entrées non enracinées, ou celles enracinées à ladite source, sont conservées.
		/^#/{next}
		/^[-+] *[^ \/]/{print;next}
		{
			chemin = $0
			gsub(/^[+-] */, "", chemin)
			if(!match(chemin, "'"$expSource"'")) next
			print $1" /"substr(chemin, RLENGTH + 1)
		}
	'
}

for source in $SOURCES
do
	pointsPointsPourSousRacine="`echo "$source" | sed -e 's#/[^/][^/]*#/..#g'`"
	lienPourSousRacine="`echo "$LIEN" | sed -e "s#\\.\\./#..$pointsPointsPourSousRacine#" -e 's#/$##'`$source"
	filtrePourSousRacine "$source" | affleger $RSYNC --delete --exclude-from=- $lienPourSousRacine -$OPTIONS_RSYNC "$SOURCEREL$source" "$PANOP$DOSSIER/$dest$source" || [ $? -eq 23 -o $? -eq 24 ] # On ne sort pas en erreur pour des Permission denied ou fichiers modifiés.
done

# Marquage comme dernière sauvegarde complète.

(
	printf "%s" 'cd "$HOME" && cd "'"$DOSSIER"'" && touch "'"$dest"'/.ouf.complet"'
	[ ! -z "$postdest" ] && printf "%s" " && [ \`ls '$postdest' | wc -l\` = 0 ] && rm -R '$postdest' && mv '$dest' '$postdest'" && dest="$postdest" # postdest a normalement été créé juste un peu au-dessus, donc on peut le supprimer sans soucis.
	printf "%s" ' && chmod 000 "'"$PREFIXE"'"*/ && chmod 700 "'"$dest"'"' # Afin d'éviter que les recherches système ne parcourent toutes les sauvegardes, on ne rend lisible que l'actuelle.
	[ -z "$ECRABOUILLE" ] && printf "%s" ' && rm -f "'"$PREFIXE"'dernier" && ln -s "'"$dest"'" "'"$PREFIXE"'dernier"'
	echo
) | $SSH "$PANO" sh

exit 0

# Pour relier une sauvegarde faite en autonome:
( cd asterix.2005-11-15 && find . -type f | while read i ; do [ -f "$i" -a -f "../asterix.2005-11-14/$i" -a `cksum "$i" | awk '{print $1}'` = `cksum "../asterix.2005-11-14/$i" | awk '{print $1}'` ] && echo "$i" && sudo rm "$i" && ln "../asterix.2005-11-14/$i" "$i" ; done )

