##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Gestion des informations sur l'hôte local
##@cooked nocomments
##@require base
uprovide sysinfos
urequire base

################################################################################
## Informations sur le système local

## type de système
# attention: dans SYSNAMES et *_SYSDISTS, les noms doivent être ordonnés du
# moins spécifique au plus spécifique
SYSNAMES=(linux linux64 linux32 linuxppc64 linuxppc32 linuxarm macosx)
linux_SYSDISTS=(debianlike debian ubuntu redhatlike rhel fedora centos suse gentoo)
linux32_SYSDISTS=(debianlike debian ubuntu redhatlike rhel fedora centos suse gentoo)
linux64_SYSDISTS=(debianlike debian ubuntu redhatlike rhel fedora centos suse gentoo)
linuxppc32_SYSDISTS=(debianlike debian ubuntu redhatlike fedora)
linuxppc64_SYSDISTS=(debianlike debian ubuntu redhatlike fedora)
linuxarm_SYSDISTS=(debianlike debian ubuntu)
macosx_SYSDISTS=(lion snowleopard leopard tiger panther)
SYSDIST_ALIASES=(
    10.7=lion 10.6=snowleopard 10.5=leopard 10.4=tiger 10.3=panther
)
# attention: dans *_SYSVERS, les noms doivent être ordonnés du plus récent au
# moins récent
debianlike_SYSVERS=()
debian_SYSVERS=(stretch jessie wheezy squeeze lenny etch)
ubuntu_SYSVERS=(oneiric natty maverick lucid karmic jaunty intrepid hardy)
redhatlike_SYSVERS=()
rhel_SYSVERS=(rhel7 rhel6 rhel5 rhel4 redhat7 redhat6 redhat5 redhat4)
fedora_SYSVERS=(fedora14 fedora13 fedora12 fedora11)
centos_SYSVERS=(centos7 centos6 centos5 centos4 redhat7 redhat6 redhat5 redhat4)
suse_SYSVERS=()
gentoo_SYSVERS=()
SYSVER_ALIASES=(
    9=stretch 8=jessie 7=wheezy 6=squeeze 5=lenny 4=etch
    11.10=oneiric 11.04=natty 10.10=maverick 10.04=lucid 9.10=karmic 9.04=jaunty 8.10=intrepid 8.04=hardy
)

function __setup_ALL_SYSvars() {
    local s ds d fs f
    ALL_SYSDISTS=()
    for s in "${SYSNAMES[@]}"; do
        array_copy ds "${s}_SYSDISTS"
        for d in "${ds[@]}"; do
            array_set ALL_SYSDISTS "$d"
        done
    done
    ALL_SYSVERS=()
    for d in "${ALL_SYSDISTS[@]}"; do
        array_copy fs "${d}_SYSVERS"
        for f in "${fs[@]}"; do
            # les valeurs sont poussés vers la fin si elles existent déjà
            array_contains ALL_SYSVERS "$f" && array_del ALL_SYSVERS "$f"
            array_add ALL_SYSVERS "$f"
        done
    done
}
__setup_ALL_SYSvars
unset -f __setup_ALL_SYSvars

