# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
##@require base

# system_caps: Déterminer les capacités du syteme sur lequel nous tournons. Ces
# scripts sont prévus pour adapter un système non Linux pour qu'il fonctionne a
# peu près comme Linux. Les fonctions de base qui doivent être adaptées le sont
# en fonction du système sur lequel on tourne. Il suffit d'inclure ce fichier
# pour bénéficier des nouvelles fonctionnalités

# note: sous Linux, ces scripts sont essentiellement un no-op

__SYSTEM_CAPS_COMPUTED__=
if [ -z "$__MAKE_SYSTEM_CAPS__" -a -z "$__FORCE_COMPUTE_SYSTEM_CAPS__" -a -f "@@dest@@/legacy/system_caps" ]; then
    source "@@dest@@/legacy/system_caps"
fi

unset __FORCE_COMPUTE_SYSTEM_CAPS__
if [ -z "$__SYSTEM_CAPS_COMPUTED__" ]; then
    if [ -n "$__MAKE_SYSTEM_CAPS__" ]; then
        >"$__MAKE_SYSTEM_CAPS__"
    fi

    function __define_and_eval__() {
        if [ -n "$__MAKE_SYSTEM_CAPS__" ]; then
            echo "$1" >>"$__MAKE_SYSTEM_CAPS__"
        fi
        eval "$1"
    }

    function __verify_system__() {
        # Vérifier que le système est supporté
        local OENC="$UTF8"
        local system
        for system in Linux SunOS Darwin Cygwin Mingw unsupported; do
            [ "$system" = "$SYSTEM_NAME" ] && break
            [ "$system" = "unsupported" ] && die "$SYSTEM_NAME: Système non supporté"
        done
    }
    __verify_system__; unset -f __verify_system__

    function __get_which_caps__() {
        [ "$SYSTEM_NAME" = "Linux" ] && return
        
        # Obtenir les capacités de which: certaines implémentations affichent "no x
        # in ..." sur stdout au lieu de l'afficher sur stderr
        local OENC="$UTF8"
        local tmpout="$TMPDIR/which_test.$$.out"
        local tmperr="$TMPDIR/which_test.$$.err"
        which __une_appli_qui_existe_pas__ >"$tmpout" 2>"$tmperr"
        if [ -s "$tmpout" ]; then
            __legacy_which__=1 # legacy
        elif [ -s "$tmperr" ]; then
            : # cas normal
        else
            __legacy_which__=2 # MacOSX 10.5
        fi
        /bin/rm -f "$tmpout"
        /bin/rm -f "$tmperr"
        
        if [ "$__legacy_which__" == 1 ]; then
            local grep="${grep:-`which grep 2>&1`}"
            if [ -x "$grep" ]; then
                __define_and_eval__ 'function progexists() { test -n "$1" -a -z "$(which "$1" 2>&1 | '"$grep"' "^no $1")"; }'
            else
                die "grep est requis pour l'implémentation de progexists() sur ce système"
            fi
        elif [ "$__legacy_which__" == 2 ]; then
            __define_and_eval__ 'function progexists() { which "$1" >&/dev/null; }'
        fi
    }
    __get_which_caps__; unset -f __get_which_caps__

    function __set_prog_func__() {
        # Tester la présence d'un programme sur le système en cours, et si
        # le programme doit être accedé avec un chemin absolu, créer une
        # fonction du nom de ce programme qui pourra être utilisée pour
        # appeler ce programme.

        # $1 == nom de la fonction a créer. Il s'agit géneralement du nom du
        # programme, mais ce n'est pas une obligation.

        # $2 == nom à afficher si le programme n'est pas trouvé. Le message
        # affiché sera de la forme "Ce script requière $2....". Si cette
        # valeur est non vide, le programme *doit exister* sinon le script
        # se termine. Si la valeur est "-", alors il s'agit de $1.

        # $3..$n == chemins dans lesquels il faut chercher le programme. Si
        # la valeur est "-", on teste si le programme est disponible dans le
        # path.
        local OENC="$UTF8"
        local prog="$1"; shift
        local req="$1"; shift
        
        if [ -z "$prog" ]; then
            die "USAGE: __set_prog_func__ prog_func req? path [paths...]"
        fi
        
        if [ -n "${!prog}" ]; then
            # Si une variable de ce nom est deja definie, on assume qu'il
            # s'agit du chemin vers le programme
            __define_and_eval__ 'function '"$prog"'() { '"${!prog}"' "$@"; }'
            return 0
        fi
        
        local found=
        while [ -n "$1" ]; do
            local path="$1"; shift
            
            if [ "$path" = "--legacy" ]; then
                # Declarer que les programmes qui suivent sont "legacy"
                __define_and_eval__ 'export __legacy_'"$prog"'__=1'
                continue
            fi
            
            if [ "${path#/}" = "$path" ] && progexists "$path"; then
                # Si on donne un nom de programme, et que ce programme se
                # trouve dans le path, l'adopter avec son chemin absolu
                if [ "$path" != "$prog" ]; then
                    path="$(type -p "$path")"
                    __define_and_eval__ 'function '"$prog"'() { "'"$path"'" "$@"; }'
                fi
                found=1
                break
            fi
            
            if [ "$path" = "-" ] && progexists "$prog"; then
                # pas la peine de creer une fonction, la commande existe
                # deja avec ce nom-la dans le path
                found=1
                break
            fi
            
            if [ -d "$path" ]; then path="$path/$prog"; fi
            if [ -d "$path" ]; then continue; fi
            
            if [ -x "$path" ]; then
                __define_and_eval__ 'function '"$prog"'() { "'"$path"'" "$@"; }'
                found=1
                break
            fi
        done
        
        if [ -z "$found" -a -n "$req" ]; then
            if [ "$req" = "-" ]; then req="$prog"; fi
            die "Ce script requière $req
Si ce programme est installé, faites
    export $prog=/path/to/$prog
et relancez ce script"
        fi

        test -n "$found"
    }
    
    function __get_tools_path_and_caps__() {
        [ "$SYSTEM_NAME" = "Linux" ] && return
        
        # Obtenir l'emplacement d'outils standards, tels que:
        #     cp, awk, grep, diff, tar, ps, date
        local system
        
        # cp
        for system in SunOS Darwin; do
            [ "$SYSTEM_NAME" = "$system" ] && __define_and_eval__ 'export __legacy_cp__=1'
        done
        [ -n "$__legacy_cp__" ] && __define_and_eval__ 'function cp_a() { /bin/cp -pR "$@"; }'
        
        # awk
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ awk "gawk or nawk" \
                gawk \
                /usr/sfw/bin/gawk \
                /opt/sfw/bin/gawk \
                --legacy nawk \
                /usr/bin/nawk
            
        elif [ "$SYSTEM_NAME" = "Darwin" ]; then
            # note: gensub() n'est supporté que sur gnuawk
            # si __legacy_awk__ est défini, on sait que cette fonction n'est pas
            # disponible
            __set_prog_func__ awk "gawk or nawk" \
                gawk \
                /sw/bin/gawk \
                /sw/bin/awk \
                --legacy /usr/bin/awk
        fi
        
        # grep
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ grep - \
                ggrep \
                /usr/sfw/bin/ggrep \
                /opt/sfw/bin/ggrep \
                --legacy /usr/bin/grep
        fi
        if [ -n "$__legacy_grep__" ]; then
            __define_and_eval__ 'function quietgrep() { grep "$@" >&/dev/null; }'
        fi
        
        # diff
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ diff - \
                gdiff \
                /usr/sfw/bin/gdiff \
                /opt/sfw/bin/gdiff \
                --legacy /usr/bin/diff
        fi
        if [ -n "$__legacy_diff__" ]; then
            __define_and_eval__ 'function quietdiff() { diff "$@" >&/dev/null; }'
        fi
        
        # tar
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ tar - \
                gtar \
                /usr/sfw/bin/gtar \
                /opt/sfw/bin/gtar \
                --legacy /usr/bin/tar
            
        elif [ "$SYSTEM_NAME" = "Darwin" ]; then
            __set_prog_func__ tar - \
                gnutar gtar \
                /sw/bin/gtar \
                --legacy /usr/bin/tar
        fi
        
        # ps
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ ps - \
                /usr/ucb/ps \
                --legacy /usr/bin/ps
            __set_prog_func__ legacyps - \
                /usr/bin/ps
            __define_and_eval__ 'function is_running() { test -n "$(legacyps -o pid -p "$1" | grep -v PID)"; }'
        fi
        if [ -n "$__legacy_ps__" ]; then
            __define_and_eval__ 'function ps_all() { ps -ef; }'
        fi
        
        # sleep
        if [ "$SYSTEM_NAME" = "SunOS" -o "$SYSTEM_NAME" = "Darwin" ]; then
            __define_and_eval__ 'function little_sleep() { n=1000; while [ $n -gt 0 ]; do n=$(($n - 1)); done; }'
        fi
        
        # date
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __set_prog_func__ date - \
                gdate \
                /usr/sfw/bin/gdate \
                /opt/sfw/bin/gdate \
                --legacy /usr/bin/date
            
        elif [ "$SYSTEM_NAME" = "Darwin" ]; then
            __set_prog_func__ date - \
                --legacy /bin/date
        fi

        # readlink
        if ! progexists readlink; then
            __define_and_eval__ 'function readlink() { if [ -L "$1" ]; then /bin/ls -ld "$1" | awk '"'"'{print $11'"'"'}; fi; }'
        fi

        # mktemp
        if ! progexists mktemp; then
            __define_and_eval__ 'function mktemp() { make_tempfile "$@"; }'
        fi

        # curl/wget (pour dumpurl)
        if ! progexists curl; then
            if [ "$SYSTEM_NAME" = "SunOS" ]; then
                if __set_prog_func__ curl "" /usr/local/bin/curl; then
                    __define_and_eval__ 'export __curl_EXISTS__=1'
                fi
            fi
        fi
        if ! progexists wget; then
            if [ "$SYSTEM_NAME" = "SunOS" ]; then
                if __set_prog_func__ wget "" /usr/sfw/bin/wget /opt/sfw/bin/wget; then
                    __define_and_eval__ 'export __wget_EXISTS__=1'
                fi
            fi
        fi

        # is_root
        if [ "$SYSTEM_NAME" = "SunOS" ]; then
            __define_and_eval__ 'function is_root() { id | quietgrep "uid=0"; }'
            
        elif [ "$SYSTEM_NAME" = "Cygwin" -o "$SYSTEM_NAME" = "Mingw" ]; then
            # on assume que sur ces systèmes, l'utilisateur est maitre à bord.
            __define_and_eval__ 'function is_root() { true; }'
        fi

        # sedi
        if [ "$SYSTEM_NAME" = "Darwin" ]; then
            __define_and_eval__ 'function __1sedi() {
    local script="$1" input="$2"
    if sed -i.bak "$script" "$input"; then
        /bin/rm -f "$input.bak"
    fi
}
function sedi() {
    local script="$1" input
    shift
    for input in "$@"; do
        __1sedi "$script" "$input"
    done
}'
        elif [ "$SYSTEM_NAME" = "SunOS" ]; then
            __define_and_eval__ 'function __1sedi() {
    local script="$1" input="$2"
    if sed "$script" "$input" >"$input.out.$$"; then
        chmod "$(/bin/ls -l "$input" | awk '\''function oct(mod) {
    value = 0
    if (substr(mod, 1, 1) == "r") value = value + 4
    if (substr(mod, 2, 1) == "w") value = value + 2
    if (substr(mod, 3, 1) == "x") value = value + 1
    return value
}
{
    print oct(substr($1, 2, 3)) oct(substr($1, 5, 3)) oct(substr($1, 8, 3))
}'\'')" "$input.out.$$"
        /bin/mv -f "$input.out.$$" "$input"
    fi
}
function sedi() {
    local script="$1" input
    shift
    for input in "$@"; do
        __1sedi "$script" "$input"
    done
}'
        fi

        # parse_date
        if [ "$SYSTEM_NAME" = "Darwin" ]; then
            function __pd_isleap() {
                # tester si l'année $1 est bissextile
                [ $(($1 % 4)) -eq 0 -a \( $(($1 % 100)) -ne 0 -o $(($1 % 400)) -eq 0 \) ] 
            }
            function __pd_fix_month() {
                # soit $1 le nom d'une variable contenant une année, et $2 le nom d'une
                # variable contenant un mois, normaliser les valeurs de ces variables, en
                # ajustant si nécessaire les valeurs.
                local __pdfm_y="${!1}" __pdfm_m="${!2}"
                let __pdfm_m=$__pdfm_m-1
                while [ $__pdfm_m -gt 11 ]; do
                    let __pdfm_m=$__pdfm_m-12
                    let __pdfm_y=$__pdfm_y+1
                done
                while [ $__pdfm_m -lt 0 ]; do
                    let __pdfm_m=$__pdfm_m+12
                    let __pdfm_y=$__pdfm_y-1
                done
                let __pdfm_m=$__pdfm_m+1
                eval "$1=$__pdfm_y; $2=$__pdfm_m"
            }
            __PD_MONTHDAYS=(0 31 28 31 30 31 30 31 31 30 31 30 31)
            function __pd_monthdays() {
                # calculer le nombre de jours du mois $2 de l'année $1
                local y="$1" m="$2" mds
                __pd_fix_month y m
                mds="${__PD_MONTHDAYS[$m]}"
                [ "$m" -eq 2 ] && __pd_isleap "$y" && let mds=$mds+1
                echo $mds
            }
            function __pd_fix_day() {
                # soit $1 le nom d'une variable contenant une année, $2 le nom d'une
                # variable contenant un mois et $3 le nom d'une variable contenant un jour,
                # normaliser les valeurs de ces variables, en ajustant si nécessaire les
                # valeurs. cette fonction assume que la valeur de $2 est déjà corrigée avec
                # __pd_fix_month
                local __pdfd_y="${!1}" __pdfd_m="${!2}" __pdfd_d="${!3}" __pdfd_mds
                let __pdfd_d=$__pdfd_d-1
                let __pdfd_mds=$(__pd_monthdays $__pdfd_y $__pdfd_m)
                while [ $__pdfd_d -gt $(($__pdfd_mds-1)) ]; do
                    let __pdfd_d=$__pdfd_d-$__pdfd_mds
                    let __pdfd_m=$__pdfd_m+1
                    __pd_fix_month __pdfd_y __pdfd_m
                    let __pdfd_mds=$(__pd_monthdays $__pdfd_y $__pdfd_m)
                done
                while [ $__pdfd_d -lt 0 ]; do
                    let __pdfd_m=$__pdfd_m-1
                    __pd_fix_month __pdfd_y __pdfd_m
                    let __pdfd_d=$__pdfd_d-$(__pd_monthdays $__pdfd_y $__pdfd_m)
                done
                let __pdfd_d=$__pdfd_d+1
                eval "$1=$__pdfd_y; $2=$__pdfd_m; $3=$__pdfd_d"
            }
            function __pd_fix_date() {
                # soit $1 le nom d'une variable contenant une année, $2 le nom d'une
                # variable contenant un mois et $3 le nom d'une variable contenant un jour,
                # normaliser les valeurs de ces variables, en ajustant si nécessaire les
                # valeurs.
                local __pdf_y="${!1}" __pdf_m="${!2}" __pdf_d="${!3}"
            }
            function parse_date() {
                local value="$1" type="${2:-date}"
                local d m y
                # date courante
                eval "$(date +%d/%m/%Y | awk -F/ '{
                    print "d=" $1 "; m=" $2 "; y=" $3
                }')"
                if [ "${value#+}" != "$value" ]; then
                    # ajouter $1 jours
                    d="$(($d+${value#+}))"
                else
                    # parser une nouvelle date, en complétant avec les informations de la
                    # date du jour
                    eval "$(<<<"$value" awk -F/ "BEGIN {
                        dn=$dn; mn=$mn; yn=$yn
                    }"'
                    {
                        d = $1 + 0; if (d < 1) d = dn + 0;
                        m = $2 + 0; if (m < 1) m = mn + 0;
                        if ($3 == "") y = yn + 0;
                        else { y = $3 + 0; if (y < 100) y = y + 2000; }
                        print "d=" d "; m=" m "; y=" y
                    }')"
                fi
                # ensuite corriger les champs si nécessaire
                __pd_fix_month y m
                __pd_fix_day y m d
                # enfin formater la date selon les volontés de l'utilisateur
                case "$type" in
                d|date)
                    awk "BEGIN { d=$d; m=$m; y=$y; "'printf "%02i/%02i/%04i\n", d, m, y }'
                    ;;
                l|ldap)
                    awk "BEGIN { d=$d; m=$m; y=$y; "'printf "%04i%02i%02i000000+0400\n", y, m, d }'
                    ;;
                esac
            }
        fi
    }
    __get_tools_path_and_caps__
    unset -f __set_prog_func__ __get_tools_path_and_caps__

    __define_and_eval__ "__SYSTEM_CAPS_COMPUTED__=1"
    unset -f __define_and_eval__
fi