1211 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1211 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| 
 | |
| SCRIPT_ALIASES=(
 | |
|     pfs:-s
 | |
|     pfa:-a
 | |
|     pfb:-b
 | |
|     pfe:-e
 | |
|     pfd:-d
 | |
| )
 | |
| 
 | |
| ORIGEXT=pff
 | |
| DEFAULT_ORIGEXTS=(".$ORIGEXT" .origine .default)
 | |
| PFFCONF=.pff.conf # ne pas modifier
 | |
| DEFAULT_PROTECTS=(/.git/ .svn/ /pff/ "/$PFFCONF")
 | |
| 
 | |
| PFFCONFVARS=(
 | |
|     "VERSION//Version actuellement installée"
 | |
|     -a
 | |
|     "PVERSIONS//Versions en attente d'intégration"
 | |
|     "PROFILES//Profils définis"
 | |
|     "ORIGEXTS=//Extensions origines"
 | |
| )
 | |
| 
 | |
| if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
 | |
|     # créer les liens
 | |
|     scriptname="$(basename -- "$0")"
 | |
|     for alias in "${SCRIPT_ALIASES[@]}"; do
 | |
|         alias="${alias%:*}"
 | |
|         ln -s "$scriptname" "$alias"
 | |
|     done
 | |
|     exit 0
 | |
