1743 lines
		
	
	
		
			64 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1743 lines
		
	
	
		
			64 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=(
 | 
						|
    pf0:-0
 | 
						|
    pfn:-N
 | 
						|
    pfg:-g
 | 
						|
    pfb:-b
 | 
						|
    pfs:-s
 | 
						|
    pfa:-a
 | 
						|
    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"
 | 
						|
    -s
 | 
						|
    "DISTTYPE=auto//Type de distribution upstream: full ou patch"
 | 
						|
    -a
 | 
						|
    "ORIGEXTS=//Extensions origines"
 | 
						|
    "PROTECTS=//Fichiers locaux à protéger lors de l'intégration au format rsync e.g /dir/, /file, etc."
 | 
						|
    "MKDIRS//Répertoires qui doivent toujours exister"
 | 
						|
    "FILTERS//Filtres appliqués aux fichiers lors de l'intégration, de la forme 'filespec:filter'. Cf la doc pour le détail du format"
 | 
						|
    "NOMERGES=//Fichiers qu'il ne faut pas chercher à fusionner. Cf la doc pour le détail du format"
 | 
						|
)
 | 
						|
 | 
						|
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 __pfe_profiles() {
 | 
						|
  echo ALL
 | 
						|
  __pff_profiles "$@" | grep -vxF Base
 | 
						|
}
 | 
						|
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 __pfe_completion() {
 | 
						|
  local cur prev opt comp
 | 
						|
  _get_comp_words_by_ref cur prev
 | 
						|
  if [[ "$prev" == -*p ]]; then
 | 
						|
    COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
 | 
						|
  elif [ "$prev" == --profile ]; then
 | 
						|
    COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
 | 
						|
  elif [[ "$cur" == -*p* ]]; then
 | 
						|
    comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
 | 
						|
    COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
 | 
						|
  fi
 | 
						|
}
 | 
						|
complete -F __pfe_completion -o bashdefault -o default pfe
 | 
						|
function __pff_completion() {
 | 
						|
  local cur prev opt comp
 | 
						|
  _get_comp_words_by_ref cur prev
 | 
						|
  if [ "${COMP_WORDS[1]}" == -e -o "${COMP_WORDS[1]}" == --edit ]; then
 | 
						|
    # ne compléter -p que si on est en mode --edit
 | 
						|
    if [[ "$prev" == -*p ]]; then
 | 
						|
      COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
 | 
						|
    elif [ "$prev" == --profile ]; then
 | 
						|
      COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
 | 
						|
    elif [[ "$cur" == -*p* ]]; then
 | 
						|
      comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
 | 
						|
      COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
 | 
						|
    fi
 | 
						|
  elif [[ "$prev" == -*s ]]; then
 | 
						|
    COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
 | 
						|
  elif [ "$prev" == --switch ]; 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 vcs javaproperties
 | 
						|
 | 
						|
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]
 | 
						|
 | 
						|
CONFIGURATION
 | 
						|
Le fichier $PFFCONF contient des variables qui conditionnent le comportement de
 | 
						|
