# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 urequire multiconf PFF_ORIGEXT=pff PFF_CONF=.pff.conf # ne pas modifier DEFAULT_PFF_ORIGEXTS=(".$ORIGEXT" .origine .default) DEFAULT_PFF_PROTECTS=(/.git/ .svn/ /pff/ "/$PFF_CONF") PFF_CONFVARS=( "VERSION//Version actuellement installée" -a "PVERSIONS//Versions en attente d'intégration" "PROFILES//Profils définis" -s "DISTTYPE=auto//Type de distribution upstream: full ou patch" -a "ORIGEXTS=//Extensions origines" "PROTECTS=//Fichiers locaux à protéger lors de l'intégration e.g /dir/, /file, etc." "MKDIRS//Répertoires qui doivent toujours exister" "FILTERS//Filtres appliqués aux fichiers lors de l'intégration" "NOMERGES=//Fichiers qu'il ne faut pas chercher à fusionner" ) # Nomenclature pour le nommage des fichiers traités: # pfile: le chemin absolu du fichier dans le projet # rfile: le chemin relatif du fichier dans le projet # bfile: le chemin absolu du fichier dans pff/Base/ # Cfile: le chemin absolu du fichier dans pff/Common/ # cfile: le chemin absolu du fichier dans pff/Current/ # Pfile: le chemin absolu du fichier dans pff/ANYPROFILE/ # plink: la destination du lien pfile # clink: la destination du lien cfile # Plink: la destination du lien Pfile function flexists() { [ -e "$1" -o -L "$1" ] } function multiups() { # afficher un chemin vers le haut e.g ../../.. avec autant d'éléments que # les répertoires du chemin relatif $1. # méthode: commencer avec la valeur de départ $2 et préfixer avec autant de # ../ que nécessaire. puis afficher le résultat. local tmp="$1" link="$2" setx tmp=dirname -- "$tmp" while [ "$tmp" != . ]; do [ -n "$link" ] && link="/$link" link="..$link" setx tmp=dirname -- "$tmp" done echo "$link" } function find_pffdir() { # trouver le répertoire du projet pff à partir du répertoire $2(=.) et # mettre le chemin absolu dans la variable $1(=pffdir) # si le répertoire n'est pas trouvé, retourner 1 local destvar="${1:-pffdir}" pffdir setx pffdir=abspath "${2:-.}" while true; do if [ -f "$pffdir/$PFF_CONF" -a -d "$pffdir/pff" ]; then local "$destvar" upvar "$destvar" "$pffdir" return 0 fi [ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break setx pffdir=dirname -- "$pffdir" done return 1 } function ensure_pffdir() { # trouver le répertoire du projet pff à partir du répertoire $2(=.) et # mettre le chemin absolu dans la variable $1(=pffdir) # si le répertoire n'est pas trouvé, arrêter le script avec un code d'erreur local destvar="${1:-pffdir}" pffdir if find_pffdir pffdir "$2"; then conf_init "${PFF_CONFVARS[@]}" source "$pffdir/$PFF_CONF" local "$destvar"; upvar "$destvar" "$pffdir" return fi local msg="Projet pff introuvable (utiliser --init ?)" [ -n "$2" ] && die "$2: $msg" || die "$msg" } function pff_get_current_profile() { # afficher le profil courant du projet pff $1, s'il est défini local pffdir="$1" [ -L "$pffdir/pff/.Current" ] && readlink "$pffdir/pff/.Current" } function pff_get_profiles() { # afficher tous les profils valides du projet pff $1 local pffdir="$1" (for profile in "${PROFILES[@]}"; do echo "$profile"; done list_dirs "$pffdir/pff") | sort -u | grep -vxF Current } function pff_get_user_profiles() { # afficher tous les profils modifiables du projet pff $1 (c'est à dire tous # les profils valides excepté Base) pff_get_profiles "$@" | grep -vxF Base } function pff_get_first_profile() { # afficher le premier profil autre que Base du projet pff $1 local profile profile="${PROFILES[0]}" if [ -z "$profile" -o "$profile" == Base ]; then pff_get_user_profiles "$@" | head -n1 else echo "$profile" fi } function pff_get_local_files() { # afficher tous les fichiers locaux exprimés relativement au répertoire du # projet pff $1 local pffdir="$1" find "$pffdir/pff/Base" -type f | sed "s|^$pffdir/pff/Base/||" | grep -v '__pv-.*__$' } function pff_get_rfile() { # obtenir le chemin relatif du fichier $1 exprimé par rapport au répertoire # du projet pff $2. Si c'est un fichier d'un répertoire de profil, # l'exprimer comme un chemin du répertoire de projet, e.g pff/Profile/path # devient path # retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou # pas dans un répertoire de profil) local rfile="$1" pffdir="$2" setx rfile=abspath "$rfile" [ "${rfile#$pffdir/}" != "$rfile" ] || return 1 rfile="${rfile#$pffdir/}" if [[ "$rfile" == pff/*/* ]]; then rfile="${rfile#pff/*/}" elif [[ "$rfile" == pff/* ]]; then return 1 fi echo "$rfile" } function pff_get_pfile() { # obtenir le chemin du fichier $1 exprimé par rapport au répertoire du # profil $2 dans le répertoire de projet $3 # retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou # pas dans un répertoire de profil) local pfile="$1" profile="$2" pffdir="$3" setx pfile=abspath "$pfile" [ "${pfile#$pffdir/}" != "$pfile" ] || return 1 pfile="${pfile#$pffdir/}" if [[ "$pfile" == pff/*/* ]]; then pfile="${pfile#pff/*/}" elif [[ "$pfile" == pff/* ]]; then return 1 fi echo "$pffdir/pff/$profile/$pfile" } function pff_get_bfile() { pff_get_pfile "$1" Base "$2"; } function pff_get_Cfile() { pff_get_pfile "$1" Common "$2"; } function pff_get_cfile() { pff_get_pfile "$1" Current "$2"; } function pff_get_vlfiles_nostrip() { # afficher tous les fichiers de version local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4" [ -d "$pffdir/pff/$profile" ] || return if [ -n "$version" ]; then if [ -n "$rfile" ]; then find "$pffdir/pff/$profile" \ -type f -path "$pffdir/pff/$profile/${rfile}__pv-${version}__" -o \ -type l -path "$pffdir/pff/$profile/${rfile}__pv-${version}__" else find "$pffdir/pff/$profile" \ -type f -name "*__pv-${version}__" -o \ -type l -name "*__pv-${version}__" fi else if [ -n "$rfile" ]; then find "$pffdir/pff/$profile" \ -type f -path "$pffdir/pff/$profile/${rfile}__pv-*__" -o \ -type l -path "$pffdir/pff/$profile/${rfile}__pv-*__" else find "$pffdir/pff/$profile" \ -type f -name "*__pv-*__" -o \ -type l -name "*__pv-*__" fi fi } function pff_get_vlfiles() { local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4" pff_get_vlfiles_nostrip "$@" | sed "s|^$pffdir/pff/$profile/||" } function pff_is_nomerge() { local file="$1" pffdir="$2" local nomerge rfile setx rfile=pff_get_rfile "$file" "$pffdir" setx file=basename -- "$rfile" # utilisé pour le match sur le nom du fichier for nomerge in "${NOMERGES[@]}"; do if [[ "$nomerge" == */* ]]; then # matcher sur le chemin relatif if eval "[[ $(qval "$rfile") == $(qwc "$nomerge") ]]"; then return 0 fi else # matcher uniquement sur le nom du fichier if eval "[[ $(qval "$file") == $(qwc "$nomerge") ]]"; then return 0 fi fi done return 1 } function pff_sync_vlfiles() { # synchroniser les fichiers de version $3..@ dans tous les répertoires de # profil, ou seulement le répertoire de profil $2 si la valeur n'est pas # vide. local pffdir="$1"; shift local profile="$1"; shift local -a profiles if [ -n "$profile" ]; then profiles=("$profile") else array_from_lines profiles "$(pff_get_user_profiles "$pffdir")" fi local vlfile rfile prefix pfile plink tmp for vlfile in "$@"; do rfile="${vlfile%__pv-*__}" for profile in "${profiles[@]}"; do prefix="$pffdir/pff/$profile" flexists "$prefix/$rfile" || continue pfile="$prefix/$vlfile" setx plink=multiups "$profile/$vlfile" "Base/$vlfile" if [ -L "$pfile" ]; then # correction éventuelle du lien existant setx tmp=readlink "$pfile" [ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile" else ln -sf "$plink" "$pfile" || return fi done done } function pff_select_profile() { # sélectionner le profil $1 dans le projet pff $2. créer d'abord le profil # s'il n'existe pas. local profile="$1" pffdir="$2" # créer le répertoire de profil si nécessaire mkdir -p "$pffdir/pff/$profile" || return 1 # mettre à jour les liens local -a lfiles; local lfile src dest setx -a lfiles=pff_get_local_files "$pffdir" for lfile in "${lfiles[@]}"; do src="$pffdir/pff/Current/$lfile" if [[ "$lfile" == */.pffdir ]]; then # répertoires entiers lfile="${lfile%/.pffdir}" src="${src%/.pffdir}" if [ -d "$pffdir/pff/$profile/$lfile" ]; then dest="$profile/$lfile" elif [ "$profile" != Common -a -d "$pffdir/pff/Common/$lfile" ]; then dest="Common/$lfile" else dest="Base/$lfile" fi elif [ -f "$pffdir/pff/$profile/$lfile" ]; then dest="$profile/$lfile" elif [ "$profile" != Common -a -f "$pffdir/pff/Common/$lfile" ]; then dest="Common/$lfile" else dest="Base/$lfile" fi setx dest=multiups "Current/$lfile" "$dest" [ -L "$src" ] || mkdirof "$src" ln -sfT "$dest" "$src" done # maj du lien "profil courant" ln -sfT "$profile" "$pffdir/pff/.Current" } function pff_autoinit() { # vérifications automatiques: créer les répertoires de base nécessaire au # fonctionnement de pff dans le projet pff $1 local pffdir="$1" profile mkdir [ -d "$pffdir/pff/Current" ] || mkdir -p "$pffdir/pff/Current" [ -d "$pffdir/pff/Base" ] || mkdir -p "$pffdir/pff/Base" # tous les fichiers du profil Base doivent être en lecture seule find "$pffdir/pff/Base" -type f -perm /222 -exec chmod a-w '{}' + # Créer les répertoires de MKDIRS for mkdir in "${MKDIRS[@]}"; do mkdir -p "$pffdir/$mkdir" done return 0 } function pff_autoselect() { # vérification automatiques: sélectionner le premier profil défini si aucun # profil n'est sélectionné dans le projet pff $1 local pffdir="$1" profile if [ ! -L "$pffdir/pff/.Current" ]; then setx profile=pff_get_first_profile "$pffdir" [ -n "$profile" ] || profile=Base enote "Autosélection du profil $profile" pff_select_profile "$profile" "$pffdir" fi } function pff_autofix() { pff_autoinit "$1" pff_autoselect "$1" }