| elif [ $# -eq 1 -a "$1" == --nutools-completion ]; then
 | |
|     echo '
 | |
| function __pff_profiles() {
 | |
|   local cwd="$(pwd)"
 | |
|   local pffdir="$cwd"
 | |
|   while true; do
 | |
|     if [ -f "$pffdir/'"$PFFCONF"'" -a -d "$pffdir/pff" ]; then
 | |
|       cd "$pffdir/pff"
 | |
|       /bin/ls -1d * | while read f; do
 | |
|         [ -d "$f" -a "$f" != Current ] || continue
 | |
|         f="$1$f"
 | |
|         if [[ "${f:0:2}" == -[eEn] ]]; then
 | |
|           echo -n -
 | |
|           echo "${f:1}"
 | |
|         else
 | |
|           echo "$f"
 | |
|         fi
 | |
|       done
 | |
|       cd "$cwd"
 | |
|       break
 | |
|     fi
 | |
|     [ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break
 | |
|     pffdir="$(dirname -- "$pffdir")"
 | |
|   done
 | |
| }
 | |
| function __pfs_completion() {
 | |
|   local cur
 | |
|   _get_comp_words_by_ref cur
 | |
|   COMPREPLY=($(compgen -W "$(__pff_profiles)" "$cur"))
 | |
| }
 | |
| complete -F __pfs_completion -o bashdefault -o default pfs
 | |
| function __pff_completion() {
 | |
|   local cur prev opt comp
 | |
|   _get_comp_words_by_ref cur prev
 | |
|   if [[ "$prev" == -*s ]]; then
 | |
|     COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
 | |
|   elif [[ "$cur" == -*s* ]]; then
 | |
|     comp="${cur#-*s}"; opt="${cur:0:$((${#cur}-${#comp}))}"
 | |
|     COMPREPLY=($(compgen -W "$(__pff_profiles "$opt")" -- "$cur"))
 | |
|   fi
 | |
| }
 | |
| complete -F __pff_completion -o bashdefault -o default pff
 | |
| '
 | |
|     exit 0
 | |
| fi
 | |
| 
 | |
| source "$(dirname "$0")/lib/ulib/ulib" || exit 1
 | |
| urequire DEFAULTS multiconf
 | |
| 
 | |
| function display_help() {
 | |
|     uecho "$scriptname: gestion de modifications locales
 | |
| 
 | |
| Un produit est distribué par un partenaire, et il faut maintenir certaines
 | |
| modifications locales tout en continuant d'accepter des modififications de
 | |
| l'upstream. Ce script aide à maintenir un tel scénario.
 | |
| 
 | |
| En général, la distribution upstream identifie les fichiers modifiables en leur
 | |
| donnant une extension particulière, par exemple 'file.origine'. On peut aussi
 | |
| décider de modifier des fichiers qui n'ont pas été prévus comme tels par
 | |
| l'upstream. On peut aussi avoir plusieurs ensembles de modifications, rassemblés
 | |
| en profils, e.g prod ou test pour les modifications à déployer en prod ou en
 | |
| test.
 | |
| 
 | |
| Terminologie: Les fichiers pour lesquels il faut maintenir une version locale
 | |
| sont appelés 'fichiers locaux', qu'ils viennent de la distribution upstream ou
 | |
| non. Les autres fichiers qui proviennent de la distribution sont appelés
 | |
| 'fichiers upstream'. Les fichiers livrés dans la distribution upstream avec une
 | |
| extension particulière pour indiquer qu'ils sont modifiables sont appelés
 | |
| 'fichiers origine'.
 | |
| 
 | |
| Les fichiers sont classés dans des profils spécifiques. Les profils reconnus
 | |
| sont:
 | |
| - Base est le profil des fichiers upstream non modifiés. Les fichiers origine
 | |
|   sont intégrés dans ce profil.
 | |
| - Common est le profil des fichiers upstream modifiés. Tous les fichiers
 | |
|   devraient être modifiés dans ce profil.
 | |
| - Les autres profils sont basés sur Common en priorité puis sur Base en cas de
 | |
|   non existence dans Common. Il peut s'agir de profils comme prod ou test, qui
 | |
|   contiennent des modifications spécifiques à différents cas d'utilisation.
 | |
| 
 | |
| USAGE
 | |
|     $scriptname [options]
 | |
| 
 | |
| COMMANDES / OPTIONS
 | |
| Les arguments du script dépendent de la commande utilisée. Les commandes
 | |
| supportées sont:
 | |
|     -0, --init [WORKDIR [ARCHIVE]]
 | |
|         Initialiser un répertoire pour le suivi des distributions upstream. Le
 | |
|         fichier $PFFCONF contient toutes les informations paramétrables sur la
 | |
|         gestion du projet.
 | |
|     --s, --origext .EXT
 | |
|         Ajouter une extension à la liste des extensions origines, c'est à dire
 | |
|         les fichiers identifiés dans la distribution upstream comme contenus
 | |
|         modifiables. Par défaut, les extensions suivantes sont reconnues:
 | |
|             ${DEFAULT_ORIGEXTS[*]}
 | |
|         Cette option peut être utilisée autant de fois que nécessaire.
 | |
|     --k, --clear-origexts
 | |
|         Supprimer la liste par défaut des extensions origines. Cette option doit
 | |
|         être spécifiée avant l'option --origext pour construire une nouvelle
 | |
|         liste. La liste des extensions ne doit pas être vide. Si c'est le cas,
 | |
|         elle est modifiée pour contenir l'unique élément (.$ORIGEXT)
 | |
| 
 | |
|     -N, --new ARCHIVE [WORKDIR]
 | |
|     -M, --new-only ARCHIVE [WORKDIR]
 | |
|         Intégrer une nouvelle distribution upstream, sous forme d'une archive ou
 | |
|         d'un répertoire. Les fichiers origine et les fichiers locaux sont placés
 | |
|         en attente d'intégration. Les autres sont intégrés sans modification.
 | |
|         La variante --new appelle automatiquement --patch après l'intégration de
 | |
|         l'archive.
 | |
|     -V, --version VERSION
 | |
|         Spécifier la version de l'archive qui est intégrée avec l'option --new.
 | |
|         Normalement, cette information est calculée automatiquement à partir du
 | |
|         nom de l'archive. Si la source est un répertoire ou une archive sans
 | |
|         numéro de version, cette option est requise.
 | |
|     -F, --full-archive
 | |
|         Intégrer une distribution complète. Par défaut, les archives avec
 | |
|         l'extension .war sont considérées commes des livraisons complètes. Si
 | |
|         la source est un répertoire ou une archive sans extension, l'une des
 | |
|         options -F ou -P est requise.
 | |
|     -P, --patch-archive
 | |
|         Intégrer un patch. Par défaut, les archives avec l'extension .zip sont
 | |
|         considérées comme des patches. Si la source est un répertoire ou une
 | |
|         archive sans extension, l'une des options -F ou -P est requise.
 | |
|         Avec une distribution de type patch, on considère que les fichiers
 | |
|         livrés ne sont pas des fichiers origines. Il n'y a donc aucun traitement
 | |
|         particulier: l'archive est simplement intégrée telle quelle.
 | |
|     --auto-unwrap
 | |
|         Si l'archive ne contient qu'un seul répertoire à la racine nommé d'après
 | |
|         le nom de base de l'archive, c'est le contenu de ce répertoire qui est
 | |
|         considéré. C'est l'option par défaut.
 | |
|         Le test est effectué avec et sans la version. Par exemple, si l'archive
 | |
|         est product-x.y.zip, et qu'elle contient un unique répertoire nommé
 | |
|         'product-x.y' ou 'product' alors intégrer le contenu de ce répertoire.
 | |
|     -E, --unwrap
 | |
|         Si l'archive ne contient qu'un seul répertoire à la racine, intégrer
 | |
|         inconditionellement le contenu de se répertoire.
 | |
|     --no-unwrap
 | |
|         Intégrer tel quel le contenu de l'archive.
 | |
| 
 | |
|     -p, --patch [WORKDIR]
 | |
|         Intégrer les modifications entrantes sur les fichiers nouvellement
 | |
|         arrivés via l'option --new
 | |
| 
 | |
|     -a, --add-global FILES...
 | |
|         Ajouter/Identifier un fichier comme un fichier local pour tous les
 | |
|         profils. Le fichier est copié dans le profil Base.
 | |
| 
 | |
|     --locals [WORKDIR]
 | |
|         Lister les fichiers locaux
 | |
| 
 | |
|     --profiles [WORKDIR]
 | |
|         Lister les profils valides
 | |
| 
 | |
|     -s, --switch PROFILE [WORKDIR]
 | |
|         Basculer le profil courant
 | |
| 
 | |
|     -b, --add-local FILES...
 | |
|         Ajouter/Identifier un fichier comme un fichier local et le rendre
 | |
|         spécifique au profil courant.
 | |
|     -e, --edit FILES...
 | |
|         Editer des fichiers, implique --add-local
 | |
| 
 | |
|     -d, --diff [DESTP [WORKDIR]]
 | |
|     -d, --diff [SRCP DESTP [WORKDIR]]
 | |
|         Afficher la différence entre entre deux profils. Avec la première
 | |
|         syntaxe, comparer le profil courant au profil DESTP. Avec la deuxième
 | |
|         syntaxe, comparer le profil SRCP au DESTP.
 | |
|     -l, --list-names
 | |
|         N'afficher que les noms des fichiers qui sont différents
 | |
| 
 | |
|     --infos [WORKDIR]
 | |
|         Afficher des informations sur le projet courant: profils, fichiers
 | |
|         locaux, profil courant, etc. C'est la commande par défaut.
 | |
|         Les fichiers locaux sont taggés avec les valeurs suivantes
 | |
|           ${COULEUR_ROUGE}P${COULEUR_NORMALE} il existe un patch pour ce fichier dans le profil courant
 | |
|           ${COULEUR_BLEUE}*${COULEUR_NORMALE} ce fichier local est spécifique à ce profil
 | |
|           $(get_color YELLOW)C${COULEUR_NORMALE} ce fichier local est spécifique au profil Common
 | |
|     -l, --show-all
 | |
|         Afficher tous les fichiers locaux au lieu de se contenter des fichiers
 | |
|         modifiés dans le profil courant."
 | |
| }
 | |
| 
 | |
| # 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 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/$PFFCONF" -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 "${PFFCONFVARS[@]}"
 | |
|         source "$pffdir/$PFFCONF"
 | |
|         local "$destvar"; upvar "$destvar" "$pffdir"
 | |
|         return
 | |
|     fi
 | |
|     local msg="Projet pff introuvable (utiliser --init ?)"
 | |
|     [ -n "$2" ] && die "$2: $msg" || die "$msg"
 | |
| }
 | |
| 
 | |
| function 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 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 get_first_profile() {
 | |
|     # afficher le premier profil autre que Base du projet pff $1
 | |
|     get_profiles "$@" | grep -vxF Base | head -n1
 | |
| }
 | |
| 
 | |
| function 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 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 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 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 get_bfile() { get_pfile "$1" Base "$2"; }
 | |
| function get_Cfile() { get_pfile "$1" Common "$2"; }
 | |
| function get_cfile() { get_pfile "$1" Current "$2"; }
 | |
| 
 | |
| function get_vlfiles() {
 | |
|     # afficher tous les fichiers de version
 | |
|     local pffdir="$1" rfile="$2" profile="${3:-Base}"
 | |
|     [ -d "$pffdir/pff/$profile" ] || return
 | |
|     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-*__" \
 | |
|             | sed "s|^$pffdir/pff/$profile/||"
 | |
|     else
 | |
|         find "$pffdir/pff/$profile" \
 | |
|              -type f -name "*__pv-*__" -o \
 | |
|              -type l -name "*__pv-*__" \
 | |
|             | sed "s|^$pffdir/pff/$profile/||"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| function 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 "$(get_profiles "$pffdir" | grep -vxF Base)"
 | |
|     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 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=get_local_files "$pffdir"
 | |
|     for lfile in "${lfiles[@]}"; do
 | |
|         src="$pffdir/pff/Current/$lfile"
 | |
|         if [ -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 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
 | |
|     [ -d "$pffdir/pff/Current" ] || mkdir -p "$pffdir/pff/Current"
 | |
|     [ -d "$pffdir/pff/Base" ] || mkdir -p "$pffdir/pff/Base"
 | |
| }
 | |
| 
 | |
| function 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=get_first_profile "$pffdir"
 | |
|         [ -n "$profile" ] || profile=Base
 | |
|         enote "Autosélection du profil $profile"
 | |
|         select_profile "$profile" "$pffdir"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| function autofix() {
 | |
|     autoinit "$1"
 | |
|     autoselect "$1"
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| # Commandes
 | |
| 
 | |
| #===========================================================
 | |
| # pff --init
 | |
| 
 | |
| function init_cmd() {
 | |
|     local workdir="$1" archive="$2"
 | |
|     local pffdir
 | |
| 
 | |
|     if [ -z "$workdir" ]; then
 | |
|         estep "Initialisation d'un nouveau projet pff"
 | |
|         read_value "Veuillez entrer le répertoire du projet" workdir
 | |
|     fi
 | |
|     if find_pffdir pffdir "$workdir"; then
 | |
|         enote "$(ppath "$pffdir"): est déjà un projet pff"
 | |
|         if ask_yesno "Voulez-vous vérifier la configuration et la mettre à jour?" O; then
 | |
|             conf_init "${PFFCONFVARS[@]}"
 | |
|             source "$pffdir/$PFFCONF"
 | |
|             conf_upgrade "$workdir/$PFFCONF"
 | |
|             conf_update -n "$workdir/$PFFCONF" ORIGEXTS
 | |
|         fi
 | |
|         return 0
 | |
|     fi
 | |
|     if [ ! -d "$workdir" ]; then
 | |
|         ewarn "$workdir: ce répertoire n'existe pas"
 | |
|         ask_yesno "Voulez-vous le créer?" O || return 1
 | |
|         mkdir -p "$workdir" || return 1
 | |
|     fi
 | |
|     enote "Initialisation d'un nouveau projet pff dans $workdir"
 | |
|     # fichier de configuration
 | |
|     conf_upgrade "$workdir/$PFFCONF" "${PFFCONFVARS[@]}"
 | |
|     if [ "${ORIGEXTS[*]}" != "${DEFAULT_ORIGEXTS[*]}" ]; then
 | |
|         conf_update "$workdir/$PFFCONF" ORIGEXTS
 | |
|     fi
 | |
|     # répertoire pff
 | |
|     mkdir -p "$workdir/pff"
 | |
|     [ -f "$workdir/pff/.gitignore" ] || echo >"$workdir/pff/.gitignore" "\
 | |
| /Current/
 | |
| /.Current"
 | |
|     autofix "$workdir"
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --new
 | |
| 
 | |
| function new__prepare_archive() {
 | |
|     # préparer l'archive fournie en la copiant dans un répertoire temporaire
 | |
|     # initialiser la variable $1(=srcdir) avec le chemin du répertoire résultat
 | |
|     local destvar="${1:-srcdir}"; shift
 | |
|     local destver="${1:-version}"; shift
 | |
|     local archive="$1" version="$2" disttype="$3" unwrap="$4"
 | |
| 
 | |
|     local srcdir
 | |
|     if [ -f "$archive" ]; then
 | |
|         is_archive "$archive" || die "$archive: doit être un fichier archive"
 | |
|         [ -n "$version" ] || version="$(get_archive_version "$archive")"
 | |
|         archive="$(abspath "$archive")"
 | |
|     elif [ -d "$archive" ]; then
 | |
|         srcdir="$(abspath "$archive")"
 | |
|         archive=
 | |
|     else
 | |
|         die "$archive: fichier introuvable"
 | |
|     fi
 | |
| 
 | |
|     read_value "Entrez un identifiant pour cette version" version "$version" || die
 | |
| 
 | |
|     local tmpd
 | |
|     if [ -n "$archive" ]; then
 | |
|         if [ "$disttype" == auto ]; then
 | |
|             case "$archive" in
 | |
|             *.war)
 | |
|                 enote "Auto-sélection du type distribution complète sur la base de l'extension .war"
 | |
|                 disttype=full
 | |
|                 ;;
 | |
|             *.zip)
 | |
|                 enote "Auto-sélection du type distribution de patch sur la base de l'extension .zip"
 | |
|                 disttype=patch
 | |
|                 ;;
 | |
|             *)
 | |
|                 die "L'extension de l'archive n'est pas reconnue. Vous devez spécifier l'une des options --full-archive ou --patch-archive"
 | |
|                 ;;
 | |
|             esac
 | |
|         fi
 | |
| 
 | |
|         ac_set_tmpdir tmpd
 | |
|         estep "Extraction de $(ppath "$archive" "$cwd")"
 | |
|         extract_archive "$archive" "$tmpd" || die
 | |
|         srcdir="$tmpd"
 | |
| 
 | |
|         if [ -n "$unwrap" ]; then
 | |
|             local -a files
 | |
|             array_lsall files "$srcdir" "*" ".*"
 | |
|             if [ "${#files[*]}" -eq 1 ]; then
 | |
|                 local file="${files[0]}"
 | |
|                 if [ "$unwrap" == auto ]; then
 | |
|                     # nom de l'archive avec la version
 | |
|                     local banv="$(get_archive_basename "$archive")"
 | |
|                     # nom de l'archive sans la version
 | |
|                     local ban="${banv%$(get_archive_versionsuffix "$archive")}"
 | |
|                     local filename="$(basename "$file")"
 | |
|                     [ "$filename" == "$banv" -o "$filename" == "$ban" ] || unwrap=
 | |
|                 fi
 | |
|                 [ -n "$unwrap" -a -d "$file" ] && srcdir="$file"
 | |
|             fi
 | |
|         fi
 | |
|     elif [ -d "$srcdir" ]; then
 | |
|         if [ "$disttype" == auto ]; then
 | |
|             die "La source est un répertoire. Vous devez spécifier l'une des option --full-archive ou --patch-archive"
 | |
|         fi
 | |
| 
 | |
|         ac_set_tmpdir tmpd
 | |
|         estep "Création d'une copie de travail de $(ppath "$srcdir" "$cwd")"
 | |
|         rsync ${QUIET:+-q} -a "$srcdir/" "$tmpd" || die
 | |
|         srcdir="$tmpd"
 | |
|     fi
 | |
| 
 | |
|     local "$destvar"; upvar "$destvar" "$srcdir"
 | |
|     local "$destver"; upvar "$destver" "$version"
 | |
| }
 | |
| 
 | |
| function new_cmd() {
 | |
|     local autopatch="$1" version="$2" disttype="$3" unwrap="$4"; shift; shift; shift; shift
 | |
|     local archive="$1" pffdir="$2"
 | |
| 
 | |
|     ensure_pffdir pffdir "$pffdir"
 | |
| 
 | |
|     ## préparer la source: les fichiers entrants
 | |
|     local srcdir full
 | |
|     new__prepare_archive srcdir version "$archive" "$version" "$disttype" "$unwrap"
 | |
|     [ "$disttype" == full ] && full=1
 | |
| 
 | |
|     ## lister les fichiers dans la source et la destination
 | |
|     local workdir
 | |
|     ac_set_tmpdir workdir
 | |
| 
 | |
|     # fichiers sources
 | |
|     local fnsrc0="$workdir/nsrc0" fosrc0="$workdir/osrc0"
 | |
|     >"$fnsrc0"
 | |
|     >"$fosrc0"
 | |
|     find "$srcdir" -type f | awkrun ORIGEXTS[@] prefix="$srcdir/" fnsrc="$fnsrc0" fosrc="$fosrc0" '{
 | |
|   found = 0
 | |
|   for (i = 1; i <= ORIGEXTS_count; i++) {
 | |
|     sub("^" prefix, "")
 | |
|     if ($0 ~ ORIGEXTS[i] "(/|$)") {
 | |
|       print >fosrc
 | |
|       found = 1
 | |
|       break
 | |
|     }
 | |
|   }
 | |
|   if (!found) {
 | |
|     print >fnsrc
 | |
|   }
 | |
| }'
 | |
|     # fichiers destination
 | |
|     local -a find; local p
 | |
|     local fndest0="$workdir/ndest0" fldest0="$workdir/ldest0"
 | |
|     get_local_files "$pffdir" >"$fldest0"
 | |
|     for p in "${PROTECTS[@]}"; do
 | |
|         [ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
 | |
|         if [ "${p#/}" != "$p" ]; then
 | |
|             # les fichiers à la racine
 | |
|             p="${p#/}"
 | |
|             if [ "${p%/}" != "$p" ]; then
 | |
|                 find=("${find[@]}" -type d -path "$pffdir/${p%/}" -prune)
 | |
|             else
 | |
|                 find=("${find[@]}" -type f -path "$pffdir/$p" -o -type l -path "$pffdir/$p")
 | |
|             fi
 | |
|         else
 | |
|             # fichiers n'importe où dans l'arborescence
 | |
|             if [ "${p%/}" != "$p" ]; then
 | |
|                 find=("${find[@]}" -type d -name "${p%/}" -prune)
 | |
|             else
 | |
|                 find=("${find[@]}" -type f -name "$p" -o -type l -name "$p")
 | |
|             fi
 | |
|         fi
 | |
|     done
 | |
|     [ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
 | |
|     find=(find "$pffdir" "${find[@]}" -type f -print -o -type l -print)
 | |
|     "${find[@]}" | sed "s|^$pffdir/||" | grep -vxf "$fldest0" >"$fndest0"
 | |
| 
 | |
|     ## Traiter les fichiers normaux
 | |
| 
 | |
|     local fnsrc="$workdir/nsrc" fndest="$workdir/ndest"
 | |
|     grep -vxf "$fldest0" "$fnsrc0" | csort >"$fnsrc"
 | |
|     csort "$fndest0" >"$fndest"
 | |
| 
 | |
|     local fcreates="$workdir/creates" fdeletes="$workdir/deletes" fcds="$workdir/cds" fupdates="$workdir/updates"
 | |
|     >"$fcreates"
 | |
|     >"$fdeletes"
 | |
|     diff "$fnsrc" "$fndest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
 | |
|   c = substr($0, 1, 1)
 | |
|   f = substr($0, 3)
 | |
|   if (c == "<") print f >fcreates
 | |
|   else if (c == ">") print f >fdeletes
 | |
| }'
 | |
|     cat "$fcreates" "$fdeletes" >"$fcds"
 | |
|     grep -vxf "$fcds" "$fnsrc" >"$fupdates"
 | |
|     local -a creates updates deletes r i have_creates have_updates have_deletes src dest
 | |
|     array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
 | |
|     array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
 | |
|     array_from_lines deletes "$(<"$fdeletes")"; [ ${#deletes[*]} -gt 0 ] && have_deletes=1 || have_deletes=
 | |
|     enote "Fichiers normaux: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)${full:+", ${#deletes[*]} à supprimer"}"
 | |
|     ask_any -i "Voulez-vous continuer?" +Od; r=$?
 | |
|     if [ $r == 2 ]; then
 | |
|         for i in "${updates[@]}"; do
 | |
|             echo " $i"
 | |
|         done
 | |
|         for i in "${creates[@]}"; do
 | |
|             echo "+$i"
 | |
|         done
 | |
|         if [ -n "$full" ]; then
 | |
|             for i in "${deletes[@]}"; do
 | |
|                 echo "-$i"
 | |
|             done
 | |
|         fi
 | |
|         ask_yesno "Voulez-vous continuer?" O || return 1
 | |
|     elif [ $r == 1 ]; then
 | |
|         return 1
 | |
|     fi
 | |
| 
 | |
|     if [ -n "$have_creates" -o -n "$have_updates" -o -n "$have_deletes" ]; then
 | |
|         ebegin "Copie des fichiers"
 | |
|         r=0
 | |
|         for i in "${creates[@]}"; do
 | |
|             mkdirof "$pffdir/$i"; r=$?
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|             cp "$srcdir/$i" "$pffdir/$i"; r=$?
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|         done
 | |
|         [ $r == 0 ] || { eend $r; return 1; }
 | |
|         for i in "${updates[@]}"; do
 | |
|             src="$srcdir/$i"
 | |
|             dest="$pffdir/$i"
 | |
|             if diff -q "$src" "$dest" >&/dev/null; then
 | |
|                 # pas la peine de copier si les fichiers sont identiques
 | |
|                 show_debug && edot 0 "$i: non modifié"
 | |
|                 continue
 | |
|             fi
 | |
|             cat "$src" >"$dest"; r=$?
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|         done
 | |
|         [ $r == 0 ] || { eend $r; return 1; }
 | |
|         if [ -n "$full" ]; then
 | |
|             # ne faire les suppression qu'en mode full
 | |
|             for i in "${deletes[@]}"; do
 | |
|                 rm "$pffdir/$i"; r=$?
 | |
|                 edot $r "$i"; [ $r == 0 ] || break
 | |
|             done
 | |
|         fi
 | |
|         eend $r
 | |
|     fi
 | |
| 
 | |
|     ## Traiter les fichiers origines
 | |
| 
 | |
|     local ftmp="$workdir/tmp"
 | |
|     local fosrc="$workdir/osrc" flosrc="$workdir/losrc" flsrc="$workdir/lsrc" fldest="$workdir/ldest"
 | |
|     csort "$fosrc0" >"$fosrc"
 | |
|     >"$flsrc"
 | |
|     >"$flosrc"
 | |
|     awkrun <"$fosrc0" ORIGEXTS[@] flsrc="$flsrc" flosrc="$flosrc" '{
 | |
|   for (i = 1; i <= ORIGEXTS_count; i++) {
 | |
|     if ($0 ~ ORIGEXTS[i] "(/|$)") {
 | |
|       orig = $0
 | |
|       local = gensub(ORIGEXTS[i] "(/|$)", "\\1", 1, $0)
 | |
|       print local ":" orig >flosrc
 | |
|       print local >flsrc
 | |
|       break
 | |
|     }
 | |
|   }
 | |
| }'
 | |
|     csort "$flsrc" >"$ftmp"; cat "$ftmp" >"$flsrc"
 | |
|     csort "$fldest0" >"$fldest"
 | |
| 
 | |
|     local losrc lsrc osrc; local -a tmpa; declare -A losrcs
 | |
|     array_from_lines tmpa "$(<"$flosrc")"
 | |
|     for losrc in "${tmpa[@]}"; do
 | |
|         splitpair "$losrc" lsrc osrc
 | |
|         losrcs["$lsrc"]="$osrc"
 | |
|     done
 | |
| 
 | |
|     >"$fcreates"
 | |
|     >"$fdeletes"
 | |
|     diff "$flsrc" "$fldest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
 | |
|   c = substr($0, 1, 1)
 | |
|   f = substr($0, 3)
 | |
|   if (c == "<") print f >fcreates
 | |
|   else if (c == ">") print f >fdeletes
 | |
| }'
 | |
|     # contrairement aux fichiers normaux, ajouter le contenu de fdeletes à fupdates
 | |
|     # les fichiers de fdeletes sont des fichiers locaux non identifiés comme tels dans l'origine
 | |
|     { grep -vxf "$fcreates" "$flsrc"; cat "$fdeletes"; } >"$fupdates"
 | |
|     array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
 | |
|     array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
 | |
|     enote "Fichiers origines: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)"
 | |
|     ask_any -i "Voulez-vous continuer?" +Od; r=$?
 | |
|     if [ $r == 2 ]; then
 | |
|         for i in "${updates[@]}"; do
 | |
|             echo " $i"
 | |
|         done
 | |
|         for i in "${creates[@]}"; do
 | |
|             echo "+$i"
 | |
|         done
 | |
|         ask_yesno "Voulez-vous continuer?" O || return 1
 | |
|     elif [ $r == 1 ]; then
 | |
|         return 1
 | |
|     fi
 | |
| 
 | |
|     local vlfile; local -a vlfiles
 | |
|     if [ -n "$have_creates" -o -n "$have_updates" ]; then
 | |
|         ebegin "Copie des fichiers"
 | |
|         r=0
 | |
|         for i in "${creates[@]}"; do
 | |
|             src="$srcdir/${losrcs[$i]}"
 | |
|             dest="$pffdir/$i"
 | |
|             mkdirof "$dest"; r=$?
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|             cp "$src" "$dest"; r=$?
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|             add_global__link "$dest" "$pffdir"
 | |
|             edot $r "$i"; [ $r == 0 ] || break
 | |
|         done
 | |
|         [ $r == 0 ] || { eend $r; return 1; }
 | |
|         for i in "${updates[@]}"; do
 | |
|             # du fait qu'on intègre fdeletes dans fupdates, il est possible que
 | |
|             # le fichier n'existe pas dans la source. de plus, l'association
 | |
|             # dans losrcs n'existe pas non plus. dans ce cas, il suffit de
 | |
|             # d'ignorer le fichier
 | |
|             if [ -n "${losrcs[$i]+set}" ]; then
 | |
|                 src="$srcdir/${losrcs[$i]}"
 | |
|             else
 | |
|                 src="$srcdir/$i"
 | |
|             fi
 | |
|             vlfile="${i}__pv-${version}__"
 | |
|             dest="$pffdir/pff/Base/$vlfile"
 | |
|             if [ -e "$src" -o -L "$src" ]; then
 | |
|                 mkdirof "$dest"; r=$?
 | |
|                 edot $r "$i"; [ $r == 0 ] || break
 | |
|                 cp "$src" "$dest"; r=$?
 | |
|                 edot $r "$i"; [ $r == 0 ] || break
 | |
|                 array_add vlfiles "$vlfile"
 | |
|             fi
 | |
|         done
 | |
|         [ $r == 0 ] || { eend $r; return 1; }
 | |
|         eend $r
 | |
|     fi
 | |
| 
 | |
|     sync_vlfiles "$pffdir" "" "${vlfiles[@]}"
 | |
|     array_addu PVERSIONS "$version"
 | |
|     conf_update "$pffdir/$PFFCONF" PVERSIONS
 | |
| 
 | |
|     ## Fin du traitement
 | |
|     ac_clean "$srcdir" "$workdir"
 | |
| 
 | |
|     if [ -n "$autopatch" ]; then
 | |
|         # lancer la commande patch pour intégrer les modifications
 | |
|         patch_cmd "$pffdir"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --patch
 | |
| 
 | |
| function patch_cmd() {
 | |
|     local pffdir="$1"
 | |
| 
 | |
|     ensure_pffdir pffdir "$pffdir"
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --add-global
 | |
| 
 | |
| function add_global__link() {
 | |
|     local pfile="$1" pffdir="$2"
 | |
|     local rfile bfile cfile plink clink tmp
 | |
| 
 | |
|     setx rfile=get_rfile "$pfile" "$pffdir" || return
 | |
|     flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
 | |
| 
 | |
|     setx bfile=get_bfile "$pfile" "$pffdir"
 | |
|     setx cfile=get_cfile "$pfile" "$pffdir"
 | |
|     setx plink=multiups "$rfile" "pff/Current/$rfile"
 | |
|     setx clink=multiups "Current/$rfile" "Base/$rfile"
 | |
|     if flexists "$bfile" && [ -L "$pfile" -a -L "$cfile" ]; then
 | |
|         : # ok pour les liens
 | |
|     else
 | |
|         # Création des liens pour $rfile
 | |
|         mkdirof "$bfile" || return
 | |
|         mv "$pfile" "$bfile" || return
 | |
|         [ -L "$pfile" ] || ln -sf "$plink" "$pfile" || return
 | |
|         mkdirof "$cfile" || return
 | |
|         [ -L "$cfile" ] || ln -sf "$clink" "$cfile" || return
 | |
|     fi
 | |
|     # correction éventuelle du lien existant
 | |
|     setx tmp=readlink "$pfile"
 | |
|     [ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile"
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| function add_global_cmd() {
 | |
|     local pffdir file pfile
 | |
| 
 | |
|     ensure_pffdir pffdir
 | |
|     for file in "$@"; do
 | |
|         if [ -d "$file" ]; then
 | |
|             ewarn "$file: est un répertoire. argument ignoré"
 | |
|             continue
 | |
|         elif flexists "$file"; then
 | |
|             :
 | |
|         else
 | |
|             ewarn "$file: fichier introuvable"
 | |
|             ask_yesno "Voulez-vous le créer?" O || continue
 | |
|         fi
 | |
|         setx pfile=abspath "$file"
 | |
|         setx rfile=get_rfile "$pfile" || {
 | |
|             eerror "$file: chemin invalide. argument ignoré"
 | |
|             continue
 | |
|         }
 | |
|         add_global__link "$pfile" "$pffdir" || return
 | |
|     done
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --locals
 | |
| 
 | |
| function list_locals_cmd() {
 | |
|     local pffdir="$1"
 | |
| 
 | |
|     ensure_pffdir pffdir "$pffdir"
 | |
|     get_local_files "$pffdir"
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --profiles
 | |
| 
 | |
| function list_profiles_cmd() {
 | |
|     local pffdir="$1"
 | |
| 
 | |
|     ensure_pffdir pffdir "$pffdir"
 | |
|     get_profiles "$pffdir"
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --switch
 | |
| 
 | |
| function switch_cmd() {
 | |
|     local profile="$1" pffdir="$2"
 | |
|     local -a profiles
 | |
| 
 | |
|     [ -n "$profile" ] || profile=Base
 | |
| 
 | |
|     ensure_pffdir pffdir "$pffdir"
 | |
|     autoinit "$pffdir"
 | |
|     setx -a profiles=get_profiles "$pffdir"
 | |
|     if ! array_contains profiles "$profile"; then
 | |
|         if ! array_contains PROFILES "$profile"; then
 | |
|             ewarn "$profile: ce profil n'existe pas"
 | |
|             ask_yesno "Voulez-vous le créer?" O || return 1
 | |
| 
 | |
|             array_addu PROFILES "$profile"
 | |
|             conf_update "$pffdir/$PFFCONF" PROFILES
 | |
|         fi
 | |
|     fi
 | |
|     enote "Sélection du profil $profile"
 | |
|     select_profile "$profile" "$pffdir"
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --add-local
 | |
| 
 | |
| function add_local__link() {
 | |
|     local pfile="$1" profile="$2" pffdir="$3"
 | |
|     local rfile bfile pfile Pfile plink Plink
 | |
|     # vérifier validité du chemin
 | |
|     setx rfile=get_rfile "$pfile" "$pffdir" || return
 | |
|     flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
 | |
|     # vérifier la présence du lien global
 | |
|     setx bfile=get_bfile "$pfile" "$pffdir"
 | |
|     if ! flexists "$bfile"; then
 | |
|         add_global__link "$pfile" "$pffdir" || return
 | |
|     fi
 | |
|     # vérifier la présence du lien local
 | |
|     setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
 | |
|     flexists "$Pfile" && return 0
 | |
|     # créer le fichier local
 | |
|     setx Cfile=get_Cfile "$pfile" "$pffdir"
 | |
|     mkdirof "$Pfile"
 | |
|     if flexists "$Cfile"; then
 | |
|         # copier depuis le profil Common par défaut
 | |
|         cp "$Cfile" "$Pfile"
 | |
|     else
 | |
|         cp "$bfile" "$Pfile"
 | |
|     fi
 | |
|     # mettre à jour le profil courant
 | |
|     setx cfile=get_cfile "$pfile" "$pffdir"
 | |
|     setx clink=multiups "Current/$rfile" "$profile/$rfile"
 | |
|     ln -sf "$clink" "$cfile"
 | |
|     # intégrer les fichiers de version
 | |
|     local -a vlfiles
 | |
|     array_from_lines vlfiles "$(get_vlfiles "$pffdir" "$rfile")"
 | |
|     sync_vlfiles "$pffdir" "$profile" "${vlfiles[@]}"
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| function add_local_cmd() {
 | |
|     local pffdir file rfile pfile
 | |
|     local profile r
 | |
| 
 | |
|     ensure_pffdir pffdir
 | |
|     setx profile=get_current_profile "$pffdir"
 | |
|     r=0
 | |
|     for file in "$@"; do
 | |
|         if [ -d "$file" ]; then
 | |
|             ewarn "$file: est un répertoire. argument ignoré"
 | |
|             continue
 | |
|         elif flexists "$file"; then
 | |
|             :
 | |
|         else
 | |
|             ewarn "$file: fichier introuvable"
 | |
|             ask_yesno "Voulez-vous le créer?" O || continue
 | |
|         fi
 | |
|         setx pfile=abspath "$file"
 | |
|         setx rfile=get_rfile "$pfile" || {
 | |
|             eerror "$file: chemin invalide. argument ignoré"
 | |
|             r=1
 | |
|             continue
 | |
|         }
 | |
|         add_local__link "$pfile" "$profile" "$pffdir" || {
 | |
|             ewarn "$file: erreur lors de la création du lien local"
 | |
|             r=1
 | |
|             continue
 | |
|         }
 | |
|     done
 | |
|     return $r
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --edit
 | |
| 
 | |
| function edit_cmd() {
 | |
|     local pffdir file rfile pfile Pfile
 | |
|     local profile r
 | |
|     local -a edits
 | |
| 
 | |
|     ensure_pffdir pffdir
 | |
|     setx profile=get_current_profile "$pffdir"
 | |
|     enote "Edition des fichiers dans le profil $profile"
 | |
| 
 | |
|     r=0
 | |
|     for file in "$@"; do
 | |
|         if [ -d "$file" ]; then
 | |
|             ewarn "$file: est un répertoire. argument ignoré"
 | |
|             continue
 | |
|         fi
 | |
|         setx pfile=abspath "$file"
 | |
|         setx rfile=get_rfile "$pfile" || {
 | |
|             eerror "$file: chemin invalide. argument ignoré"
 | |
|             r=1
 | |
|             continue
 | |
|         }
 | |
|         add_local__link "$pfile" "$profile" "$pffdir" || {
 | |
|             ewarn "$file: erreur lors de la création du lien local"
 | |
|             r=1
 | |
|             continue
 | |
|         }
 | |
|         setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
 | |
|         array_add edits "$Pfile"
 | |
|     done
 | |
| 
 | |
|     "${EDITOR:-vi}" "${edits[@]}" || r=$?
 | |
|     return $r
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --diff
 | |
| 
 | |
| function diff_cmd() {
 | |
|     local list_names="$1"; shift
 | |
| 
 | |
|     local pffdir profile srcp destp desc
 | |
|     case $# in
 | |
|     0)
 | |
|         ensure_pffdir pffdir
 | |
|         setx srcp=get_current_profile "$pffdir"
 | |
|         destp=
 | |
|         ;;
 | |
|     1)
 | |
|         if withpath "$1"; then
 | |
|             ensure_pffdir pffdir "$1"
 | |
|             setx srcp=get_current_profile "$pffdir"
 | |
|             destp=
 | |
|         else
 | |
|             ensure_pffdir pffdir
 | |
|             setx srcp=get_current_profile "$pffdir"
 | |
|             destp="$1"
 | |
|         fi
 | |
|         ;;
 | |
|     2)
 | |
|         if withpath "$2"; then
 | |
|             ensure_pffdir pffdir "$2"
 | |
|             setx srcp=get_current_profile "$pffdir"
 | |
|             destp="$1"
 | |
|         else
 | |
|             ensure_pffdir pffdir
 | |
|             srcp="$1"
 | |
|             destp="$2"
 | |
|         fi
 | |
|         ;;
 | |
|     *)
 | |
|         ensure_pffdir pffdir "$3"
 | |
|         srcp="$1"
 | |
|         destp="$2"
 | |
|         ;;
 | |
|     esac
 | |
|     [ "$destp" == "Common|Base" ] && destp=
 | |
|     if [ -z "$destp" ]; then
 | |
|         if [ "$srcp" == Base -o "$srcp" == Common ]; then
 | |
|             desc="pff --diff $srcp Base"
 | |
|         else
 | |
|             desc="pff --diff $srcp ${destp:-"Common|Base"}"
 | |
|         fi
 | |
|     else
 | |
|         desc="pff --diff $srcp $destp"
 | |
|     fi
 | |
| 
 | |
|     local -a lfiles; local rfile bfile Cfile srcfile destfile
 | |
|     setx -a lfiles=get_local_files "$pffdir"
 | |
| 
 | |
|     for rfile in "${lfiles[@]}"; do
 | |
|         setx srcfile=get_pfile "$pffdir/$rfile" "$srcp" "$pffdir"
 | |
|         flexists "$srcfile" || continue
 | |
|         if [ -n "$destp" ]; then
 | |
|             setx destfile=get_pfile "$pffdir/$rfile" "$destp" "$pffdir"
 | |
|         else
 | |
|             setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
 | |
|             setx bfile=get_bfile "$pffdir/$rfile" "$pffdir"
 | |
|             # si on précise pas le profil destination, sélectionner dans l'ordre
 | |
|             # Common ou Base
 | |
|             if [ "$srcp" == Base -o "$srcp" == Common ]; then
 | |
|                 destfile="$bfile"
 | |
|             elif flexists "$Cfile"; then
 | |
|                 destfile="$Cfile"
 | |
|             else
 | |
|                 destfile="$bfile"
 | |
|             fi
 | |
|         fi
 | |
|         if [ -n "$list_names" ]; then
 | |
|             diff -q "$srcfile" "$destfile" >&/dev/null || echo "$srcfile"
 | |
|         else
 | |
|             [ -n "$desc" ] && echo "$desc"
 | |
|             desc=
 | |
|             diff -ur "$srcfile" "$destfile"
 | |
|         fi
 | |
|     done | page_maybe
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # pff --infos
 | |
| 
 | |
| function infos_cmd() {
 | |
|     local show_all="$1"; shift
 | |
|     local pffdir="$1"
 | |
|     local -a profiles vlfiles
 | |
|     local rfile Pfile flag
 | |
| 
 | |
|     if find_pffdir pffdir "$pffdir"; then
 | |
|         ensure_pffdir pffdir "$pffdir"
 | |
|         autofix "$pffdir"
 | |
|         setx -a lfiles=get_local_files "$pffdir"
 | |
|         setx profile=get_current_profile "$pffdir"
 | |
|         setx -a profiles=get_profiles "$pffdir"
 | |
| 
 | |
|         if [ ${#lfiles[*]} -gt 0 ]; then
 | |
|             [ ${#lfiles[*]} -gt 1 ] && estep "${#lfiles[*]} fichiers locaux" || estep "1 fichier local"
 | |
|             for rfile in "${lfiles[@]}"; do
 | |
|                 setx -a vlfiles=get_vlfiles "$pffdir" "$rfile" "$profile"
 | |
|                 setx Pfile=get_pfile "$pffdir/$rfile" "$profile" "$pffdir"
 | |
|                 setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
 | |
|                 if [ ${#vlfiles[*]} -gt 0 ]; then
 | |
|                     flag="${COULEUR_ROUGE}P${COULEUR_NORMALE} "
 | |
|                 elif [ -f "$Pfile" ]; then
 | |
|                     flag="${COULEUR_BLEUE}*${COULEUR_NORMALE} "
 | |
|                 elif [ "$profile" != Common -a -f "$Cfile" ]; then
 | |
|                     flag="$(get_color YELLOW)C${COULEUR_NORMALE} "
 | |
|                 elif [ -z "$show_all" ]; then
 | |
|                     continue
 | |
|                 else
 | |
|                     flag="  "
 | |
|                 fi
 | |
|                 uecho "  $flag$rfile"
 | |
|             done
 | |
|         else
 | |
|             estep "Pas de fichiers locaux définis"
 | |
|         fi
 | |
|         estep "Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
 | |
|         estep "Versions en attente: ${COULEUR_ROUGE}${PVERSIONS[*]}${COULEUR_NORMALE}"
 | |
|         [ -n "$profile" ] && estep "Profil courant: ${COULEUR_BLEUE}$profile${COULEUR_NORMALE}" || estep "${COULEUR_JAUNE}Pas de profil courant${COULEUR_NORMALE}"
 | |
|         [ ${#profiles[*]} -gt 0 ] && estep "Profils valides: ${profiles[*]}" || estep "Pas de profils définis"
 | |
|     elif [ -f "${pffdir:-.}/$PFFCONF" ]; then
 | |
|         : "${pffdir:=.}"
 | |
|         conf_init "${PFFCONFVARS[@]}"
 | |
|         source "$pffdir/$PFFCONF"
 | |
|         estep "[Projet pff déployé] Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| # Programme principal
 | |
| 
 | |
| for script_alias in "${SCRIPT_ALIASES[@]}"; do
 | |
|     splitpair "$script_alias" src option
 | |
|     if [ "$scriptname" == "$src" ]; then
 | |
|         eval "set -- $option \"\$@\""
 | |
|         break
 | |
|     fi
 | |
| done
 | |
| 
 | |
| QUIET=1 # masquer les messages de git et rsync?
 | |
| VERYQUIET=1 # masquer les messages de git commit?
 | |
| 
 | |
| action=infos
 | |
| autopatch=1
 | |
| version=
 | |
| disttype=auto
 | |
| ORIGEXTS=("${DEFAULT_ORIGEXTS[@]}")
 | |
| PROTECTS=("${DEFAULT_PROTECTS[@]}")
 | |
| unwrap=auto
 | |
| alternate=
 | |
| args=(
 | |
|     --help '$exit_with display_help'
 | |
|     -0,--init action=init
 | |
|     --s:,--origext: '$add@ ORIGEXTS'
 | |
|     --k,--clear-origexts '$ORIGEXTS=()'
 | |
|     -N,--new action=new
 | |
|     -M,--new-only '$action=new; autopatch='
 | |
|     -V:,--version: version=
 | |
|     --auto-archive disttype=auto
 | |
|     -F,--full-archive disttype=full
 | |
|     -P,--patch-archive disttype=patch
 | |
|     --auto-unwrap unwrap=auto
 | |
|     --no-unwrap unwrap=
 | |
|     -E,--unwrap unwrap=1
 | |
|     -p,--patch action=patch
 | |
|     -a,--add-global action=add-global
 | |
|     --locals action=list-locals
 | |
|     --profiles action=list-profiles
 | |
|     -s,--switch action=switch
 | |
|     -b,--add-local action=add-local
 | |
|     -e,--edit action=edit
 | |
|     -d,--diff action=diff
 | |
|     --infos action=infos
 | |
|     -l,--list-names,--show-all alternate=1
 | |
| )
 | |
| parse_args "$@"; set -- "${args[@]}"
 | |
| 
 | |
| array_fix_paths ORIGEXTS
 | |
| 
 | |
| case "$action" in
 | |
| init) init_cmd "$@";;
 | |
| new) new_cmd "$autopatch" "$version" "$disttype" "$unwrap" "$@";;
 | |
| patch) patch_cmd "$@";;
 | |
| add-global) add_global_cmd "$@";;
 | |
| list-locals) list_locals_cmd "$@";;
 | |
| list-profiles) list_profiles_cmd "$@";;
 | |
| switch) switch_cmd "$@";;
 | |
| add-local) add_local_cmd "$@";;
 | |
| edit) edit_cmd "$@";;
 | |
| diff) diff_cmd "$alternate" "$@";;
 | |
| infos) infos_cmd "$alternate" "$@";;
 | |
| esac
 |