pff:
 | 
						|
 | 
						|
    PROTECTS -- liste de spécifications de fichiers ou de répertoires à protéger
 | 
						|
        lors de l'intégration. Ces spécifications sont au format rsync, e.g
 | 
						|
        /dir/, /file, etc.
 | 
						|
 | 
						|
    FILTERS -- liste de définitions de filtres à appliquer aux fichiers lors de
 | 
						|
        l'intégration. ces spécifications sont de la forme 'filespec:filter'.
 | 
						|
        Les spécifications de fichier peuvent contenir des wildcards et sont de
 | 
						|
        deux sortes:
 | 
						|
        - sans chemin, e.g 'myfile' ou '*.c', tous les fichiers de ce nom
 | 
						|
          correspondent
 | 
						|
        - avec un chemin, e.g '/myfile', 'path/to/*.jsp', la correspondance est
 | 
						|
          cherchée relativement au répertoire du projet. ainsi '/*.c' ne matche
 | 
						|
          que les fichiers ayant l'extension .c situés dans le répertoire du
 | 
						|
          projet
 | 
						|
        Les filtres disponibles sont:
 | 
						|
            nl2lf -- forcer le caractère de fin de ligne à LF
 | 
						|
            nl2crlf -- forcer le caractère de fin de ligne à CR+LF
 | 
						|
            nl2cr -- forcer le caractère de fin de ligne à CR
 | 
						|
            normalize_properties -- normaliser fichier de propriétés java
 | 
						|
        Il est possible de créer de nouveaux filtres en définissant des
 | 
						|
        fonctions de la forme pff_filter_NAME(). Ces fonctions, si elles sont
 | 
						|
        appelées sans argument, doivent filtrer l'entrée standard. Si un
 | 
						|
        argument est spécifié, il faut filtrer ce fichier-là.
 | 
						|
 | 
						|
    NOMERGES -- liste de spécifications de fichiers qu'il ne faut pas chercher à
 | 
						|
        fusionner. Les spécifications de fichier peuvent contenir des wildcards
 | 
						|
        et sont de deux sortes:
 | 
						|
        - sans chemin, e.g 'myfile' ou '*.c', tous les fichiers de ce nom
 | 
						|
          correspondent
 | 
						|
        - avec un chemin, e.g '/myfile', 'path/to/*.jsp', la correspondance est
 | 
						|
          cherchée relativement au répertoire du projet. ainsi '/*.c' ne matche
 | 
						|
          que les fichiers ayant l'extension .c situés dans le répertoire du
 | 
						|
          projet
 | 
						|
 | 
						|
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 -H est requise.
 | 
						|
    -H, --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 -H 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.
 | 
						|
 | 
						|
    -g, --patch [WORKDIR]
 | 
						|
        Intégrer les modifications entrantes sur les fichiers nouvellement
 | 
						|
        arrivés via l'option --new
 | 
						|
    -o, --ours
 | 
						|
        Utiliser la stratégie '-s recursive -X ours' en cas de conflit lors de
 | 
						|
        l'intégration de la version.
 | 
						|
    --ask-commit
 | 
						|
    -c, --commit
 | 
						|
    --no-commit
 | 
						|
        Après l'intégration avec succès d'un patch, demander à l'utilisateur
 | 
						|
        s'il veut faire commit & push dans git (--ask-commit, la valeur par
 | 
						|
        défaut), le faire sans confirmation (--commit), ou ne jamais le faire
 | 
						|
        (--no-commit)
 | 
						|
 | 
						|
    -b, --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
 | 
						|
 | 
						|
    -a, --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
 | 
						|
    -p, --profile PROFILE
 | 
						|
        Pour l'option --edit, sélectionner le profil spécifié comme celui
 | 
						|
        concerné pour les fichier mentionnés après cette option. Par exemple:
 | 
						|
            $scriptname -e A -pprod B C -ptest D
 | 
						|
        Edite le fichier A du profil courant, les fichiers B et C du profil prod
 | 
						|
        et le fichier D du profil test.
 | 
						|
        Le profil ALL est spécial et permet d'éditer le fichier dans tous les
 | 
						|
        profils *où il existe*, excepté Base parce que ce profil contient des
 | 
						|
        fichiers qui ne devraient pas être modifiés.
 | 
						|
        Pour tous les autres profils, si le fichier n'existe pas dans le profil
 | 
						|
        spécifié il est rajouté dans le profil avant son édition.
 | 
						|
        Attention: pour que l'option -p/--profile soit correctement reconnue
 | 
						|
        avec l'option -e/--edit, il faut que cette dernière option soit
 | 
						|
        mentionnée en premier sur la ligne de commande.
 | 
						|
    -P, --prod
 | 
						|
    -T, --test
 | 
						|
    -A, --all-profiles
 | 
						|
        Raccourcis pour respectivement -pprod, -ptest et -pALL
 | 
						|
 | 
						|
    -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 DESTP au profil courant. Avec la deuxième
 | 
						|
        syntaxe, comparer le profil DESTP au profil SRCP.
 | 
						|
    -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_user_profiles() {
 | 
						|
    # afficher tous les profils modifiables du projet pff $1 (c'est à dire tous
 | 
						|
    # les profils valides excepté Base)
 | 
						|
    get_profiles "$@" | grep -vxF Base
 | 
						|
}
 | 
						|
 | 
						|
function 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
 | 
						|
        get_user_profiles "$@" | head -n1
 | 
						|
    else
 | 
						|
        echo "$profile"
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
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_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 get_vlfiles() {
 | 
						|
    local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
 | 
						|
    get_vlfiles_nostrip "$@" | sed "s|^$pffdir/pff/$profile/||"
 | 
						|
}
 | 
						|
 | 
						|
function is_nomerge() {
 | 
						|
    local file="$1" pffdir="$2"
 | 
						|
    local nomerge rfile
 | 
						|
    setx rfile=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
 | 
						|
            nomerge="${nomerge#/}"
 | 
						|
            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 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_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 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 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 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"
 | 
						|
}
 | 
						|
 | 
						|
