nutools/lib/ulib/pff

326 lines
11 KiB
Bash

# -*- 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" files file dirs dir isald
files="$(find "$pffdir/pff/Base" -type f | sed "s|^$pffdir/pff/Base/||" | grep -v '/__pv-[^/_]*__[^/]*$')"
dirs="$(echo "$files" | grep '/\.pffdir$' | sed 's/\.pffdir$//')"
setx -a files=echo "$files"
setx -a dirs=echo "$dirs"
for file in "${files[@]}"; do
isald=
for dir in "${dirs[@]}"; do
if [ "${file#$dir}" != "$file" ]; then
isald=1
break
fi
done
[ -z "$isald" -o "$file" == "${dir}.pffdir" ] && echo "$file"
done
}
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"
}