##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Fonctions de support pour uinst: déploiement local d'un fichier ou d'un répertoire.
##@cooked nocomments
##@require ulib
##@require base
##@require sysinfos
##@require compat
##@require udir
##@require prefixes
##@require pyulib/pyulib
##@require uinc
uprovide uinst
urequire ulib base sysinfos compat udir prefixes pyulib/pyulib uinc

function uinst() {
    # lancer uinst en déclarant les variables locales, de façon à ne pas polluer
    # l'environnement de l'appelant.
    local UINST_CONFIGURE_FORCE
    local UINST_COPY_METHOD
    local -a UINST_DEFAULTS
    local -a UINST_CONFIG_VARS
    local -a UINST_CONFIG_VARCMDS
    local -a UINST_PROTECTED_VARS
    local UINST_ORIGSRC
    local UINST_SRCDIR
    local UINST_ISTMPDIR
    local UINST_TMPDIR
    local UINST_AUTOPREFIX
    local -a UDIR_VARS
    local -a UDIR_ARRAYS
    local UINST_ACTION
    local UINST_AUTOSRCDIR
    local UINST_PREPARE_DIR UINST_PREPARE_CONF UINST_PREPARE_ULIB
    local udir_desc udir_note udir_types
    local uinc
    local -a uinc_options
    local -a uinc_args
    local -a preconfig_scripts
    local -a configure_variables
    local -a configure_dest_for
    local -a config_scripts
    local install_profiles
    local profiledir
    local bashrcdir
    local defaultdir
    local workdir_rsync_options
    local workdir_excludes
    local workdir_includes
    local copy_files
    local destdir
    local srcdir
    local -a files
    local owner
    local -a modes
    local -a root_scripts
    local uinst2s

    uinst_nolocal "$@"
}

function uinst_nolocal() {
    # Interface en mode ligne de commande pour uinst. Appeler cette fonction
    # avec les paramètres de la ligne de commande, e.g.:
    #     uinst_nolocal "$@"
    function __uinst_display_help() {
        uecho "$scriptname: Déployer en local un fichier, une archive, ou un répertoire

USAGE
    $scriptname [options] <file|archive|dir>

OPTIONS
    var=value
        Spécifier la valeur d'une variable ou d'un préfixe, plutôt que de
        laisser uprefix l'autodétecter. Utiliser 'uprefix -l' pour avoir une
        liste de préfixes valides. Utiliser 'udir --help-vars' pour avoir une
        liste de variables valides pour $scriptname.
    -d /path/to/destdir
        Spécifier le répertoire destination. Equivalent à l'option
        destdir=\"/path/to/destdir\"
    -a  (par défaut) Si la source n'est pas spécifiée, déterminer le répertoire
        à déployer automatiquement.
    --no-auto
        Ne pas déterminer automatiquement le répertoire à déployer.
    --prefix
        (par défaut) Corriger les chemins srcdir et destdir qui commencent par
        des préfixes valides. Utiliser 'uprefix -l' pour avoir une liste de
        préfixes valides.
    --no-prefix
        Ne jamais corriger un chemin.
    --include-vcs
        Inclure les fichiers de VCS dans les fichiers copiés. Par défaut, les
        fichiers de VCS sont exclus.
    -C  Configurer un répertoire pour le déploiement avec uinst
        Ajouter l'option --force pour forcer la reconfiguration"
    }

    # Définir ULIBDIR, PYULIBDIR et UINST si ce n'est pas le cas.
    # Ces variables sont utilisées par les scripts
    if [ -z "$ULIBDIR" ]; then
        if [ -d "$scriptdir/ulib" ]; then
            ULIBDIR="$scriptdir/ulib"
        elif [ -d "$scriptdir/lib/ulib" ]; then
            ULIBDIR="$scriptdir/lib/ulib"
        else
            ULIBDIR="$scriptdir/ulib"
        fi
    fi
    if [ -z "$PYULIBDIR" ]; then
        if [ -d "$scriptdir/pyulib" ]; then
            PYULIBDIR="$scriptdir/pyulib"
        elif [ -d "$scriptdir/lib/pyulib" ]; then
            PYULIBDIR="$scriptdir/lib/pyulib"
        else
            PYULIBDIR="$scriptdir/pyulib"
        fi
    fi
    UINST="${UINST:-$script}"

    eval "$(utools_local)"
    __uinst_init
    UINST_ACTION=
    UINST_AUTOSRCDIR=1
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with __uinst_display_help' \
        --is-tmpdir UINST_ISTMPDIR \
        -O: '$__uinst_addvar owner "$value_"' \
        -m: '$__uinst_addvar modes "$value_"' \
        -d: '$__uinst_addvar destdir $value_' \
        -a,--auto UINST_AUTOSRCDIR=1 \
        --no-auto UINST_AUTOSRCDIR= \
        --prefix UINST_AUTOPREFIX=1 \
        --no-prefix UINST_AUTOPREFIX= \
        --include-vcs UINST_COPY_METHOD=cpdir \
        -C UINST_ACTION=configure \
        -f,--force UINST_CONFIGURE_FORCE=1 \
        --prepare-1s: UINST_PREPARE_DIR \
        --prepare-2s UINST_ACTION=prepare-2s \
        --prepare-conf: UINST_PREPARE_CONF \
        --prepare-with-ulib UINST_PREPARE_ULIB \
        @ args -- "$@" &&
    set -- "${args[@]}" || {
        eerror "$args"
        return 1
    }

    # configuration de la phase, dans le mode prepare
    [ -n "$UINST_PREPARE_DIR" -a -z "$UINST_ACTION" ] && UINST_ACTION=prepare-1s

    # action par défaut
    [ -n "$UINST_ACTION" ] || UINST_ACTION=uinst

    if [ "$UINST_ACTION" == "configure" ]; then
        __uinst_configure "${args[@]}" || return 1
    elif [ "$UINST_ACTION" == prepare-1s ]; then
        UINST_PREPARE_DIR="$(abspath "$UINST_PREPARE_DIR")"
        mkdir -p "$UINST_PREPARE_DIR" || return 1
        if [ -z "$UINST_PREPARE_CONF" ]; then
            UINST_PREPARE_CONF="$UINST_PREPARE_DIR/uinst_prepare.conf"
        fi
        UINST_PREPARE_CONF="$(abspath "$UINST_PREPARE_CONF")"
        __uinst_do "${args[@]}" || return 1
    elif [ "$UINST_ACTION" == prepare-2s ]; then
        UINST_ISTMPDIR=1
        __uinst_do "${args[@]}" || return 1
    else
        __uinst_do "${args[@]}" || return 1
    fi
    return 0
}