################################################################################
 | 
						|
# Filtres
 | 
						|
 | 
						|
function pff_filter_normalize_properties() {
 | 
						|
    if [ $# -eq 0 ]; then
 | 
						|
        __norm_properties
 | 
						|
    else
 | 
						|
        local mode r
 | 
						|
        mode="$(fix_mode "$1")"
 | 
						|
        norm_properties "$1"; r=$?
 | 
						|
        unfix_mode "$1" "$mode"
 | 
						|
        return $r
 | 
						|
    fi
 | 
						|
}
 | 
						|
function pff_filter_nl2lf() {
 | 
						|
    if [ $# -eq 0 ]; then
 | 
						|
        _nl2lf
 | 
						|
    else
 | 
						|
        local mode r
 | 
						|
        mode="$(fix_mode "$1")"
 | 
						|
        doinplace "$1" _nl2lf; r=$?
 | 
						|
        unfix_mode "$1" "$mode"
 | 
						|
        return $r
 | 
						|
    fi
 | 
						|
}
 | 
						|
function pff_filter_nl2crlf() {
 | 
						|
    if [ $# -eq 0 ]; then
 | 
						|
        _nl2crlf
 | 
						|
    else
 | 
						|
        local mode r
 | 
						|
        mode="$(fix_mode "$1")"
 | 
						|
        doinplace "$1" _nl2crlf; r=$?
 | 
						|
        unfix_mode "$1" "$mode"
 | 
						|
        return $r
 | 
						|
    fi
 | 
						|
}
 | 
						|
function pff_filter_nl2cr() {
 | 
						|
    if [ $# -eq 0 ]; then
 | 
						|
        _nl2cr
 | 
						|
    else
 | 
						|
        local mode r
 | 
						|
        mode="$(fix_mode "$1")"
 | 
						|
        doinplace "$1" _nl2cr; r=$?
 | 
						|
        unfix_mode "$1" "$mode"
 | 
						|
        return $r
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
function apply_filter() {
 | 
						|
    # Appliquer les filtres définis au fichier $1 dans le projet pff $2
 | 
						|
    # $3 est le nom du fichier sans préfixe, pour la sélection du filtre
 | 
						|
    # retourner 0 si un filtre a été appliqué avec succès, 1 si une erreur s'est
 | 
						|
    # produite, 2 si aucun filtre n'existe pour ce fichier
 | 
						|
    local pfile="$1" pffdir="$2"
 | 
						|
    local realfile="${3:-$pfile}"
 | 
						|
    local rfile ffile filter r=2 filedir filespec
 | 
						|
    setx rfile=get_rfile "$realfile" "$pffdir"
 | 
						|
    setx file=basename -- "$rfile" # utilisé pour le match sur le nom du fichier
 | 
						|
    for filter in "${FILTERS[@]}"; do
 | 
						|
        splitpair "$filter" ffile filter
 | 
						|
        if [[ "$ffile" == */* ]]; then
 | 
						|
            # matcher sur le chemin relatif
 | 
						|
            ffile="${ffile#/}"
 | 
						|
            if eval "[[ $(qval "$rfile") == $(qwc "$ffile") ]]"; then
 | 
						|
                "pff_filter_$filter" "$pfile" && r=0 || r=1
 | 
						|
            fi
 | 
						|
        else
 | 
						|
            # matcher uniquement sur le nom du fichier
 | 
						|
            if eval "[[ $(qval "$file") == $(qwc "$ffile") ]]"; then
 | 
						|
                "pff_filter_$filter" "$pfile" && r=0 || r=1
 | 
						|
            fi
 | 
						|
        fi
 | 
						|
    done
 | 
						|
    return $r
 | 
						|
}
 | 
						|
 | 
						|
################################################################################
 | 
						|
# 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
 | 
						|
    array_addu PROFILES Common
 | 
						|
    conf_update "$workdir/$PFFCONF" PROFILES
 | 
						|
    # 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" unwrap="$3"
 | 
						|
 | 
						|
    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 -F ou -H"
 | 
						|
                ;;
 | 
						|
            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")"
 | 
						|
                    if [ "$filename" == "$banv" -o "$filename" == "$ban" ]; then
 | 
						|
                        # le nom du répertoire correspond au nom de l'archive. cool!
 | 
						|
                        unwrap=1
 | 
						|
                    elif [ "${filename%-$version}" == "$ban" ]; then
 | 
						|
                        # le nom du répertoire n'est pas contruit correctement
 | 
						|
                        # mais ce n'est pas grave (e.g ban-VERSION au lieu de
 | 
						|
                        # banVERSION comme l'archive)
 | 
						|
                        unwrap=1
 | 
						|
                    else
 | 
						|
                        unwrap=
 | 
						|
                    fi
 | 
						|
                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 -F ou -H"
 | 
						|
        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" merge_strategy="$5" commit_policy="$6"; shift; shift; shift; shift; shift; shift
 | 
						|
    local archive="$1" pffdir="$2"
 | 
						|
 | 
						|
    ensure_pffdir pffdir "$pffdir"
 | 
						|
    [ -n "$disttype" ] && DISTTYPE="$disttype"
 | 
						|
 | 
						|
    ## préparer la source: les fichiers entrants
 | 
						|
    local srcdir full
 | 
						|
    new__prepare_archive srcdir version "$archive" "$version" "$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[@]}" "${DEFAULT_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
 | 
						|
            src="$srcdir/$i"
 | 
						|
            dest="$pffdir/$i"
 | 
						|
            mkdirof "$dest"; r=$?
 | 
						|
            edot $r "mkdirof $i"; [ $r -eq 0 ] || break
 | 
						|
            cp "$src" "$dest"; r=$?
 | 
						|
            edot $r "create $i"; [ $r -eq 0 ] || break
 | 
						|
            apply_filter "$dest" "$pffdir"; r=$?
 | 
						|
            [ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
 | 
						|
            [ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
 | 
						|
        done
 | 
						|
        [ $r -eq 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 "update $i"; [ $r -eq 0 ] || break
 | 
						|
            apply_filter "$dest" "$pffdir"; r=$?
 | 
						|
            [ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
 | 
						|
            [ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
 | 
						|
        done
 | 
						|
        [ $r -eq 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 "delete $i"; [ $r -eq 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 "mkdirof $i"; [ $r -eq 0 ] || break
 | 
						|
            cp "$src" "$dest"; r=$?
 | 
						|
            edot $r "create $i"; [ $r -eq 0 ] || break
 | 
						|
            apply_filter "$dest" "$pffdir"; r=$?
 | 
						|
            [ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
 | 
						|
            [ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
 | 
						|
            add_global__link "$dest" "$pffdir"
 | 
						|
            edot $r "add_global $i"; [ $r -eq 0 ] || break
 | 
						|
        done
 | 
						|
        [ $r -eq 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 flexists "$src"; then
 | 
						|
                mkdirof "$dest"; r=$?
 | 
						|
                edot $r "mkdirof $vlfile"; [ $r -eq 0 ] || break
 | 
						|
                cp "$src" "$dest"; r=$?
 | 
						|
                edot $r "create $vlfile"; [ $r -eq 0 ] || break
 | 
						|
                apply_filter "$dest" "$pffdir" "$pffdir/pff/Base/$i"; r=$?
 | 
						|
                [ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $vlfile"
 | 
						|
                [ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
 | 
						|
                array_add vlfiles "$vlfile"
 | 
						|
            fi
 | 
						|
        done
 | 
						|
        [ $r -eq 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
 | 
						|
        ask_yesno "Voulez-vous passer à l'intégration de cette version?" O &&
 | 
						|
            patch_cmd "$merge_strategy" "$commit_policy" "$pffdir"
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
#===========================================================
 | 
						|
# pff --patch
 | 
						|
 | 
						|
function patch_cmd() {
 | 
						|
    local merge_strategy="$1" commit_policy="$2"; shift; shift
 | 
						|
    local pffdir="$1"
 | 
						|
    local gitproject was_patched eop_version
 | 
						|
    local version profile rcfile curdir workdir controlfile control tmpfile
 | 
						|
    local -a profiles vlfiles tmpfiles
 | 
						|
 | 
						|
    ensure_pffdir pffdir "$pffdir"
 | 
						|
 | 
						|
    # est-ce un projet suivi dans git?
 | 
						|
    (cd "$pffdir"; git_check_gitvcs) && gitproject=1 || gitproject=
 | 
						|
 | 
						|
    array_from_lines profiles "$(get_user_profiles "$pffdir")"
 | 
						|
    if array_contains profiles Common; then
 | 
						|
        # toujours traiter Common en dernier
 | 
						|
        array_del profiles Common
 | 
						|
        array_add profiles Common
 | 
						|
    fi
 | 
						|
 | 
						|
    case "$merge_strategy" in
 | 
						|
    ours) merge_strategy=(-s recursive -X ours);;
 | 
						|
    theirs) merge_strategy=(-s recursive -X theirs);;
 | 
						|
    *) merge_strategy=();;
 | 
						|
    esac
 | 
						|
 | 
						|
    while true; do
 | 
						|
        # si pas de version en cours, il n'y a rien à patcher
 | 
						|
        [ ${#PVERSIONS[*]} -gt 0 ] || break
 | 
						|
 | 
						|
        eop_version=
 | 
						|
        for version in "${PVERSIONS[@]}"; do
 | 
						|
            have_profile_vlfiles=
 | 
						|
            have_base_vlfiles=
 | 
						|
            for profile in "${profiles[@]}"; do
 | 
						|
                array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" "$profile" "$version")"
 | 
						|
                # filtrer les fichiers de NOMERGES
 | 
						|
                mergefiles=()
 | 
						|
                for vlfile in "${vlfiles[@]}"; do
 | 
						|
                    setx rfile=get_rfile "${vlfile%__pv-${version}__}" "$pffdir"
 | 
						|
                    is_nomerge "$rfile" "$pffdir" && continue
 | 
						|
                    array_add mergefiles "$vlfile"
 | 
						|
                done
 | 
						|
                vlfiles=("${mergefiles[@]}")
 | 
						|
                # on en a trouvé
 | 
						|
                if [ ${#vlfiles[*]} -gt 0 ]; then
 | 
						|
                    have_profile_vlfiles=1
 | 
						|
                    break
 | 
						|
                fi
 | 
						|
            done
 | 
						|
            [ -n "$have_profile_vlfiles" ] && break
 | 
						|
            array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" Base "$version")"
 | 
						|
            # filtrer les fichiers de NOMERGES
 | 
						|
            mergefiles=()
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                setx rfile=get_rfile "${vlfile%__pv-${version}__}" "$pffdir"
 | 
						|
                is_nomerge "$rfile" "$pffdir" && continue
 | 
						|
                array_add mergefiles "$vlfile"
 | 
						|
            done
 | 
						|
            vlfiles=("${mergefiles[@]}")
 | 
						|
            have_base_vlfiles=1
 | 
						|
            break
 | 
						|
        done
 | 
						|
        if [ -n "$have_profile_vlfiles" ]; then
 | 
						|
            # il faut patcher les fichiers du profil
 | 
						|
            etitle "Intégration de la version $version dans le profil $profile"
 | 
						|
            [ -n "$rcfile" ] || ac_set_tmpfile rcfile
 | 
						|
            [ -n "$controlfile" ] || ac_set_tmpfile controlfile
 | 
						|
            [ -n "$workdir" ] && ac_clean "$workdir"
 | 
						|
            ac_set_tmpdir workdir
 | 
						|
            curdir="$(pwd)"
 | 
						|
            cd "$workdir"
 | 
						|
            git init -q .
 | 
						|
            git checkout -q --orphan upstream
 | 
						|
            # rajouter les fichiers de Base dans la branche upstream
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                setx bfile=get_bfile "${vlfile%__pv-${version}__}" "$pffdir"
 | 
						|
                setx rfile=get_rfile "$bfile" "$pffdir"
 | 
						|
                mkdirof "$rfile"
 | 
						|
                cp -a "$bfile" "$rfile"
 | 
						|
                chmod +w "$rfile"
 | 
						|
            done
 | 
						|
            git add -A
 | 
						|
            [ -n "$(git status --porcelain)" ] && git commit -qm "Base"
 | 
						|
            # créer la branche v$version depuis upstream
 | 
						|
            git checkout -qb "v$version"
 | 
						|
            # rajouter les fichiers de Common s'ils existent (sauf si la branche c'est Common ^^)
 | 
						|
            if [ "$profile" != Common ]; then
 | 
						|
                for vlfile in "${vlfiles[@]}"; do
 | 
						|
                    setx Cfile=get_Cfile "${vlfile%__pv-${version}__}" "$pffdir"
 | 
						|
                    if flexists "$Cfile"; then
 | 
						|
                        setx rfile=get_rfile "$Cfile" "$pffdir"
 | 
						|
                        mkdirof "$rfile"
 | 
						|
                        cp -a "$Cfile" "$rfile"
 | 
						|
                    fi
 | 
						|
                done
 | 
						|
                git add -A
 | 
						|
                [ -n "$(git status --porcelain)" ] && git commit -qm "Common"
 | 
						|
            fi
 | 
						|
            # rajouter les fichiers du profil
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                setx pfile=get_pfile "${vlfile%__pv-${version}__}" "$profile" "$pffdir"
 | 
						|
                setx rfile=get_rfile "$pfile" "$pffdir"
 | 
						|
                mkdirof "$rfile"
 | 
						|
                cp -a "$pfile" "$rfile"
 | 
						|
            done
 | 
						|
            git add -A
 | 
						|
            [ -n "$(git status --porcelain)" ] && git commit -qm "$profile"
 | 
						|
            # rebasculer vers upstream et rajouter les fichiers de patch
 | 
						|
            git checkout -q upstream
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                setx rfile=get_rfile "${vlfile%__pv-${version}__}" "$pffdir"
 | 
						|
                mkdirof "$rfile"
 | 
						|
                cp -L "$vlfile" "$rfile"
 | 
						|
            done
 | 
						|
            git add -A
 | 
						|
            [ -n "$(git status --porcelain)" ] && git commit -qm "$version"
 | 
						|
            # basculer vers la branche de version et tenter de merger upstream dans version
 | 
						|
            git checkout -q "v$version"
 | 
						|
            if git merge -q "${merge_strategy[@]}" --no-commit upstream; then
 | 
						|
                # tout s'est bien passé
 | 
						|
                [ -n "$(git status --porcelain)" ] && git commit -qm "v$version --> $profile"
 | 
						|
            else
 | 
						|
                # il y a eu une erreur. laisser l'utilisateur décider quoi faire
 | 
						|
                echo >"$rcfile" "#
 | 
						|
[ -f /etc/bash.bashrc ] && . /etc/bash.bashrc
 | 
						|
[ -f ~/.bashrc ] && . ~/.bashrc
 | 
						|
$(qvals source "$ULIBDIR/ulib")
 | 
						|
urequire DEFAULTS
 | 
						|
function Commit() { echo COMMIT >$(qvals "$controlfile"); exit; }
 | 
						|
# raccourcis pour Commit
 | 
						|
function C() { Commit \"\$@\"; }; function c() { Commit \"\$@\"; }; function commit() { Commit \"\$@\"; }
 | 
						|
function Abort() { echo ABORT >$(qvals "$controlfile"); exit; }
 | 
						|
# raccourcis pour Abort
 | 
						|
function A() { Abort \"\$@\"; }; function a() { Abort \"\$@\"; }; function abort() { Abort \"\$@\"; }
 | 
						|
$(qvals cd "$workdir")
 | 
						|
$(qvals eerror "Une erreur s'est produite: consultez les fichiers concernés et faites les corrections nécessaires. En cas de conflit, vous devez les résoudre en examinant les lignes marquées
 | 
						|
    <<<<<<< HEAD
 | 
						|
    =======
 | 
						|
    >>>>>>> upstream
 | 
						|
puis en gardant la partie HEAD pour les modifications locales et upstream pour les modifications entrantes")
 | 
						|
$(qvals eimportant "Puis tapez ${COULEUR_VERTE}Commit${COULEUR_NORMALE} pour valider l'intégration de cette version")
 | 
						|
$(qvals eimportant "Sinon, tapez ${COULEUR_ROUGE}Abort${COULEUR_NORMALE} pour arrêter l'intégration de cette version")
 | 
						|
"
 | 
						|
                >"$controlfile"
 | 
						|
                "${SHELL:-bash}" --rcfile "$rcfile"
 | 
						|
                control="$(<"$controlfile")"
 | 
						|
                if [ -z "$control" ]; then
 | 
						|
                    # demander à l'utilisateur sa décision
 | 
						|
                    eerror "Vous n'avez pas terminé votre session par ${COULEUR_VERTE}Commit${COULEUR_NORMALE} ou ${COULEUR_ROUGE}Abort${COULEUR_NORMALE}"
 | 
						|
                    ask_yesno "Voulez-vous valider l'intégration de la version?" N && control=COMMIT || control=ABORT
 | 
						|
                fi
 | 
						|
                if [ "$control" == COMMIT ]; then
 | 
						|
                    array_from_lines tmpfiles "$(git status --porcelain | awk '{print substr($0, 4)}')"
 | 
						|
                    for tmpfile in "${tmpfiles[@]}"; do
 | 
						|
                        if grep -qE '^(<<<<<<<|=======|>>>>>>>)' "$tmpfile"; then
 | 
						|
                            ewarn "Vous avez choisi de valider l'intégration des modifications mais..."
 | 
						|
                            ewarn "$tmpfile: ce fichier semble encore contenir des marques de conflit"
 | 
						|
                            if ! ask_yesno -y "Etes-vous sûr d'avoir correctement édité le fichier?" N; then
 | 
						|
                                eecho "Annulation de l'intégration de la version"
 | 
						|
                                control=ABORT
 | 
						|
                                break
 | 
						|
                            fi
 | 
						|
                        fi
 | 
						|
                    done
 | 
						|
                fi
 | 
						|
                if [ "$control" == ABORT ]; then
 | 
						|
                    enote "Vous pouvez reprendre l'intégration de la version $version avec la commande    pff -g"
 | 
						|
                    break # abort
 | 
						|
                fi
 | 
						|
            fi
 | 
						|
            # récupérer les versions modifiées et supprimer les fichiers de patch
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                setx pfile=get_pfile "${vlfile%__pv-${version}__}" "$profile" "$pffdir"
 | 
						|
                setx rfile=get_rfile "$pfile" "$pffdir"
 | 
						|
                cp -a "$rfile" "$pfile"
 | 
						|
                rm "$vlfile"
 | 
						|
            done
 | 
						|
            cd "$curdir"
 | 
						|
            eend
 | 
						|
        elif [ -n "$have_base_vlfiles" ]; then
 | 
						|
            # il faut intégrer la nouvelle version dans Base
 | 
						|
            etitle "Finaliser intégration de la version $version"
 | 
						|
            for vlfile in "${vlfiles[@]}"; do
 | 
						|
                bfile="${vlfile%__pv-${version}__}"
 | 
						|
                chmod +w "$bfile"
 | 
						|
                mv "$vlfile" "$bfile"
 | 
						|
                chmod a-w "$bfile"
 | 
						|
            done
 | 
						|
            eop_version=1
 | 
						|
            VERSION="$version"
 | 
						|
            array_del PVERSIONS "$VERSION"
 | 
						|
            conf_update "$pffdir/$PFFCONF" VERSION PVERSIONS
 | 
						|
            eend
 | 
						|
        fi
 | 
						|
 | 
						|
        if [ -n "$gitproject" ]; then
 | 
						|
            local commit default
 | 
						|
            if [ -n "$eop_version" ]; then
 | 
						|
                msg="Intégration de la version $version"
 | 
						|
            else
 | 
						|
                msg="Correction du profil $profile pour la version $version"
 | 
						|
            fi
 | 
						|
            commit="$commit_policy"
 | 
						|
            if [ "$commit" == ask ]; then
 | 
						|
                if [ -n "$eop_version" ]; then
 | 
						|
                    enote "Vous avez terminé l'intégration des patches de la version $version"
 | 
						|
                    default=O
 | 
						|
                else
 | 
						|
                    einfo "Vous avez intégré les patches de la version $version pour le profil $profile"
 | 
						|
                    default=N
 | 
						|
                fi
 | 
						|
                ask_yesno "Voulez-vous enregistrer les modifications dans git?" $default || commit=
 | 
						|
            fi
 | 
						|
            if [ -n "$commit" ]; then
 | 
						|
                git add -A && git commit -m "$msg" || return
 | 
						|
                if [ -z "$UTOOLS_VCS_OFFLINE" -a -n "$(git_get_branch_remote)" ]; then
 | 
						|
                    git push
 | 
						|
                fi
 | 
						|
            fi
 | 
						|
        fi
 | 
						|
    done
 | 
						|
}
 | 
						|
 | 
						|
#===========================================================
 | 
						|
# 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
 | 
						|
        chmod a-w "$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
 | 
						|
 | 
						|
    ensure_pffdir pffdir "$pffdir"
 | 
						|
    autoinit "$pffdir"
 | 
						|
    [ -n "$profile" ] || setx profile=get_first_profile "$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"
 | 
						|
        chmod +w "$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 profile="$1"; shift
 | 
						|
    local pffdir file rfile pfile Pfile r
 | 
						|
    local -a profiles edits args
 | 
						|
 | 
						|
    ensure_pffdir pffdir
 | 
						|
    array_from_lines profiles "$(get_user_profiles "$pffdir")"
 | 
						|
    [ -n "$profile" ] || setx profile=get_current_profile "$pffdir"
 | 
						|
    enote "Dans le profil $profile:"
 | 
						|
 | 
						|
    r=0
 | 
						|
    while [ $# -gt 0 ]; do
 | 
						|
        if [[ "$1" == -* ]]; then
 | 
						|
            # nouvelle option
 | 
						|
            local prev_profile="$profile"
 | 
						|
            args=(+ -p:,--profile: profile=)
 | 
						|
            parse_args "$@"; set -- "${args[@]}"
 | 
						|
            [ "$profile" != "$prev_profile" ] && enote "Dans le profil $profile:"
 | 
						|
            continue
 | 
						|
        fi
 | 
						|
        file="$1"; shift
 | 
						|
 | 
						|
        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
 | 
						|
        }
 | 
						|
        if [ "$profile" == ALL ]; then
 | 
						|
            local tmpp
 | 
						|
            for tmpp in "${profiles[@]}"; do
 | 
						|
                setx Pfile=get_pfile "$pfile" "$tmpp" "$pffdir"
 | 
						|
                [ -e "$Pfile" ] || continue
 | 
						|
                estep "Edition de $(ppath "$Pfile")"
 | 
						|
                array_add edits "$Pfile"
 | 
						|
            done
 | 
						|
        else
 | 
						|
            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"
 | 
						|
            estep "Edition de $(ppath "$Pfile")"
 | 
						|
            array_add edits "$Pfile"
 | 
						|
        fi
 | 
						|
    done
 | 
						|
 | 
						|
    if [ ${#edits[*]} -gt 0 ]; then
 | 
						|
        "${EDITOR:-vi}" "${edits[@]}" || r=$?
 | 
						|
    else
 | 
						|
        r=2
 | 
						|
    fi
 | 
						|
    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"
 | 
						|
 | 
						|
    local -a diffcolor
 | 
						|
    isatty && diffcolor=(--color) || diffcolor=(--no-color)
 | 
						|
    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 "$destfile" "$srcfile" >&/dev/null || echo "$srcfile"
 | 
						|
        else
 | 
						|
            [ -n "$desc" ] && echo "$desc"
 | 
						|
            desc=
 | 
						|
            if [ -n "$PFF_USE_REGULAR_DIFF" ]; then
 | 
						|
                # au cas où git n'est pas disponible
 | 
						|
                diff -ur "$destfile" "$srcfile"
 | 
						|
            else
 | 
						|
                git diff "${diffcolor[@]}" --no-index "$destfile" "$srcfile"
 | 
						|
            fi
 | 
						|
        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?
 | 
						|
 | 
						|
parse_mode=
 | 
						|
if [ "$1" == -e -o "$1" == --edit ]; then
 | 
						|
    parse_mode=+
 | 
						|
fi
 | 
						|
 | 
						|
action=infos
 | 
						|
autopatch=1
 | 
						|
version=
 | 
						|
disttype=
 | 
						|
ORIGEXTS=("${DEFAULT_ORIGEXTS[@]}")
 | 
						|
unwrap=auto
 | 
						|
merge_strategy=
 | 
						|
commit_policy=ask
 | 
						|
profile=
 | 
						|
alternate=
 | 
						|
args=($parse_mode
 | 
						|
    --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
 | 
						|
    -H,--patch-archive disttype=patch
 | 
						|
    --auto-unwrap unwrap=auto
 | 
						|
    --no-unwrap unwrap=
 | 
						|
    -E,--unwrap unwrap=1
 | 
						|
    -g,--patch action=patch
 | 
						|
    -o,--ours merge_strategy=ours
 | 
						|
    --theirs merge_strategy=theirs
 | 
						|
    --ask-commit commit_policy=ask
 | 
						|
    -c,--comit commit_policy=1
 | 
						|
    --no-commit commit_policy=
 | 
						|
    -b,--add-global action=add-global
 | 
						|
    --locals action=list-locals
 | 
						|
    --profiles action=list-profiles
 | 
						|
    -s,--switch action=switch
 | 
						|
    -a,--add-local action=add-local
 | 
						|
    -e,--edit action=edit
 | 
						|
    -p:,--profile: profile=
 | 
						|
    -P,--prod profile=prod
 | 
						|
    -T,--test profile=test
 | 
						|
    -A,--all-profiles profile=ALL
 | 
						|
    -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" "$merge_strategy" "$commit_policy" "$@";;
 | 
						|
patch) patch_cmd "$merge_strategy" "$commit_policy" "$@";;
 | 
						|
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 "$profile" "$@";;
 | 
						|
diff) diff_cmd "$alternate" "$@";;
 | 
						|
infos) infos_cmd "$alternate" "$@";;
 | 
						|
esac
 |