##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Gestion de la mise à jour des fichiers de ~/etc/{profile.d,bashrc.d,default}
##@cooked nocomments
##@require base
##@require uenv
uprovide uenv_update
urequire base uenv

function __uenv_move_after() {
    # Dans le fichier $1, déplacer la ligne $2 après la ligne commençant par
    # l'expression régulière '^$3:'
    # Si la ligne $2 n'existe pas, ne rien faire
    # Si la ligne $3 n'existe pas ou vaut '*', mettre la ligne $2 en dernier.
    # Si $4 est spécifié, l'utiliser comme fichier temporaire.
    local tmpfile="$4"
    [ -n "$tmpfile" ] || ac_set_tmpfile tmpfile

    # XXX Il faudrait quoter $3 pour éviter les caractères spéciaux de grep!
    if quietgrep -F "$2" "$1"; then
        local re_after="$3"
        [ "$re_after" != "*" ] && re_after="^$re_after:"
        if [ "$re_after" != "*" ] && quietgrep "$re_after" "$1"; then
            awkrun line="$2" re_after="$re_after" '
BEGIN {
    found_line = 0
    found_after = 0
}
! found_line && $0 == line {
    found_line = 1
    if (! found_after) {
        next
    }
}
! found_after && $0 ~ re_after {
    found_after = 1
    if (found_line) {
        # si nous avons trouvé la ligne avant re_after, la mettre juste après
        # sinon, pas la peine de faire de modification
        print
        print line
        next
    }
}
{ print }
END {
    if (! found_after && re_after == "*") {
        if (found_line) {
            print line
        }
    }
}' <"$1" >"$tmpfile" &&
            cat "$tmpfile" >"$1"
        else
            grep -vF "$2" "$1" >"$tmpfile"
            echo "$2" >>"$tmpfile"
            cat "$tmpfile" >"$1"
        fi
    fi
}

function __uenv_move_before() {
    # Dans le fichier $1, déplacer la ligne $2 avant la ligne commençant par
    # l'expression régulière '^$3:'
    # Si la ligne $2 n'existe pas, ne rien faire
    # Si la ligne $3 n'existe pas ou vaut '*', mettre la ligne $2 en premier.
    # Si $4 est spécifié, l'utiliser comme fichier temporaire.
    local tmpfile="$4"
    [ -n "$tmpfile" ] || ac_set_tmpfile tmpfile

    # XXX Il faudrait quoter $3 pour éviter les caractères spéciaux de grep!
    if quietgrep -F "$2" "$1"; then
        local re_before="$3"
        [ "$re_before" != "*" ] && re_before="^$re_before:"
        if [ "$re_before" != "*" ] && quietgrep "$re_before" "$1"; then
            awkrun line="$2" re_before="$re_before" '
BEGIN {
    found_line = 0
    found_before = 0
}
! found_line && $0 == line {
    found_line = 1
    if (found_before) {
        next
    }
}
! found_before && ($0 ~ re_before || re_before == "*") {
    found_before = 1
    if (! found_line) {
        print line
        print
        next
    }
}
{ print }
' <"$1" >"$tmpfile" &&
            cat "$tmpfile" >"$1"
        else
            echo "$2" >"$tmpfile"
            grep -vF "$2" "$1" >>"$tmpfile"
            cat "$tmpfile" >"$1"
        fi
    fi
}