function __compute_local_sysinfos_data() {
    SYSINFOS_DATA=(
        "$UNAME_SYSTEM"
        "$UNAME_MACHINE"
        "$([ -f /etc/debian_version ] && cat /etc/debian_version)"
        "$([ -f /etc/gentoo-release ] && cat /etc/gentoo-release)"
        "$([ -f /etc/redhat-release ] && cat /etc/redhat-release)"
        "$([ -f /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/version.plist ] && cat /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/version.plist)"
        "$([ -f /System/Library/Frameworks/CoreServices.framework/Resources/version.plist ] && cat /System/Library/Frameworks/CoreServices.framework/Resources/version.plist)"
    )
}
function __dump_remote_sysinfos_data() {
    "${2:-ssh}" "$1" "\
uname -s
echo .----------------.
uname -m
echo .----------------.
[ -f /etc/debian_version ] && cat /etc/debian_version
echo .----------------.
[ -f /etc/gentoo-release ] && cat /etc/gentoo-release
echo .----------------.
[ -f /etc/redhat-release ] && cat /etc/redhat-release
echo .----------------.
[ -f /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/version.plist ] && cat /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/version.plist
echo .----------------.
[ -f /System/Library/Frameworks/CoreServices.framework/Resources/version.plist ] && cat /System/Library/Frameworks/CoreServices.framework/Resources/version.plist
echo .----------------."
}
function __build_sysinfos_data() {
    awk '
BEGIN {
  data = ""
  have_data = 0
  print "SYSINFOS_DATA=("
}
function read_data() {
  if (have_data) data = data "\n"
  else have_data = 1
  data = data $0
}
function dump_data() {
  gsub(/'\''/, "'\'\\\\\'\''", data)
  print "'\''" data "'\''"
  data = ""
  have_data = 0
}
$0 == ".----------------." { dump_data(); next }
{ read_data() }
END {
  dump_data()
  print ")"
}
'
}
function __compute_sysinfos() {
    # SYSINFOS_DATA doit être initialisé
    local system="${SYSINFOS_DATA[0]}"
    local machine="${SYSINFOS_DATA[1]}"
    local debian_version="${SYSINFOS_DATA[2]}"
    local gentoo_release="${SYSINFOS_DATA[3]}"
    local redhat_release="${SYSINFOS_DATA[4]}"
    local macosx_plist1="${SYSINFOS_DATA[5]}"
    local macosx_plist2="${SYSINFOS_DATA[6]}"

    if [ "$system" == "Linux" ]; then
        case "$machine" in
        x86_64) MYSYSNAME=(linux64 linux); MYBITS=64;;
        i386|i586|i686) MYSYSNAME=(linux32 linux); MYBITS=32;;
        ppc) MYSYSNAME=(linuxppc32 linuxppc linux); MYBITS=32;;
        ppc64) MYSYSNAME=(linuxppc64 linuxppc linux); MYBITS=64;;
        arm*) MYSYSNAME=(linuxarm linux);;
        *) MYSYSNAME=(linux);;
        esac
        if [ -n "$debian_version" ]; then
            case "$debian_version" in
            9*|stretch*) MYSYSDIST=(debian debianlike); MYSYSVER=(stretch);;
            8*|jessie*) MYSYSDIST=(debian debianlike); MYSYSVER=(jessie);;
            7*|wheezy*) MYSYSDIST=(debian debianlike); MYSYSVER=(wheezy);;
            6*|squeeze*) MYSYSDIST=(debian debianlike); MYSYSVER=(squeeze);;
            5*) MYSYSDIST=(debian debianlike); MYSYSVER=(lenny);;
            4*) MYSYSDIST=(debian debianlike); MYSYSVER=(etch);;
            *) MYSYSDIST=(debianlike);;
            esac
        elif [ -n "$gentoo_release" ]; then
            MYSYSDIST=(gentoo)
        elif [ -n "$redhat_release" ]; then
            case "$redhat_release" in
            Fedora*) MYSYSDIST=(fedora redhatlike);;
            Red*Hat*Enterprise*Linux*) MYSYSDIST=(rhel redhatlike);;
            CentOS*) MYSYSDIST=(centos redhatlike);;
            *) MYSYSDIST=(redhatlike);;
            esac
            case "$redhat_release" in
            Fedora*14*) MYSYSVER=(fedora14);;
            Fedora*13*) MYSYSVER=(fedora13);;
            Fedora*12*) MYSYSVER=(fedora12);;
            Fedora*11*) MYSYSVER=(fedora11);;
            Red*Hat*Enterprise*Linux*release\ 7*) MYSYSVER=(rhel7 redhat7);;
            Red*Hat*Enterprise*Linux*release\ 6*) MYSYSVER=(rhel6 redhat6);;
            Red*Hat*Enterprise*Linux*release\ 5*) MYSYSVER=(rhel5 redhat5);;
            Red*Hat*Enterprise*Linux*release\ 4*) MYSYSVER=(rhel4 redhat4);;
            CentOS*release\ 7*) MYSYSVER=(centos7 redhat7);;
            CentOS*release\ 6*) MYSYSVER=(centos6 redhat6);;
            CentOS*release\ 5*) MYSYSVER=(centos5 redhat5);;
            CentOS*release\ 4*) MYSYSVER=(centos4 redhat4);;
            esac
        fi
    elif [ "$system" == "Darwin" ]; then
        function get_macosx_version() {
            local plist
            for plist in "$@"; do
                [ -n "$plist" ] || continue
                echo "$plist" | grep -A 1 CFBundleShortVersionString | grep string | sed 's/.*<string>//g
s/<\/string>.*$//g'
                break
            done
        }
        MYSYSNAME=(macosx darwin)
        case "$(get_macosx_version)" in
        10.7*) MYSYSDIST=(lion);;
        10.6*) MYSYSDIST=(snowleopard);;
        10.5*) MYSYSDIST=(leopard);;
        10.4*) MYSYSDIST=(tiger);;
        10.3*) MYSYSDIST=(panther);;
        esac
    fi
}