function __uinst_init() {
    UINST_CONFIGURE_FORCE=
    UINST_COPY_METHOD="${UINST_COPY_METHOD:-cpdirnovcs}"
    UINST_DEFAULTS=(
        uinc='release'
        uinc_options='()'
        uinc_args='()'
        preconfig_scripts='()'
        configure_variables='(dest)'
        configure_dest_for='()'
        config_scripts='()'
        install_profiles='false'
        profiledir='lib/profile.d'
        bashrcdir='lib/bashrc.d'
        defaultdir='lib/default'
        workdir_rsync_options='()'
        workdir_excludes='()'
        workdir_includes='()'
        copy_files='true'
        destdir='/usr/local'
        srcdir='.'
        files='()'
        owner='root:'
        modes='(u=rwX,g=rX,o=rX)'
        root_scripts='()'
    )

    UINST_CONFIG_VARS=()
    UINST_CONFIG_VARCMDS=()
    UINST_PROTECTED_VARS=(UINST_PROTECTED_VARS UINST_CONFIG_VARS UINST_CONFIG_VARCMDS)

    __uinst_protectvars configure_variables \
        UINST_ORIGSRC UINST_SRCDIR \
        UINST_ISTMPDIR UINST_TMPDIR \
        UINST_AUTOPREFIX \
        UDIR_VARS UDIR_ARRAYS
    UINST_ORIGSRC=
    UINST_SRCDIR=
    UINST_ISTMPDIR=
    UINST_TMPDIR=
    UINST_AUTOPREFIX=1
    UDIR_VARS=()
    UDIR_ARRAYS=()
}