function uenv_update_dir() {
    # Mettre à jour l'ordre de chargement pour le répertoire $1 qui contient des
    # fichiers de profil pour le shell. L'ordre dans lequel le fichiers de
    # profil doivent être chargé est écrit dans le fichier $1/.source_in_order
    # Si $2 est spécifié, il s'agit d'un fichier temporaire utilisé pour les
    # calculs de l'ordre des chargements.
    # $3(=$1) est le répertoire de destination. Si $1 est un répertoire de
    # préparation temporaire, on peut spécifier grâce à $3 quel est le
    # répertoire final après préparation.
    # S'ils sont spécifiés, les arguments $4..* sont des répertoires contenant
    # des fichiers de profils supplémentaires qu'il faut considérer aussi. Dans
    # ce cas, $3 est ignoré.
    local dir="$(abspath "$1")"; shift
    local tmpfile="$1"; shift
    local destdir="${1:-$dir}"; shift
    local sio="$dir/.source_in_order"
    local before after file filename
    local -a files

    [ -n "$tmpfile" ] || ac_set_tmpfile tmpfile
    [ $# -gt 0 ] && destdir=
    set -- "$dir" "$@"

    local __uname_system="$UNAME_SYSTEM"
    local __myhostname="$MYHOSTNAME"
    >"$sio"
    for dir in "$@"; do
        [ -n "$dir" ] || continue
        for file in "$dir/"*; do
            [ -f "$file" ] || continue
            files=("${files[@]}" "$file")
            filename="$(basename -- "$file")"
            __uenv_check_file "$dir" "$filename" && echo "$filename:$file" >>"$sio"
        done
    done

    # traiter d'abord les fichiers qui ont '@before *' ou '@after *'
    local -a nfiles
    for file in "${files[@]}"; do
        filename="$(basename -- "$file")"

        before="$(<"$file" awk '$0 ~ /^##@before / { print $2; exit 0 }')"
        if [ "$before" == "*" ]; then
            __uenv_move_before "$sio" "$filename:$file" "$before" "$tmpfile"
        else
            array_set nfiles "$file"
        fi

        after="$(<"$file" awk '$0 ~ /^##@after / { print $2; exit 0 }')"
        if [ "$after" == "*" ]; then
            __uenv_move_after "$sio" "$filename:$file" "$after" "$tmpfile"
        else
            array_set nfiles "$file"
        fi
    done
    # Ensuite traiter les autres fichiers.
    # L'opération est faite en deux temps parce que si A est avant B, et que B
    # est avant *, si A est traité avant B, A risque de se trouver après B si B
    # est déplacé au début de la liste.
    for file in "${nfiles[@]}"; do
        filename="$(basename -- "$file")"

        before="$(<"$file" awk '$0 ~ /^##@before / { print $2; exit 0 }')"
        [ -n "$before" -a "$before" != "*" ] && __uenv_move_before "$sio" "$filename:$file" "$before" "$tmpfile"

        after="$(<"$file" awk '$0 ~ /^##@after / { print $2; exit 0 }')"
        [ -n "$after" -a "$after" != "*" ] && __uenv_move_after "$sio" "$filename:$file" "$after" "$tmpfile"
    done

    awkrun destdir="$destdir" '{
  pos = match($0, /:/)
  if (pos != 0) {
    filename = substr($0, 1, pos - 1)
    file = substr($0, pos + 1)
    if (destdir != "") {
      print "source " destdir "/" filename
    } else {
      print "source " file
    }
  } else {
    print "source " destdir "/" $0
  }
}' <"$sio" >"$tmpfile" &&
    cat "$tmpfile" >"$sio"
}

function uenv_set_destdirs() {
    if [ "$1" == --local-profiles ]; then
        local hostname="${HOSTNAME%%.*}"
        profiledestdir="$HOME/etc/profile.${hostname}.d"
        bashrcdestdir="$HOME/etc/bashrc.${hostname}.d"
        defaultdestdir="$HOME/etc/default.${hostname}"
        shared_profiledestdir="$HOME/etc/profile.d"
        shared_bashrcdestdir="$HOME/etc/bashrc.d"
        shared_defaultdestdir="$HOME/etc/default"
    else
        profiledestdir="$HOME/etc/profile.d"
        bashrcdestdir="$HOME/etc/bashrc.d"
        defaultdestdir="$HOME/etc/default"
        shared_profiledestdir=
        shared_bashrcdestdir=
        shared_defaultdestdir=
    fi
}

function uenv_sourced_in() {
    # vérifier que l'un des fichiers $2..$* est sourcé dans $1
    local src="$1"; shift
    local file
    for file in "$@"; do
        grep -q "\\.[ \\t]*$file\$" "$src" && return 0
        grep -q "source[ \\t]*$file\$" "$src" && return 0
    done
    return 1
}