function compute_local_sysinfos() {
    local SYSINFOS_DATA
    __compute_local_sysinfos_data
    __compute_sysinfos
    if [ -n "$UTOOLS_CHROOT" ]; then
        # Dans un chroot, il est possible de forcer les valeurs
        [ -n "$UTOOLS_SYSNAME" ] && eval "MYSYSNAME=($UTOOLS_SYSNAME)"
        [ -n "$UTOOLS_BITS" ] && eval "MYBITS=$UTOOLS_BITS"
        [ -n "$UTOOLS_SYSDIST" ] && eval "MYSYSDIST=($UTOOLS_SYSDIST)"
        [ -n "$UTOOLS_SYSVER" ] && eval "MYSYSVER=($UTOOLS_SYSVER)"
    fi
}
function compute_remote_sysinfos() {
    local SYSINFOS_DATA
    eval "$(__dump_remote_sysinfos_data "$@" | __build_sysinfos_data)"
    __compute_sysinfos
}

SYSINFOSLOCALS="\
local -a MYSYSNAME MYSYSDIST MYSYSVER
local MYBITS"
MYSYSNAME=()
MYBITS=
MYSYSDIST=()
MYSYSVER=()
compute_local_sysinfos

function __get_sysdist_alias() {
    if ! array_contains ALL_SYSDISTS "$1"; then
        local nd n d
        for nd in "${SYSDIST_ALIASES[@]}"; do
            splitvar "$nd" n d
            if [ "$n" == "$1" ]; then
                echo "$d"
                return
            fi
        done
    fi
    echo "$1"
}

function __get_sysver_alias() {
    if ! array_contains ALL_SYSVERS "$1"; then
        local nv n v
        for nv in "${SYSVER_ALIASES[@]}"; do
            splitvar "$nv" n v
            if [ "$n" == "$1" ]; then
                echo "$v"
                return
            fi
        done
    fi
    echo "$1"
}

