##@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 UINST_LOCAL_PROFILES
    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 destdir_override_userhost destdir_force_remote
    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, exactement comme si la valeur
        destdir avait été spécifiée, i.e destdir=\"/path/to/destdir\"
    -h, --host [user@]host
        Avec la méthode de déploiement uinst:rsync, permettre de spécifier un
        hôte différent. Cette option est ignorée pour les autres méthodes de
        déploiement. Si host vaut localhost, un déploiement local avec ssh est
        effectué. Si host vaut '.', un déploiement local *sans passer par ssh*
        est effectué, comme si seul le chemin avait été spécifié.
        Cette option initialise la valeur destdir_override_userhost
    -S, --ssh ssh
        Avec la méthode de déploiement uinst:rsync, spécifier le programme à
        utiliser pour la connection par ssh. Cette option initialise la valeur
        destdir_ssh
    --force-remote
        Avec la méthode de déploiement uinst:rsync, si un hôte est spécifié dans
        la valeur de destdir, forcer le déploiement distant avec ssh+rsync, même
        si l'hôte et l'utilisateur correspondent aux valeurs courantes. Cette
        option initialise la valeur destdir_force_remote
    -a, --auto
        Si la source n'est pas spécifiée, déterminer le répertoire à déployer
        automatiquement (c'est la valeur par défaut)
    --no-auto
        Ne pas déterminer automatiquement le répertoire à déployer.
    --prefix
        Corriger les chemins srcdir et destdir qui commencent par des préfixes
        valides (c'est la valeur par défaut). 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.
    -l, --local-profiles
        Installer les profils locaux comme tels
    --shared-profiles
        Installer les profils locaux comme des profils partagés. C'est la valeur
        par défaut pour compatibilité.
    -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_"' \
        -h:,-H:,--host: '$__uinst_addvar destdir_override_userhost "$value_"' \
        -S:,--ssh: '$__uinst_addvar destdir_ssh "$value_"' \
        --force-remote '$__uinst_addvar destdir_force_remote 1' \
        -a,--auto UINST_AUTOSRCDIR=1 \
        --no-auto UINST_AUTOSRCDIR= \
        --prefix UINST_AUTOPREFIX=1 \
        --no-prefix UINST_AUTOPREFIX= \
        --include-vcs UINST_COPY_METHOD=cpdir \
        -l,--local-profiles UINST_LOCAL_PROFILES=1 \
        --shared-profiles UINST_LOCAL_PROFILES= \
        -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_LOCAL_PROFILES="${UTOOLS_LOCAL_PROFILES:-auto}"
    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'
        destdir_override_userhost=
        destdir_ssh=
        destdir_force_remote=
        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 destdir_override_userhost destdir_ssh destdir_force_remote 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 -y "Le répertoire '$(ppath "$srcdir")' a été calculé automatiquement. Voulez-vous le déployer?" O || 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_LOCAL_PROFILES" "$UINST_LOCAL_PROFILES" >>"$__uinst_config"
    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"
        local local_profiles="$UINST_LOCAL_PROFILES"
        [ "$local_profiles" == auto ] && local_profiles=
        uenv_install_profiles ${local_profiles:+--local-profiles} "$srcdir" "$profiledir" "$bashrcdir" "$defaultdir"
        eend
    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
    # vérifier la destination
    local __destdir_userhost __destdir_path __destdir_user __destdir_host
    splitfsep2 "$destdir" : __destdir_userhost __destdir_path
    splituserhost "$__destdir_userhost" __destdir_user __destdir_host
    # si un hôte a été spécifié avec --host, utiliser cette valeur
    if [ -n "$destdir_override_userhost" ]; then
        local __override_user __override_host
        splituserhost "$destdir_override_userhost" __override_user __override_host
        [ -n "$__override_user" ] || __override_user="$__destdir_user"
        if [ "$__override_host" == . ]; then
            # forcer déploiement local
            __override_user=
            __override_host=
        fi
        __destdir_user="$__override_user"
        __destdir_host="$__override_host"
    fi
    # vérifier s'il faut faire une copie distante ou locale
    local __destdir_remote=
    if [ -n "$__destdir_host" ]; then
        if [ -n "$destdir_force_remote" ]; then
            __destdir_remote=1
        else
            if [ "$__destdir_host" == localhost ]; then
                :
            elif [[ "$__destdir_host" == *.* ]]; then
                [ "$__destdir_host" == "$MYHOST" ] || __destdir_remote=1
            else
                [ "$__destdir_host" == "$MYHOSTNAME" ] || __destdir_remote=1
            fi
        fi
        if [ -z "$__destdir_remote" -a -n "$__destdir_user" ]; then
            [ "$__destdir_user" == "$USER" ] || __destdir_remote=1
        fi
    fi
    # corriger éventuellement destdir s'il contient un préfixe
    if [ -n "$UINST_AUTOPREFIX" ] && has_prefix "$__destdir_path"; then
        if [ -n "$__destdir_remote" ]; then
            eval "$SYSINFOSLOCALS"
            local APACHE_PREFIXES_CHECK_OR_FIRST=1
            compute_remote_sysinfos "${__destdir_user:+$__destdir_user@}$__destdir_host" "$destdir_ssh"
            recompute_all_prefixes
        fi
        __destdir_path="$(expand_prefix "$__destdir_path")"
    fi
    # valeur finale de destdir
    if [ -n "$__destdir_remote" ]; then
        destdir="${__destdir_user:+$__destdir_user@}$__destdir_host:$__destdir_path"
    else
        destdir="$__destdir_path"
    fi
    # 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
        if [ -n "$__destdir_host" ]; then
            ask_yesno "Synchroniser $srcdesc vers $(ppath "$destdir")?" O || return
        else
            ask_yesno "Synchroniser $srcdesc vers $(ppath "$destdir") sur $MYHOST?" O || return
        fi
    fi

    cd "$srcdir" || return 1
    if [ -n "$__destdir_remote" ]; then
        # Déploiement distant, on laisse rsync gérer
        :
    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
    local UTOOLS_USSH_RSYNC_SUPPORT=1; export UTOOLS_USSH_RSYNC_SUPPORT
    cmd=(rsync ${destdir_ssh:+-e "$destdir_ssh"} "${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 [ -z "$__destdir_remote" ]; 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
}