function uenv_configure_profiles() {
    local destdir="$1"
    local LEGACY_PROFILE="$HOME/.profile"
    local PROFILE="$HOME/.bash_profile"
    local BASHRC="$HOME/.bashrc"

    if [ ! -f "$PROFILE" ]; then
        echo "# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi" >"$PROFILE"
    fi
    if [ ! -f "$BASHRC" ]; then
        touch "$BASHRC"
    fi

    if [ -f "$LEGACY_PROFILE" ]; then
        if ! uenv_sourced_in "$PROFILE" "~/.profile"; then
            ewarn "\
~/.profile existe, il contient peut-être des instructions utiles. Si c'est le
cas, il faut décommenter la ligne dans ~/.bash_profile qui source ~/.profile"
            estep "Configuration de l'inclusion de ~/.profile dans $PROFILE"
            echo >>"$PROFILE" "
# decommenter si ~/.profile contient des commandes utiles
#[ -f ~/.profile ] && source ~/.profile"
        fi
    fi

    if ! uenv_sourced_in "$PROFILE" "$destdir/profile"; then
        estep "Configuration de l'inclusion de $destdir/profile"
        echo >>"$PROFILE" "
# source $destdir/profile.d scripts
[ -f \"$destdir/profile\" ] && source $destdir/profile"
    fi

    if ! uenv_sourced_in "$BASHRC" "$destdir/bashrc"; then
        estep "Configuration de l'inclusion de $destdir/bashrc"
        echo >>"$BASHRC" "
# source $destdir/bashrc.d scripts
[ -f \"$destdir/bashrc\" ] && source $destdir/bashrc"
    fi
}

function uenv_install_profiles() {
    local local_profiles
    if [ "$1" == --local-profiles ]; then
        local_profiles=1
        shift
    fi
    local projdir="$1" profiledir="$2" bashrcdir="$3" defaultdir="$4"
    local tmpfile shared
    local prefix envsrcdir envdestdir shadestdir envfiles envfile envsrc envdest shadest
    local shared_profiledestdir shared_bashrcdestdir shared_defaultdestdir
    local profiledestdir bashrcdestdir defaultdestdir
    uenv_set_destdirs ${local_profiles:+--local-profiles}

    ac_set_tmpfile tmpfile
    for prefix in profile bashrc default; do
        envsrcdir="${prefix}dir"; envsrcdir="$projdir/${!envsrcdir}"
        envdestdir="${prefix}destdir"; envdestdir="${!envdestdir}"
        shadestdir="shared_${prefix}destdir"; shadestdir="${!shadestdir}"
        [ -n "$envsrcdir" -a -d "$envsrcdir" ] || continue
        mkdir -p "$envdestdir"
        [ -n "$shadestdir" ] && mkdir -p "$shadestdir"

        array_from_lines envfiles "$(list_files "$envsrcdir")"
        for envfile in "${envfiles[@]}"; do
            envsrc="$envsrcdir/$envfile"
            if [ "${envfile%.shared}" != "$envfile" ]; then
                envfile="${envfile%.shared}"
                shared=1
            else
                shared=
            fi

            if [ -n "$shadestdir" ]; then
                if [ -n "$shared" ]; then
                    # si fichier partagé, toujours le copier dans le répertoire
                    # des fichiers partagés
                    envdest="$shadestdir/$envfile"
                else
                    envdest="$envdestdir/$envfile"
                    shadest="$shadestdir/$envfile"
                    if [ ! -f "$envdest" -a -f "$shadest" ]; then
                        # Si déjà installé en partagé, continuer à mettre à jour ce
                        # fichier-là
                        envdest="$shadest"
                    fi
                fi
            else
                envdest="$envdestdir/$envfile"
            fi

            local actual_envdestdir="$(dirname -- "$envdest")"
            if [ ! -f "$envdest" ]; then
                estep "Installation du profil par défaut $envfile dans $(ppath "$actual_envdestdir")"
                /bin/cp -f "$envsrc" "$envdest"
            elif [ "$prefix" == "default" ]; then
                # ne jamais mettre à jour dans default
                if testdiff "$envdest" "$envsrc"; then
                    estepi "Le profil $(ppath "$envdest") a été conservé"
                fi
            elif endswith "$envfile" ".userconf"; then
                # ne jamais mettre à jour les fichiers .userconf
                if testdiff "$envdest" "$envsrc"; then
                    estepi "Le profil $(ppath "$envdest") a été conservé"
                fi
            elif testdiff "$envdest" "$envsrc"; then
                estep "Le profil par défaut $envfile dans $(ppath "$actual_envdestdir") a été modifié"
                if show_info; then
                    eecho "Voici les différences détectées:"
                    eecho ""
                    diff -u "$envdest" "$envsrc" 1>&2
                    eecho ""
                fi

                if ask_yesno "Faut-il remplacer $(ppath "$envdest") par le profil par défaut?" O; then
                    /bin/cp -f "$envsrc" "$envdest"
                fi
            fi
        done

        if [ "$prefix" != "default" ]; then
            estep "Mise à jour de l'ordre de lecture de $(ppath "$envdestdir")"
            uenv_update_dir "$envdestdir" "$tmpfile" "" "$shadestdir"
        fi
    done
    ac_clean "$tmpfile"
}