function __fix_sysinfos_upward() {
    local sysname_ sysdists_ sysdist_ sysvers_ sysver_
    if [ -z "${!sysnamevar_}" ]; then
        if [ -z "${!sysdistvar_}" ]; then
            [ -z "${!sysvervar_}" ] && return
            # essayer de déterminer !sysdistvar_ à partir de !sysvervar_
            for sysname_ in "${SYSNAMES[@]}"; do
                array_copy sysdists_ "${sysname_}_SYSDISTS"
                for sysdist_ in "${sysdists_[@]}"; do
                    array_copy sysvers_ "${sysdist_}_SYSVERS"
                    for sysver_ in "${sysvers_[@]}"; do
                        if [ "$sysver_" == "${!sysvervar_}" ]; then
                            set_var "$sysdistvar_" "$sysdist_"
                            set_var "$sysnamevar_" "$sysname_"
                            return
                        fi
                    done
                done
            done
        fi
        [ -z "${!sysdistvar_}" ] && return 0
        # essayer de déterminer !sysnamevar_ à partir de !sysdistvar_
        for sysname_ in "${SYSNAMES[@]}"; do
            array_copy sysdists_ "${sysname_}_SYSDISTS"
            for sysdist_ in "${sysdists_[@]}"; do
                if [ "$sysdist_" == "${!sysdistvar_}" ]; then
                    set_var "$sysnamevar_" "$sysname_"
                    return
                fi
            done
        done
    fi
}
function __fix_sysinfos_downward() {
    local sysname_ sysdist_ sysver_

    # si !sysnamevar_ n'est pas défini, on ne peut rien faire
    [ -z "${!sysnamevar_}" ] && return

    if [ -z "${!sysdistvar_}" ]; then
        # essayer de déterminer !sysdistvar_ à partir de !sysnamevar_
        array_copy sysdists_ "${!sysnamevar_}_SYSDISTS"
        for sysdist_ in "${sysdists_[@]}"; do
            set_var "$sysdistvar_" "$sysdist_"
            break
        done
    fi
    [ -z "${!sysdistvar_}" ] && return

    if [ -z "${!sysvervar_}" ]; then
        # essayer de déterminer !sysvervar_ à partir de !sysdistvar_
        array_copy sysvers_ "${sysdistvar_}_SYSVERS"
        for sysver_ in "${sysvers_[@]}"; do
            set_var "$sysvervar_" "$sysver_"
            break
        done
    fi
    #[ -z "${!sysvervar_}" ] && return
}
function ensure_sysinfos() {
    # Essayer de déterminer les valeurs des variables $1(=SYSNAME), $2(=SYSDIST)
    # et $3(=SYSVER) en fonction des valeurs des autres. Cette fonction est à
    # utiliser quand on récupère cette information de la part de l'utilisateur,
    # et qu'il faut compléter
    local sysnamevar_="${1:-SYSNAME}"
    local sysdistvar_="${2:-SYSDIST}"
    local sysvervar_="${3:-SYSVER}"
    [ -n "${!sysdistvar_}" ] && set_var "$sysdistvar_" "$(__get_sysdist_alias "${!sysdistvar_}")"
    [ -n "${!sysvervar_}" ] && set_var "$sysvervar_" "$(__get_sysver_alias "${!sysvervar_}")"
    __fix_sysinfos_upward
    __fix_sysinfos_downward
}

function get_sysinfos_desc() {
    # Afficher une chaine de la forme SYSNAME/SYSDIST/SYSVER qui décrit le
    # système actuel
    local sysname_="${1:-MYSYSNAME}"; sysname_="${!sysname_}"
    local sysdist_="${2:-MYSYSDIST}"; sysdist_="${!sysdist_}"
    local sysver_="${3:-MYSYSVER}"; sysver_="${!sysver_}"
    echo "$sysname_${sysdist_:+/$sysdist_}${sysver_:+/$sysver_}"
}