function __uinst_configure() {
    # Configurer le répertoire $1 pour installation avec uinst
    srcdir="${1:-.}"; shift
    [ -d "$srcdir" ] || {
        eerror "$srcdir: répertoire inexistant"
        return 1
    }
    srcdir="$(abspath "$srcdir")"
    urequire udir

    function __uinst_update_types() {
        udir_types=("${udir_types[@]}" uinst)
        set_array_cmd udir_types
    }
    function __uinst_should_update() {
        local udir_type
        for udir_type in "${udir_types[@]}"; do
            if [ "$udir_type" == "uinst" ]; then
                echo "false"; return
            elif [[ "$udir_type" == uinst:* ]]; then
                echo "false"; return
            fi
        done
        __uinst_update_types uinst
    }
    if [ -z "$UINST_CONFIGURE_FORCE" ]; then
        if udir_check "$srcdir"; then
            # Nous avons déjà un fichier .udir
            # Si le répertoire n'est pas du type uinst, le migrer en rajoutant
            # les options par défaut
            if eval "$(udir_eval "$srcdir" __uinst_should_update)"; then
                udir_update "$srcdir" "$(set_array_cmd udir_types)" "${UINST_DEFAULTS[@]}"
                enote "Le répertoire $(ppath "$srcdir") a déjà été configuré pour uinst.
Utilisez 'udir -e $(ppath "$srcdir")' pour modifier les paramètres qui ont été rajoutés."
            fi
            return 0
        elif [ -f "$srcdir/.uinst.conf" ]; then
            # Nous avons un fichier .uinst.conf
            # proposer de migrer le répertoire vers .udir
            check_interaction -c && enote "Ce répertoire contient un fichier .uinst.conf"
            if ask_yesno "Voulez-vous le migrer vers le format .udir?" N; then
                estep "Configuration de $(ppath "$srcdir") pour uinst"
                __uinst_migrate_legacy "$srcdir"
                __uinst_varcmds=()
                for __uinst_v in "${UDIR_VARS[@]}"; do
                    __uinst_varcmds=("${__uinst_varcmds[@]}" "$(set_var_cmd "$__uinst_v" "${!__uinst_v}")")
                done
                for __uinst_a in "${UDIR_ARRAYS[@]}"; do
                    __uinst_varcmds=("${__uinst_varcmds[@]}" "$(set_array_cmd "$__uinst_a")")
                done
                udir_update "$srcdir" 'udir_types=(uinst)' "${__uinst_varcmds[@]}"
                enote "Vous pouvez maintenant supprimer le fichier: rm -f '$(ppath "$srcdir/.uinst.conf")'"
            else
                estep "Configuration de $(ppath "$srcdir") pour uinst:legacy"
                udir_update "$srcdir" 'udir_types=(uinst:legacy)'
            fi
            return 0
        fi
    fi

    # Sans fichier .udir, faire la configuration par défaut
    # Poser des questions pour déterminer le type de configuration à faire
    local udir_desc udir_note udir_types
    local profiles conf rootconf ulibsync copy_files

    eval "$(udir_eval "$srcdir" 'set_var_cmd udir_desc "$udir_desc"; set_var_cmd udir_note "$udir_note"')"

    etitle "Description du projet"
    check_interaction -c && estepn "La description courte de l'objet de ce projet est affichée avec udir -i"
    read_value "Entrez une description de ce projet" udir_desc "$udir_desc" N
    check_interaction -c && estepn "La note est affichée quand on entre dans le répertoire du projet.
Elle peut être utilisée pour diriger le visiteur vers des informations importantes."
    read_value "Entrez une note associée au répertoire de ce projet" udir_note "$udir_note" N
    eend
    udir_update "$srcdir" "udir_desc=$(quoted_arg "$udir_desc")" "udir_note=$(quoted_arg "$udir_note")"

    estepn "La configuration par défaut permet de créer un projet qui utilise les outils et librairies de nutools.
Ce projet pourra ensuite être installé avec uinst"
    if ask_yesno "Voulez-vous utiliser la configuration par défaut?" O; then
        udir_types='(uinst)'
        etitle "Configuration du module"
        ask_yesno "Faut-il installer des profils pour bash?" O && profiles=1
        ask_yesno "Faut-il créer un script de configuration?" O && conf=1
        ask_yesno "Faut-il créer un script de configuration root?" O && rootconf=1
        check_interaction -c && estep "Configuration avancée"
        ask_yesno "Faut-il faire une copie locale de ulib?" O && ulibsync=1
        ask_yesno "Faut-il déployer le projet dans /usr/local?" O && copy_files=1
        eend

        mkdir -p "$srcdir/lib"
        [ -n "$ulibsync" ] && ulibsync "$srcdir/lib"

        udir_update "$srcdir" "udir_types=$udir_types" "${UINST_DEFAULTS[@]}"
        [ -n "$copy_files" ] && copy_files=true || copy_files=false
        udir_update "$srcdir" \
            ${profiles:+install_profiles=true} \
            ${conf:+config_scripts='(lib/uinst/conf)'} \
            copy_files="$copy_files" \
            ${rootconf:+root_scripts='(lib/uinst/rootconf)'}
        
        local genfile
        if [ -n "$profiles" ]; then
            genfile="$srcdir/lib/profile.d/$(basename "$srcdir")"
            mkdirof "$genfile"
            [ -f "$genfile" ] || echo '# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
__uaddpath "@@dest@@" PATH' >"$genfile"
            udir_update "$srcdir" "configure_dest_for=($(quoted_arg "${genfile#"$srcdir/"}"))"
        fi
        if [ -n "$conf" ]; then
            genfile="$srcdir/lib/uinst/conf"
            mkdirof "$genfile"
            [ -f "$genfile" ] || echo '# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

source "$@" || exit 1
source "$ULIBDIR/ulib" || exit 1
urequire DEFAULTS' >"$genfile"
        fi
        if [ -n "$rootconf" ]; then
            genfile="$srcdir/lib/uinst/rootconf"
            mkdirof "$genfile"
            [ -f "$genfile" ] || echo '# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

source "$@" || exit 1
source "$ULIBDIR/ulib" || exit 1
urequire DEFAULTS' >"$genfile"
        fi
        
    else
        local -a udir_typess
        udir_typess=(uinst uinst:rsync uinst:python)
        udir_types=uinst
        simple_menu udir_types udir_typess -t "Choix du type d'installation" -m "Veuillez choisir le type d'installation pour ce projet"
        
        if [ "$udir_types" == uinst ]; then
            udir_update "$srcdir" "udir_types=($udir_types)" "${UINST_DEFAULTS[@]}"
        elif [ "$udir_types" == uinst:rsync ]; then
            local rsync_options
            estepn "Les options suivantes sont définies pour la synchronisation par rsync:
    -av --exclude CVS --exclude .svn --exclude /.git/
Vous pouvez en définir d'autres"
            read_value "Entrez des options de rsync" rsync_options "" N
            udir_update "$srcdir" "udir_types=($udir_types)" "${UINST_DEFAULTS[@]}" "rsync_options=($rsync_options)"
        elif [ "$udir_types" == uinst:python ]; then
            udir_update "$srcdir" "udir_types=($udir_types)"
        fi
    fi
    
    enote "Le répertoire $(ppath "$srcdir") a été configuré avec les valeurs par défaut pour uinst.
Utilisez 'udir -e $(ppath "$srcdir")' pour modifier les paramètres."
}

function __uinst_defaultvars() {
    eval "$(array_join UINST_DEFAULTS ';')"
    UDIR_VARS=(uinc install_profiles profiledir bashrcdir defaultdir copy_files destdir srcdir owner)
    UDIR_ARRAYS=(uinc_options uinc_args preconfig_scripts configure_variables configure_dest_for config_scripts workdir_rsync_options workdir_excludes workdir_includes files modes root_scripts)
}

function __uinst_protectvars() {
    local __uinst_var
    if [ -n "$*" ]; then
        # Enregistrer les noms des variables à protéger de la modification dans
        # une fonction qui source un fichier externe
        for __uinst_var in "$@"; do
            array_set UINST_PROTECTED_VARS "$__uinst_var"
        done
    else
        # Afficher des commandes à lancer pour protéger les variables dans la
        # fonction. A utiliser ainsi:
        #     eval "$(__uinst_protectvars)"
        for __uinst_var in "${UINST_PROTECTED_VARS[@]}"; do
            if [ "$__uinst_var" == UINST_PROTECTED_VARS ]; then
                echo "local $__uinst_var"
            else
                echo "local $__uinst_var=\"\$$__uinst_var\""
            fi
        done
    fi
}

function __uinst_do() {
    # Installer le répertoire $1. L'environnement doit d'abord être initialisé
    # avec __uinst_init() et éventuellement uinst()
    srcdir=
    for __uinst_var in "$@"; do
        if [[ "$__uinst_var" == *=* ]]; then
            __uinst_name="${__uinst_var%%=*}"
            __uinst_value="${__uinst_var#*=}"
            __uinst_addvar "$__uinst_name" "$__uinst_value"
        elif [ -z "$srcdir" ]; then
            srcdir="$__uinst_var"
            UINST_ORIGSRC="$srcdir"
        else
            ewarn "$__uinst_var: cet argument a été ignoré"
        fi
    done

    if [ -z "$srcdir" ]; then
        if __uinst_check_dir .; then
            srcdir="."
            UINST_ORIGSRC="$srcdir"
        fi
    fi
    if [ -z "$srcdir" ]; then
        __uinst_found=
        if [ -n "$UINST_AUTOSRCDIR" ]; then
            parentdirs srcdirs ..
            for srcdir in "${srcdirs[@]}"; do
                if __uinst_check_dir "$srcdir"; then
                    __uinst_found=1
                    break
                fi
            done
        fi
        [ -n "$__uinst_found" ] || {
            eerror "\
Impossible de déterminer automatiquement le répertoire à installer
Essayez avec 'uinst -C'"
            return 1
        }
        ask_yesno "Le répertoire '$(ppath "$srcdir")' a été calculé automatiquement. Voulez-vous le déployer?" X || return 1
        UINST_ORIGSRC="$srcdir"
    fi

    # Tester s'il faut bootstrapper
    if [ -f "$srcdir/.nutools-bootstrap" ]; then
        local need_python need_gawk
        has_python || need_python=1
        has_gawk || need_gawk=1
        if check_sysinfos -s linux; then
            if [ -n "$need_python" -o -n "$need_gawk" ]; then
                eimportant "Il FAUT installer Python et $(get_color y)*GNU*$(get_color z)awk pour que nutools fonctionne correctement."
                if check_sysinfos -d debian; then
                    if ask_yesno "Voulez-vous que ce script essaye d'installer automatiquement ces dépendances (requière les droits root)?" O; then
                        urequire debian
                        pkg_install ${need_python:+python} ${need_gawk:+gawk} || {
                            eerror "Une erreur s'est produite pendant l'installation. Veuillez faire l'installation manuellement"
                            return 1
                        }
                        need_python=
                        need_gawk=
                    fi
                fi
            fi
        fi
        if [ -n "$need_python" ]; then
            eerror "Python est requis. Veuillez faire l'installation avant de relancer ce script."
            return 1
        fi
        if [ -n "$need_gawk" ]; then
            ewarn "$(get_color y)*GNU*$(get_color z)awk est requis mais n'est pas installé. Ce script va continuer, mais les résultats ne sont pas garantis."
        fi
        # s'assurer que les libraries *locales* sont dans PYTHONPATH
        source "$ULIBDIR/pyulib/pyulib"
    fi

    if [ -n "$UINST_AUTOPREFIX" ]; then
        # initialiser le moteur de préfixes
        urequire PREFIXES-DEFAULTS
        compute_all_prefixes
        srcdir="$(expand_prefix "$srcdir")"
    fi
    [ -e "$srcdir" ] || {
        eerror "$srcdir: fichier ou répertoire introuvable"
        return 1
    }
    UINST_SRCDIR="$(abspath "$srcdir")"
    __uinst_addvar srcdir "$UINST_SRCDIR"

    __uinst_dispatch
}

function __uinst_addvarnf() {
    # Ajouter une variable et sa valeurs, sans l'enregistrer dans la liste des
    # variables modifiées par l'utilisateur.
    # $1=name, $2=value
    if array_contains UINST_PROTECTED_VARS "$1"; then
        local OENC="$UTF8"
        eerror "La variable $1 est protégée"
        return 1
    else
        array_add UINST_CONFIG_VARCMDS "$(set_var_cmd "$1" "$2")"
        set_var "$1" "$2"
        if [ "$1" == "MYHOST" ]; then
            # cas particulier: initialiser aussi MYHOSTNAME
            set_var "MYHOSTNAME" "${2%%.*}"
            array_add UINST_CONFIG_VARCMDS "$(set_var_cmd "MYHOSTNAME" "$MYHOSTNAME")"
        fi
        return 0
    fi
}

function __uinst_addvar() {
    # Ajouter une variable et sa valeur, et l'enregistrer dans la liste des
    # variables spécifiées par l'utilisateur.
    # $1=name, $2=value
    if __uinst_addvarnf "$@"; then
        array_set UINST_CONFIG_VARS "$1"
    fi
}

function __uinst_setvar() {
    # Evaluer la variable $1 en prenant sa valeur dans CONFIG_VARCMDS
    local __uinst_varcmd
    for __uinst_varcmd in "${UINST_CONFIG_VARCMDS[@]}"; do
        if beginswith "$__uinst_varcmd" "$1="; then
            eval "$__uinst_varcmd"
            # ne pas faire break, parce que plusieurs occurences peuvent
            # exister, les dernières écrasant les premières
        fi
    done
}

function __uinst_setvars() {
    # Evaluer toute les variables de CONFIG_VARCMDS pour initialiser leurs
    # valeurs
    local __uinst_varcmd
    for __uinst_varcmd in "${UINST_CONFIG_VARCMDS[@]}"; do
        eval "$__uinst_varcmd"
    done
}

function __uinst_printmergedvars() {
    # Construire une liste de variables dont il faut afficher la valeur:
    # - les variables qui ont été mentionnées sur la ligne de commandes
    # - les variables mentionnées dans le tableau configure_variables
    # Puis afficher ces variables et leurs valeurs
    local -a __uinst_mvars
    local __uinst_mvar
    array_copy __uinst_mvars UINST_CONFIG_VARS
    array_extend __uinst_mvars configure_variables
    for __uinst_mvar in "${__uinst_mvars[@]}"; do
        estep "$__uinst_mvar=${!__uinst_mvar}"
    done
}

function __uinst_check_dir() {
    # Tester si le répertoire $1 est installable par uinst
    __uinst_check_udir "$1" ||
    __uinst_check_legacy "$1" ||
    __uinst_check_rsync "$1" ||
    __uinst_check_python "$1"
}

function __uinst_check_uinst2s() {
    # Vérifier le script de second étage $1 existe, et initialiser la variable
    # uinst2s avec le chemin vers le script
    uinst2s="$ULIBDIR/support/${1:-uinst2s}"
    [ -x "$uinst2s" ] || {
        eerror "Script ${uinst2s#$ULIBDIR/support/} introuvable dans '$(dirname "$uinst2s")'"
        return 1
    }
}

function __uinst_dispatch() {
    # Sélectionner la méthode appropriée pour installer $srcdir
    if __uinst_check_udir "$srcdir"; then
        # Répertoire uinst configuré avec udir
        __uinst_udir || return 1
    elif __uinst_check_legacy "$srcdir"; then
        # Répertoire uinst original
        __uinst_legacy || return 1
    elif __uinst_check_rsync "$srcdir"; then
        # Fichiers à déployer avec rsync
        __uinst_rsync || return 1
    elif __uinst_check_python "$srcdir"; then
        # Package python
        __uinst_python || return 1
    elif __uinst_check_archive "$srcdir"; then
        # Archive
        __uinst_archive || return 1
    elif [ -f "$srcdir" ]; then
        # Fichier simple
        __uinst_file || return 1
    else
        eerror "$srcdir: impossible de déployer un répertoire non préparé.
Veuillez préparer ce répertoire avec 'uinst -C $(ppath "$srcdir")'
Si le répertoire a déjà été préparé, vérifier la présence de l'une des valeurs
'uinst', 'uinst:legacy', 'uinst:rsync' ou 'uinst:python' dans la variable
udir_types."
        return 1
    fi
}

function __uinst_prepare_workdir() {
    # Faire une copie de travail de $srcdir dans $1
    # srcdir est modifié pour devenir la nouvelle valeur
    local srcname="$(basename "$srcdir")"
    local destdir="$1/$srcname"

    estep "Création d'une copie de travail dans $1..."
    if [ ${#workdir_excludes[*]} -gt 0 -o ${#workdir_includes[*]} ]; then
        # on a spécifié des inclusions ou exclusions pour le répertoire de
        # travail
        (
            if ! array_isempty workdir_rsync_options; then
                array_copy __CPNOVCS_RSYNC_ARGS workdir_rsync_options
            fi
            for exclude in "${workdir_excludes[@]}"; do
                array_add __CPNOVCS_RSYNC_ARGS --exclude "$exclude"
            done
            for include in "${workdir_includes[@]}"; do
                array_add __CPNOVCS_RSYNC_ARGS --include "$include"
            done
            cd "$srcdir"
            "${UINST_COPY_METHOD:-cpdirnovcs}" . "$destdir"
        )
    else
        # copie simple du répertoire de travail
        "${UINST_COPY_METHOD:-cpdirnovcs}" "$srcdir" "$destdir"
    fi

    srcdir="$destdir"
    chmod -R +w "$srcdir" || return 1
    return 0
}

function __uinst_prepare_ulib() {
    # Copie l'environnement de déploiement dans $UINST_PREPARE_DIR
    estep "Copie de l'environnement de déploiement"
    ulibsync "$UINST_PREPARE_DIR"
    pyulibsync "$UINST_PREPARE_DIR"
    echo '#!/bin/sh
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
if . `dirname "$0"`/ulib/ulibsh; then
    urequire DEFAULTS sysinfos compat uinst udir prefixes uinc
else
    exit 1
fi
OENC="$UTF8"
uinst "$@"' >"$UINST_PREPARE_DIR/uinst.sh"
    chmod +x "$UINST_PREPARE_DIR/uinst.sh"
}

function __uinst_prepare_conf() {
    # créer le fichier de configuration $UINST_PREPARE_CONF avec
    # prepare_name=$srcdir
    # srcdir est un chemin relatif à $UINST_PREPARE_DIR (si la source était une
    # archive, il est possible que ce chemin soit en plusieurs parties
    set_var_cmd prepare_name "$(relpath "$srcdir" "$UINST_PREPARE_DIR")" >"$UINST_PREPARE_CONF"
}

################################################################################
# uinst: udir

function __uinst_check_udir() {
    # Vérifier si $1 est un répertoire configuré avec udir, installable avec
    # uinst
    if [ -d "$1" -a -f "$1/.udir" ]; then
        udir_eval "$1" 'array_contains udir_types uinst' && return 0
    fi
    return 1
}

function __uinst_udir() {
    # Installer le répertoire $srcdir qui contient la configuration dans le
    # fichier .udir
    local uinst2s
    __uinst_check_uinst2s || return 1

    # initialiser les valeurs par défaut, mais penser à restaurer srcdir
    __uinst_defaultvars
    __uinst_setvar srcdir
    # puis charger les valeurs de $srcdir/.udir, mais penser à restaurer srcdir
    eval "$(udir_dump "$srcdir")"
    __uinst_setvar srcdir
    # initialiser UDIR_VARS et UDIR_ARRAYS. cela permettra de construire le
    # fichier de configuration pour les scripts et le déploiement
    udir_parse "$srcdir"
    # puis charger les valeurs de la ligne de commande
    __uinst_setvars
    # corriger éventuellement destdir
    [ -n "$UINST_AUTOPREFIX" ] && destdir="$(expand_prefix "$destdir")"
    # afficher les valeurs dignes d'intérêt
    __uinst_printmergedvars

    # puis lancer le déploiement
    __uinst_udir2s
}

function __uinst_udir2s() {
    # Installer le répertoire $srcdir
    local OENC="$UTF8"

    if [ "$UINST_ACTION" == prepare-1s ]; then
        : # pas de confirmation en mode prépare
    elif is_yes "$copy_files"; then
        ask_yesno "Déployer $(ppath "$srcdir") dans $(ppath "$destdir") sur $MYHOST?" O || return 1
    else
        ask_yesno "Déployer $(ppath "$srcdir") sur $MYHOST?" O || return 1
    fi

    local origsrcdir="$srcdir" # utilisé par uinc
    local srcname="$(basename "$srcdir")"
    local dest="$destdir/$srcname"

    ## Configurer la source: faire éventuellement une copie de travail
    if [ -z "$UINST_ISTMPDIR" ]; then
        if [ "$UINST_ACTION" == prepare-1s ]; then
            # Faire la copie de travail pour le prédéploiement dans le
            # répertoire spécifié
            __uinst_prepare_workdir "$UINST_PREPARE_DIR" || return 1
        else
            # Si nous ne sommes pas dans un répertoire temporaire, alors faire
            # une copie de travail
            ac_set_tmpdir UINST_TMPDIR
            __uinst_prepare_workdir "$UINST_TMPDIR" || return 1
        fi
    fi

    ## Déplier les inclusions
    # Ne déplier les inclusions que maintenant, parce que le répertoire initial
    # était peut-être protégé en écriture.
    # ne pas déplier si on est dans la deuxième phase de l'étape de préparation
    if [ "$UINST_ACTION" != prepare-2s ] && is_yes "$uinc"; then
        # uinc doit charger les paramètres de .udir/.uinst.conf, d'où le --auto
        # par contre, on doit forcer les paramètres --refdir et -u
        etitle "Dépliage des inclusions" \
            uinc -y --auto --refdir "$origsrcdir" -C uinst -u "$srcdir"
    fi

    ## Prédéploiement
    if [ "$UINST_ACTION" == prepare-1s ]; then
        # Fin de traitement si on est dans la première phase de l'étape de
        # préparation
        [ -n "$UINST_PREPARE_ULIB" ] && __uinst_prepare_ulib
        __uinst_prepare_conf
        return 0
    fi

    ## Faire le fichier de configuration
    local __uinst_configdir __uinst_config __uinst_abort
    local __uinst_var
    local -a __uinst_vars __uinst_arrays
    for __uinst_var in "${UINST_CONFIG_VARS[@]}" "${configure_variables[@]}"; do
        if array_contains UDIR_ARRAYS "$var_"; then
            array_set __uinst_arrays "$__uinst_var"
        else
            array_set __uinst_vars "$__uinst_var"
        fi
    done
    for __uinst_var in "${UDIR_VARS[@]}"; do
        array_set __uinst_vars "$__uinst_var"
    done
    for __uinst_var in "${UDIR_ARRAYS[@]}"; do
        array_set __uinst_arrays "$__uinst_var"
    done

    ac_set_tmpdir __uinst_configdir
    __uinst_config="$__uinst_configdir/config"
    __uinst_abort="$__uinst_configdir/abort"
    __uinst_updateconfig="$__uinst_configdir/updateconfig"
    set_var_cmd ABORT "$__uinst_abort" >>"$__uinst_config"
    set_var_cmd CONFIG "$__uinst_updateconfig" >>"$__uinst_config"
    set_var_cmd __verbosity "$__verbosity" >>"$__uinst_config"
    set_var_cmd __interaction "$__interaction" >>"$__uinst_config"
    for __uinst_var in "${__uinst_vars[@]}"; do
        set_var_cmd "$__uinst_var" "${!__uinst_var}" >>"$__uinst_config"
    done
    for __uinst_var in "${__uinst_arrays[@]}"; do
        set_array_cmd "$__uinst_var" >>"$__uinst_config"
    done
    set_var_cmd "UINST_ISTMPDIR" "$UINST_ISTMPDIR" >>"$__uinst_config"
    set_var_cmd "UINST_ORIGSRC" "$UINST_ORIGSRC" >>"$__uinst_config"
    set_var_cmd "UINST_SRCDIR" "$UINST_SRCDIR" >>"$__uinst_config"
    set_var_cmd "ULIBDIR" "$ULIBDIR" >>"$__uinst_config"
    set_var_cmd "UINST" "$UINST" >>"$__uinst_config"
    # ulib recalcule systématiquement la valeur de ULIBDIR. Pareil pour
    # pyulib/pyulib. Mais cela ne fonctionne pas si nous déployons sur une
    # machine avec bash 2.x. Il faut donc forcer l'utilisation de la valeur
    # calculée.
    set_var_cmd "FORCED_ULIBDIR" "$ULIBDIR" >>"$__uinst_config"
    set_var_cmd "FORCED_PYULIBDIR" "$PYULIBDIR" >>"$__uinst_config"

    ## Lancer les scripts de préconfiguration
    function __uinst_preconfig_scripts() {
        for preconfig_script in "${preconfig_scripts[@]}"; do
            rm -f "$__uinst_abort" "$__uinst_updateconfig"
            etitle "$preconfig_script" \
                "${BASH:-/bin/sh}" "$srcdir/$preconfig_script" "$__uinst_config"
            [ -f "$__uinst_abort" ] && return 1
            if [ -f "$__uinst_updateconfig" ]; then
                cat "$__uinst_updateconfig" >>"$__uinst_config"
                source "$__uinst_updateconfig"
            fi
        done
        return 0
    }
    etitle "Lancement des scripts de préconfiguration" __uinst_preconfig_scripts || return 1

    ## Configurer les variables
    if [ -n "${configure_variables[*]}" ]; then
        function __uinst_config_variables() {
            local __uinst_cvar __uinst_cvarname __uinst_cfilespecs

            for __uinst_cvarname in "${configure_variables[@]}"; do
                __uinst_cvar="${!__uinst_cvarname}"
                __uinst_cvar="${__uinst_cvar//,/\\,}"
                
                etitle -s "$__uinst_cvarname"
                __uinst_cfilespecs="configure_${__uinst_cvarname}_for[*]"
                if [ -n "${!__uinst_cfilespecs}" ]; then
                    array_copy __uinst_cfilespecs "configure_${__uinst_cvarname}_for"
                    for __uinst_cfilespec in "${__uinst_cfilespecs[@]}"; do
                        splitwcs "$__uinst_cfilespec" __uinst_cfilen __uinst_cfilewc
                        if [ -n "$__uinst_cfilewc" ]; then
                            # il y a des wildcards
                            array_from_lines __uinst_cfiles "$(list_files "$srcdir/$__uinst_cfilen" "$__uinst_cfilewc")"
                            for __uinst_cfile in "${__uinst_cfiles[@]}"; do
                                __uinst_cfile="$__uinst_cfilen/$__uinst_cfile"
                                estep "$__uinst_cfile"
                                if quietgrep "@@${__uinst_cvarname}@@" "$srcdir/$__uinst_cfile"; then
                                    sedi "s|@@${__uinst_cvarname}@@|$__uinst_cvar|g" "$srcdir/$__uinst_cfile"
                                fi
                            done
                        else
                            # pas de wildcards
                            __uinst_cfile="$__uinst_cfilespec"
                            estep "$__uinst_cfile"
                            if quietgrep "@@${__uinst_cvarname}@@" "$srcdir/$__uinst_cfile"; then
                                sedi "s|@@${__uinst_cvarname}@@|$__uinst_cvar|g" "$srcdir/$__uinst_cfile"
                            fi
                        fi
                    done
                fi
                eend
            done
        }
        etitle "Configuration des variables" __uinst_config_variables
    fi

    ## Lancer les scripts de configuration
    function __uinst_config_scripts() {
        for config_script in "${config_scripts[@]}"; do
            rm -f "$__uinst_abort"
            etitle "$config_script" \
                "${BASH:-/bin/sh}" "$srcdir/$config_script" "$__uinst_config"
            [ -f "$__uinst_abort" ] && return 1
        done
        return 0
    }
    etitle "Lancement des scripts de configuration" __uinst_config_scripts || return 1

    ## Installer les profils
    if is_yes "$install_profiles"; then
        urequire uenv uenv_update
        etitle "Installation des profils" \
            uenv_install_profiles "$srcdir" "$profiledir" "$bashrcdir" "$defaultdir"
    fi

    ## Déploiement
    # seulement s'il y a des copies de fichier ou des scripts roo
    if is_yes "$copy_files" || [ -n "${root_scripts[*]}" ]; then
        etitle "Lancement du déploiement" runscript_as_root "$uinst2s" "$__uinst_config"
    fi

    return 0
}

################################################################################
# uinst: rsync

function __uinst_check_rsync() {
    # Vérifier si $1 est un répertoire à déployer avec rsync
    if [ -d "$1" -a -f "$1/.udir" ]; then
        udir_eval "$1" 'array_contains udir_types uinst:rsync' && return 0
    fi
    return 1
}

function __uinst_rsync() {
    # Déployer les fichiers de $srcdir avec rsync

    # initialiser les valeurs par défaut, mais penser à restaurer srcdir
    force_rsync_options=(-av --exclude CVS --exclude .svn --exclude /.git/)
    rsync_options=()
    __uinst_defaultvars
    __uinst_setvar srcdir
    # puis charger les valeurs de $srcdir/.udir, mais penser à restaurer srcdir
    eval "$(udir_dump "$srcdir")"
    __uinst_setvar srcdir
    # puis charger les valeurs de la ligne de commande
    __uinst_setvars
    # corriger éventuellement destdir
    [ -n "$UINST_AUTOPREFIX" ] && destdir="$(expand_prefix "$destdir")"
    # les variables de configure_variables ne sont pas prises en compte. pas la
    # peine de les afficher
    configure_variables=()
    # afficher les valeurs dignes d'intérêt
    __uinst_printmergedvars

    if [ "$UINST_ACTION" == prepare-1s ]; then
        # Faire la copie de travail pour le prédéploiement dans le répertoire
        # spécifié
        __uinst_prepare_workdir "$UINST_PREPARE_DIR" || return 1
        [ -n "$UINST_PREPARE_ULIB" ] && __uinst_prepare_ulib
        __uinst_prepare_conf
        return 0
    else
        local srcdesc="$(ppath "$srcdir")"
        if [ -n "${files[*]}" ]; then
            srcdesc="$srcdesc/{$(array_join files ,)}"
        fi
        ask_yesno "Synchroniser $srcdesc vers $(ppath "$destdir") sur $MYHOST?" O || return
    fi

    cd "$srcdir" || return 1
    local localsync=1
    if [[ "$destdir" == *:* ]]; then
        # Déploiement distant, on laisse rsync gérer
        localsync=
    else
        # Déploiement local. S'assurer que le répertoire de destination existe
        mkdir -p "$destdir" || return 1
    fi
    
    local -a cmd tmp_files actual_files copied_files
    local file filename
    cmd=(rsync "${force_rsync_options[@]}" "${rsync_options[@]}")
    if [ -n "${files[*]}" ]; then
        for file in "${files[@]}"; do
            file="${file#/}" # les chemins sont toujours relatifs
            if [ -e "$file" ]; then
                tmp_files=("$file")
            else
                array_from_lines tmp_files "$(list_all . "$file")"
            fi
            array_extend actual_files tmp_files
        done
        for file in "${actual_files[@]}"; do
            cmd=("${cmd[@]}" "$file")
            filename="$(basename "$file")"
            copied_files=("${copied_files[@]}" "$destdir/$filename")
        done
    else
        cmd=("${cmd[@]}" ./)
        array_from_lines actual_files "$(list_all . "*" ".*")"
        for file in "${actual_files[@]}"; do
            filename="$(basename "$file")"
            copied_files=("${copied_files[@]}" "$destdir/$filename")
        done
    fi
    cmd=("${cmd[@]}" "$destdir")

    # Faire la synchro
    "${cmd[@]}"

    if [ -n "$localsync" ]; then
        if [ -n "$owner" -a -n "${copied_files[*]}" ]; then
            ## Initialiser le propriétaire
            estep "Initialisation du propriétaire à $owner"
            chown -R "$owner" "${copied_files[@]}" || return 1
        fi
    
        if [ -n "${modes[*]}" ]; then
            ## Initialiser les modes
            for mode in "${modes[@]}"; do
                file="${mode%:*}"
                if [ "$file" != "$mode" ]; then
                    # une spécification de mode pour un fichier spécifique
                    mode="${mode##*:}"
                    estep "Initialisation du mode à $mode pour $file"
                    chmod "$mode" "$destdir/$file" || return 1
                elif [ -n "${copied_files[*]}" ]; then
                    # une spécification de mode pour tous les fichiers déployés
                    estep "Initialisation du mode à $mode"
                    chmod -R "$mode" "${copied_files[@]}" || return 1
                fi
            done
        fi
    fi
}

################################################################################
# uinst: legacy

function __uinst_check_legacy() {
    # Vérifier si $1 est un répertoire à installer avec la version originale de
    # uinst
    if [ -d "$1" -a -f "$1/.udir" ]; then
        udir_eval "$1" 'array_contains udir_types uinst:legacy' && return
    fi
    [ -d "$1" -a -f "$1/.uinst.conf" ]
}

function __uinst_migrate_legacy() {
    local srcdir # XXX srcdir ne doit pas être écrasé, bien que .uinst.conf
                 # contienne la variable srcdir. Il faut donc protéger cette
                 # variable avec 'local srcdir'
    local __uinst_srcdir="$1"

    # initialiser les valeurs par défaut
    update_inc=false
    update_inc_options=
    update_inc_args=.
    configure_variables=dest
    configure_dest_for=
    config_scripts=
    install_profiles=false
    profiledir=lib/profile.d
    bashrcdir=lib/bashrc.d
    defaultdir=lib/default
    copy_files=true
    destdir=/usr/local
    srcdir=.
    files=
    owner="root:"
    modes="u=rwX,g=rX,o=rX"
    root_scripts=
    # puis charger les valeurs de $srcdir/.uinst.conf
    # initialiser aussi UDIR_VARS et UDIR_ARRAYS. cela permettra de construire
    # le fichier de configuration pour les scripts et le déploiement
    
    if [ -f "$__uinst_srcdir/.uinst.conf" ]; then
        # note: les regex qui sont entre "" au lieu de // le sont à cause d'un bug
        # de awk sous macosx
        source "$__uinst_srcdir/.uinst.conf"
        eval "$(<"$__uinst_srcdir/.uinst.conf" filter_comment -m | awk 'BEGIN {
  vars = "UDIR_VARS=("; first_var = 1;
  arrays = "UDIR_ARRAYS=("; first_array = 1;
}
/^[ \t]*#/ { next }
$0 ~ /^[ \t]*[a-zA-Z_][a-zA-Z0-9_]*=\(/ {
  match($0, /^[ \t]*[a-zA-Z_][a-zA-Z0-9_]*=\(/)
  name = substr($0, RSTART, RLENGTH)
  sub(/^([ \t]*)?/, "", name)
  sub("=\\($", "", name)

  if (first_array) first_array = 0
  else arrays = arrays " "
  arrays = arrays name
  next
}
$0 ~ /^[ \t]*[a-zA-Z_][a-zA-Z0-9_]*=/ {
  match($0, /^[ \t]*[a-zA-Z_][a-zA-Z0-9_]*=/)
  name = substr($0, RSTART, RLENGTH)
  sub(/^([ \t]*)?/, "", name)
  sub("=$", "", name)

  if (first_var) first_var = 0
  else vars = vars " "
  vars = vars name
  next
}
END {
  print vars ")"
  print arrays ")"
}')"
    else
        UDIR_VARS=(copy_files destdir srcdir files owner modes update_inc update_inc_options update_inc_args configure_variables configure_dest_for config_scripts install_profiles profiledir bashrcdir defaultdir root_scripts)
        UDIR_ARRAYS=()
    fi
    # traduire les valeurs à la mode udir.

    #XXX à commenter tant que l'on ne fait pas clairement la différence entre
    # srcdir, le répertoire qu'il faut *copier* et uinstdir (ou projdir), le
    # répertoire qu'il faut déployer
    #if [ "$srcdir" != "." ]; then
    #    __uinst_addvar srcdir "$(abspath "$srcdir" "$__uinst_srcdir")"
    #fi

    uinc="$update_inc"; \
        unset update_inc; \
        array_del UDIR_VARS update_inc; \
        array_set UDIR_VARS uinc
    uinc_options=($update_inc_options); \
        unset update_inc_options; \
        array_del UDIR_VARS update_inc_options; \
        array_set UDIR_ARRAYS uinc_options
    uinc_args=($update_inc_args); \
        unset update_inc_args; \
        array_del UDIR_VARS update_inc_args; \
        array_set UDIR_ARRAYS uinc_args
    for __uinst_a in configure_variables config_scripts root_scripts files; do
        array_from_lines "$__uinst_a" "${!__uinst_a}"; \
            array_del UDIR_VARS "$__uinst_a"; \
            array_set UDIR_ARRAYS "$__uinst_a"
    done
    for __uinst_cv in "${configure_variables[@]}"; do
        __uinst_cv="configure_${__uinst_cv}_for"
        array_from_lines "$__uinst_cv" "${!__uinst_cv}"; \
            array_del UDIR_VARS "$__uinst_cv"; \
            array_set UDIR_ARRAYS "$__uinst_cv"
    done
    modes=($modes); \
        array_del UDIR_VARS modes; \
        array_set UDIR_ARRAYS modes
}

function __uinst_legacy() {
    # Installer le répertoire $srcdir qui contient la configuration dans le
    # fichier .uinst.conf
    local uinst2s
    __uinst_check_uinst2s || return 1

    # charger les valeurs de $srcdir/.uinst.conf
    __uinst_migrate_legacy "$srcdir"
    # puis charger les valeurs de la ligne de commande
    __uinst_setvars
    # corriger éventuellement destdir
    [ -n "$UINST_AUTOPREFIX" ] && destdir="$(expand_prefix "$destdir")"
    # afficher les valeurs dignes d'intérêt
    __uinst_printmergedvars

    # puis lancer le déploiement
    __uinst_udir2s
}

################################################################################
# uinst: python

function __uinst_check_python() {
    # Vérifier si $1 est un répertoire d'un produit python à installer avec
    # setup.py
    [ -d "$1" -a -f "$1/setup.py" ]
}

function __uinst_python() {
    # Installer le package python $srcdir
    local uinst2s
    __uinst_check_uinst2s uinst2s_python || return 1

    if [ "$UINST_ACTION" == prepare-1s ]; then
        # Faire la copie de travail pour le prédéploiement dans le répertoire
        # spécifié
        __uinst_prepare_workdir "$UINST_PREPARE_DIR" || return 1
        [ -n "$UINST_PREPARE_ULIB" ] && __uinst_prepare_ulib
        __uinst_prepare_conf
        return 0
    else
        ask_yesno "Déployer $(ppath "$srcdir") sur $MYHOST?" O || return 1

        UINST_ORIGSRC="$srcdir"
        UINST_SRCDIR="$(abspath "$srcdir")"
    fi

    local __uinst_config __uinst_var
    ac_set_tmpfile __uinst_config
    for __uinst_var in srcdir; do
        set_var_cmd "$__uinst_var" "${!__uinst_var}" >>"$__uinst_config"
    done
    set_var_cmd "UINST_ISTMPDIR" "$UINST_ISTMPDIR" >>"$__uinst_config"
    set_var_cmd "UINST_ORIGSRC" "$UINST_ORIGSRC" >>"$__uinst_config"
    set_var_cmd "UINST_SRCDIR" "$UINST_SRCDIR" >>"$__uinst_config"
    set_var_cmd "ULIBDIR" "$ULIBDIR" >>"$__uinst_config"
    set_var_cmd "UINST" "$UINST" >>"$__uinst_config"
    runscript_as_root "$uinst2s" "$__uinst_config"
}

################################################################################
# uinst: archive

function __uinst_check_archive() {
    # Vérifier si $1 est une archive installable avec uinst
    is_archive "$1"
}

function __uinst_archname() {
    # essayer de déterminer un nom de base pour le répertoire de destination
    # d'une archive à décompresser: l'extension et le numéro de version sont
    # supprimés du nom de l'archive

    local archname="$(basename "$1")"
    # supprimer l'extension
    archname="${archname%.zip}"
    archname="${archname%.tgz}"
    archname="${archname%.tar.gz}"
    archname="${archname%.tbz2}"
    archname="${archname%.tar.bz2}"
    archname="${archname%.tar}"
    archname="${archname%.jar}"
    archname="${archname%.war}"
    # supprimer la version
    archname="$(echo "$archname" | awk '{sub(/-[0-9]+(\.[0-9]+)*$/, ""); print}')"
    # résultat
    echo "${archname:-archive}"
}

function __uinst_archive() {
    # Installer l'archive $srcdir. Elle est décompactée pour déterminer le type
    # d'installation qu'il faut pour elle

    if [ "$UINST_ACTION" == prepare-1s ]; then
        # Décompacter l'archive dans le répertoire spécifié
        UINST_TMPDIR="$UINST_PREPARE_DIR"
    else
        # Décompacter l'archive dans un répertoire temporaire
        ac_set_tmpdir UINST_TMPDIR

        UINST_ORIGSRC="$srcdir"
    fi
    estep "Décompactage d'une copie de travail dans $UINST_TMPDIR..."
    local archdir="$UINST_TMPDIR/$(__uinst_archname "$srcdir")"
    mkdir -p "$archdir" || return 1
    extract_archive "$srcdir" "$archdir" || return 1
    UINST_SRCDIR="$(abspath "$archdir")"

    # Décomptacter l'archive dans un répertoire temporaire
    # s'il n'y a qu'un seul répertoire dans l'archive, le considérer comme le
    # répertoire à déployer
    local -a contents_
    array_lsall contents_ "$archdir"
    if [ "${#contents_[*]}" -eq 1 ]; then
        archdir="${contents_[0]}"
    fi

    UINST_ISTMPDIR=1
    __uinst_addvarnf srcdir "$archdir"
    __uinst_dispatch
}

################################################################################
# uinst: file

function __uinst_check_file() {
    # Vérifier si $1 est un fichier installable avec uinst
    [ -f "$1" ]
}

function __uinst_file() {
    # Installer le fichier simple $srcdir
    local uinst2s
    __uinst_check_uinst2s || return 1
    __uinst_defaultvars
    __uinst_setvars
    [ -n "$UINST_AUTOPREFIX" ] && destdir="$(expand_prefix "$destdir")"
    __uinst_printmergedvars

    if [ "$UINST_ACTION" == prepare-1s ]; then
        # Faire la copie de travail pour le prédéploiement dans le répertoire
        # spécifié
        local srcname="$(basename "$srcdir")"
        
        estep "Création d'une copie de travail dans $UINST_PREPARE_DIR..."
        "${UINST_COPY_METHOD:-cpnovcs}" "$srcdir" "$UINST_PREPARE_DIR"
        
        srcdir="$UINST_PREPARE_DIR/$srcname"
        chmod +w "$srcdir" || return 1
        
        [ -n "$UINST_PREPARE_ULIB" ] && __uinst_prepare_ulib
        __uinst_prepare_conf
        return 0
    else
        ask_yesno "Déployer $(ppath "$srcdir") dans $(ppath "$destdir")?" O || return 1

        UINST_ORIGSRC="$srcdir"
        UINST_SRCDIR="$(dirname "$(abspath "$srcdir")")"
    fi

    local __uinst_config __uinst_var
    ac_set_tmpfile __uinst_config
    for __uinst_var in copy_files destdir srcdir owner modes; do
        set_var_cmd "$__uinst_var" "${!__uinst_var}" >>"$__uinst_config"
    done
    for __uinst_var in files modes; do
        set_array_cmd "$__uinst_var" >>"$__uinst_config"
    done
    set_var_cmd "UINST_ISTMPDIR" "$UINST_ISTMPDIR" >>"$__uinst_config"
    set_var_cmd "UINST_ORIGSRC" "$UINST_ORIGSRC" >>"$__uinst_config"
    set_var_cmd "UINST_SRCDIR" "$UINST_SRCDIR" >>"$__uinst_config"
    set_var_cmd "ULIBDIR" "$ULIBDIR" >>"$__uinst_config"
    set_var_cmd "UINST" "$UINST" >>"$__uinst_config"
    runscript_as_root "$uinst2s" "$__uinst_config"
}

################################################################################
# uinst2s

function __uinst2s_copy_files() {
    # Copier les fichiers files de srcdir dans destdir
    # Cette fonction est utilisée par le script uinst2s
    srcname="$(basename "$srcdir")"
    destdir="$(abspath "$destdir")"
    dest="$destdir/$srcname"
    copied_files=()

    # pour la copie "atomique" du répertoire de destination (en réalité, on
    # cherche à minimiser le temps d'indisponibilité du répertoire destination)
    atomic=
    actualdest=
    
    if [ -f "$srcdir" ]; then
        ## Copie d'un fichier dans un répertoire
        if [ ! -d "$destdir" ]; then
            estep "Création de $destdir"
            mkdir -p "$destdir" || return 1
        fi
        
        estep "Copie de $(ppath "$srcdir") dans $destdir"
        cpdir "$srcdir" "$destdir" || return 1
        copied_files=("${copied_files[@]}" "$destdir/$(basename "$srcdir")")

    elif [ -d "$srcdir" ]; then
        ## Copie d'un répertoire ou d'une partie de son contenu dans un
        ## répertoire de destination
        if [ -z "${files[*]}" ]; then
            # On n'a pas spécifié de liste de fichiers. C'est tout le répertoire
            # source qui est copié, et il remplace la destination.
            if [ -d "$dest" ]; then
                # Le répertoire existe déjà. Tenter de faire une copie la plus
                # atomique possible: déployer dans un répertoire temporaire, qui
                # sera renommé vers la destination au dernier moment
                eimportant "Le répertoire destination $dest sera écrasé"
                if is_interaction -C; then
                    ebegin "Attente de 3 secondes"
                    sleep 1; edot
                    sleep 1; edot
                    sleep 1; edot
                    eend
                fi

                atomic=1
                actualdest="$dest"
                dest="$actualdest.copy$$"
                i=0
                while [ -d "$dest" ]; do
                    dest="$actualdest.copy$$-$i"
                    i=$(($i + 1))
                done
            else
                enote "Le répertoire destination est $dest"
            fi
            mkdir -p "$dest" || return 1

            estep "Copie de $(ppath "$srcdir")"
            cpdir "$srcdir" "$dest" || return 1
            copied_files=("${copied_files[@]}" "$dest")
            
        else
            # On a spécifié une liste de fichiers. Seuls ces fichiers sont
            # copiés, et le répertoire de destination n'est pas écrasé
            enote "La copie se fera dans $destdir"

            mkdir -p "$destdir" || return 1
            for file in "${files[@]}"; do
                estep "Copie de $file"
                if [ -e "$srcdir/$file" ]; then
                    # fichier existant
                    actual_files=("$file")
                    wildcards=
                else
                    # fichier non existant: c'est peut-être une spécification de
                    # fichier avec des wildcards
                    array_from_lines actual_files "$(list_all "$srcdir" "$file")"
                    wildcards=1
                fi
                
                local srcfile srcfilename
                for file in "${actual_files[@]}"; do
                    srcfile="$srcdir/$file"
                    srcfilename="$(basename "$file")"
                    [ -n "$wildcards" ] && echo "... $file" 1>&2
                    if [ -d "$srcfile" ]; then
                        cpdir "$srcfile" "$destdir/$srcfilename" || return 1
                    else
                        cpdir "$srcfile" "$destdir" || return 1
                    fi
                    copied_files=("${copied_files[@]}" "$destdir/$srcfilename")
                done
            done
        fi
    else
        ewarn "$srcdir: fichier ignoré"
    fi

    if [ -n "$owner" -a -n "${copied_files[*]}" ]; then
        ## Initialiser le propriétaire
        estep "Initialisation du propriétaire à $owner"
        chown -R "$owner" "${copied_files[@]}" || return 1
    fi
    
    if [ -n "${modes[*]}" ]; then
        ## Initialiser les modes
        for mode in "${modes[@]}"; do
            file="${mode%:*}"
            if [ "$file" != "$mode" ]; then
                # une spécification de mode pour un fichier spécifique
                mode="${mode##*:}"
                estep "Initialisation du mode à $mode pour $file"
                if [ -z "${files[*]}" ]; then
                    chmod "$mode" "$dest/$file" || return 1
                else
                    chmod "$mode" "$destdir/$file" || return 1
                fi
            elif [ -n "${copied_files[*]}" ]; then
                # une spécification de mode pour tous les fichiers déployés
                estep "Initialisation du mode à $mode"
                chmod -R "$mode" "${copied_files[@]}" || return 1
            fi
        done
    fi

    if [ -n "$atomic" ]; then
        tmpdest="$actualdest.tmp$$"
        i=0
        while [ -d "$tmpdest" ]; do
            tmpdest="$actualdest.tmp$$-$i"
            i=$(($i + 1))
        done

        estep "Installation du nouveau répertoire"
        mv "$actualdest" "$tmpdest" &&
        mv "$dest" "$actualdest" || return 1

        estep "Suppression de l'ancien répertoire"
        rm -rf "$tmpdest" || return 1
    fi
}

function __uinst2s_root_scripts() {
    # Lancer les scripts de root_scripts avec les arguments de cette fonction
    if is_yes "$copy_files"; then
        if [ -d "$dest" ]; then
            cd "$dest" || return 1
        else
            cd "$destdir" || return 1
        fi
    fi

    for root_script in "${root_scripts[@]}"; do
        etitle "$root_script" \
            "${BASH:-/bin/sh}" "$srcdir/$root_script" "$@"
    done
}

function __uinst2s_python_setup() {
    # Installer le package python
    cd "$srcdir" && python setup.py install
}