function check_sysinfos() {
    # Tester si le système courant ($MYSYSNAME, $MYSYSDIST, $MYSYSVER, $MYBITS)
    # correspond à au moins un des arguments.
    # Il est possible de spécifier des variables différentes pour tester le
    # système courant avec l'option --vars qui doit être spécifiée en premier:
    #     check_sysinfos --vars sysname sysdist sysver bits -d debian
    # Les options -s, -d, -v, -b permettent respectivement de vérifier le
    # système, la distribution, la version et le nombre de bits. Il est possible
    # de spécifier plusieurs tests à effectuer, e.g.:
    #     check_sysinfos -d debian ubuntu -b 64
    # pour tester si l'on est sur une distribution debian ou ubuntu *et* sur un
    # système 64 bits
    # Avec l'option -v, il est possible de suffixer la valeur avec + ou - selon
    # que l'on veut toutes les versions situées après ou avant la version
    # spécifiée. Attention, à cause d'une limitation de l'implémentation, il
    # faut alors impérativement filtrer aussi sur la distribution, e.g:
    #     check_sysinfo -d debian -v lenny+
    # pour tester si on est en lenny ou en squeeze.
    # De même, l'option -d accepte aussi de suffixer la valeur avec + ou -, mais
    # cela n'a actuellement de sens qu'avec les version de MacOS X. Il faut
    # aussi impérativement filtrer sur le système, e.g:
    #     check_sysinfos -s macosx -d 10.5+
    local sysnamevar_ sysdistvar_ sysvervar_ bitsvar_
    if [ "$1" == --vars ]; then
        shift
        if [[ "$1" != -* ]]; then sysnamevar_="${1:-MYSYSNAME}"; shift; fi
        if [[ "$1" != -* ]]; then sysdistvar_="${1:-MYSYSDIST}"; shift; fi
        if [[ "$1" != -* ]]; then sysvervar_="${1:-MYSYSVER}"; shift; fi
        if [[ "$1" != -* ]]; then bitsvar_="${1:-MYBITS}"; shift; fi
    else
        sysnamevar_="MYSYSNAME"
        sysdistvar_="MYSYSDIST"
        sysvervar_="MYSYSVER"
        bitsvar_="MYBITS"
    fi

    local check_=sysname r_=0
    while [ -n "$1" ]; do
        if [[ "$1" == -* ]]; then
            [ "$1" == "-S" ] && { sysnamevar_="$2"; shift 2; continue; }
            [ "$1" == "-D" ] && { sysdistvar_="$2"; shift 2; continue; }
            [ "$1" == "-V" ] && { sysvervar_="$2"; shift 2; continue; }
            [ "$1" == "-B" ] && { bitsvar_="$2"; shift 2; continue; }
            [ "$r_" == "1" ] && break
            [ "$1" == "-s" ] && check_=sysname
            [ "$1" == "-d" ] && check_=sysdist
            [ "$1" == "-v" ] && check_=sysver
            [ "$1" == "-b" ] && check_=bits
            r_=1
            shift
            continue
        fi
        if [ "$check_" == "sysname" ]; then
            if array_contains "$sysnamevar_" "$1"; then
                r_=0
                check_=skip
            fi

        elif [ "$check_" == "sysdist" ]; then
            local mode_=exact value_="$1"
            if [ "${value_%+}" != "$value_" ]; then
                mode_=after
                value_="${value_%+}"
            elif [ "${value_%-}" != "$value_" ]; then
                mode_=before
                value_="${value_%-}"
            fi
            value_="$(__get_sysdist_alias "$value_")"
            if [ "$mode_" == "exact" ]; then
                if array_contains "$sysdistvar_" "$value_"; then
                    r_=0
                    check_=skip
                fi
            elif [ "$mode_" == "after" ]; then
                local sysdist_
                for sysdist_ in "${ALL_SYSDISTS[@]}"; do
                    if array_contains "$sysdistvar_" "$sysdist_"; then
                        r_=0
                        check_=skip
                        [ "$sysdist_" == "$value_" ] && break
                    elif [ "$sysdist_" == "$value_" ]; then
                        break
                    fi
                done
            elif [ "$mode_" == "before" ]; then
                local sysdist_ found_
                for sysdist_ in "${ALL_SYSDISTS[@]}"; do
                    [ "$sysdist_" == "$value_" ] && found_=1
                    if [ -n "$found_" ]; then
                        if array_contains "$sysdistvar_" "$sysdist_"; then
                            r_=0
                            check_=skip
                            break
                        fi
                    fi
                done
            fi

        elif [ "$check_" == "sysver" ]; then
            local mode_=exact value_="$1"
            if [ "${value_%+}" != "$value_" ]; then
                mode_=after
                value_="${value_%+}"
            elif [ "${value_%-}" != "$value_" ]; then
                mode_=before
                value_="${value_%-}"
            fi
            value_="$(__get_sysver_alias "$value_")"
            if [ "$mode_" == "exact" ]; then
                if array_contains "$sysvervar_" "$value_"; then
                    r_=0
                    check_=skip
                fi
            elif [ "$mode_" == "after" ]; then
                local sysver_
                for sysver_ in "${ALL_SYSVERS[@]}"; do
                    if array_contains "$sysvervar_" "$sysver_"; then
                        r_=0
                        check_=skip
                        [ "$sysver_" == "$value_" ] && break
                    elif [ "$sysver_" == "$value_" ]; then
                        break
                    fi
                done
            elif [ "$mode_" == "before" ]; then
                local sysver_ found_
                for sysver_ in "${ALL_SYSVERS[@]}"; do
                    [ "$sysver_" == "$value_" ] && found_=1
                    if [ -n "$found_" ]; then
                        if array_contains "$sysvervar_" "$sysver_"; then
                            r_=0
                            check_=skip
                            break
                        fi
                    fi
                done
            fi

        elif [ "$check_" == "bits" ]; then
            if [ "$1" == "${!bitsvar_}" ]; then
                r_=0
                check_=skip
            fi
        fi
        shift
    done
    return $r_
}