4163 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			4163 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | 
						||
## Fonctions de base
 | 
						||
##@cooked nocomments
 | 
						||
##@*inc[base.ulib
 | 
						||
## Support des fonctions uprovided(), uprovide() et urequire() de ulib dans le
 | 
						||
## cas où cette librairie n'est pas chargée
 | 
						||
if [ -z "$ULIBDIR" -o "$ULIBDIR" != "$ULIBINIT" ]; then
 | 
						||
    ULIBPROVIDED=()
 | 
						||
    function uprovided() {
 | 
						||
        local module
 | 
						||
        for module in "${ULIBPROVIDED[@]}"; do
 | 
						||
            [ "$module" == "$1" ] && return 0
 | 
						||
        done
 | 
						||
        return 1
 | 
						||
    }
 | 
						||
    function uprovide() {
 | 
						||
        uprovided "$1" && return 1
 | 
						||
        ULIBPROVIDED=("${ULIBPROVIDED[@]}" "$1")
 | 
						||
    }
 | 
						||
    function urequire() {
 | 
						||
        local module r=0
 | 
						||
        for module in "$@"; do
 | 
						||
            uprovided "$module" && continue
 | 
						||
            echo "error: $module: this module is required but cannot be automatically loaded" 1>&2
 | 
						||
            r=1
 | 
						||
        done
 | 
						||
        return $r
 | 
						||
    }
 | 
						||
    uprovide base.ulib
 | 
						||
fi
 | 
						||
##@*inc]base.ulib
 | 
						||
##@include base.init
 | 
						||
##@include base.core
 | 
						||
##@include base.string
 | 
						||
##@include base.num
 | 
						||
##@include base.bool
 | 
						||
##@include base.array
 | 
						||
##@include base.quote
 | 
						||
##@include base.split
 | 
						||
##@include base.args
 | 
						||
##@include base.tools
 | 
						||
##@include base.compat
 | 
						||
##@include base.deprecated
 | 
						||
uprovide base
 | 
						||
urequire base.init base.core base.string base.num base.bool base.array base.quote base.split base.args base.tools base.compat
 | 
						||
urequire base.deprecated
 | 
						||
 | 
						||
## variables tableaux
 | 
						||
function array_count() {
 | 
						||
# retourner le nombre d'éléments du tableau $1
 | 
						||
    eval "echo \${#$1[*]}"
 | 
						||
}
 | 
						||
function array_isempty() {
 | 
						||
# tester si le tableau $1 est vide
 | 
						||
    [ $(array_count "$1") -eq 0 ]
 | 
						||
}
 | 
						||
function array_new() {
 | 
						||
# créer un tableau vide dont le nom est $1
 | 
						||
    eval "$1=()"
 | 
						||
}
 | 
						||
function array_add() {
 | 
						||
# ajouter les valeurs $2..@ au tableau dont le nom est $1
 | 
						||
    local __aa_a="$1"; shift
 | 
						||
    eval "$__aa_a=(\"\${$__aa_a[@]}\" \"\$@\")"
 | 
						||
    #eval "$1=(\"\${$1[@]}\" $(qval "$2"))"
 | 
						||
}
 | 
						||
function array_ins() {
 | 
						||
# insérer les valeurs $2..@ au début du tableau dont le nom est $1
 | 
						||
    local __aa_a="$1"; shift
 | 
						||
    eval "$__aa_a=(\"\$@\" \"\${$__aa_a[@]}\")"
 | 
						||
    #eval "$1=($(qval "$2") \"\${$1[@]}\")"
 | 
						||
}
 | 
						||
function array_del() {
 | 
						||
# supprimer *les* valeurs $2 du tableau dont le nom est $1
 | 
						||
    local __ad_v
 | 
						||
    local -a __ad_vs
 | 
						||
    eval 'for __ad_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$__ad_v" != '"$(qval "$2")"' ]; then
 | 
						||
    array_add __ad_vs "$__ad_v"
 | 
						||
  fi
 | 
						||
done'
 | 
						||
    array_copy "$1" __ad_vs
 | 
						||
}
 | 
						||
function array_addu() {
 | 
						||
# ajouter la valeur $2 au tableau dont le nom est $1, si la valeur n'y est pas
 | 
						||
# déjà. Retourner vrai si la valeur a été ajoutée
 | 
						||
    local __as_v
 | 
						||
    eval 'for __as_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$__as_v" == '"$(qval "$2")"' ]; then
 | 
						||
    return 1
 | 
						||
  fi
 | 
						||
done'
 | 
						||
    array_add "$1" "$2"
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function array_set() {
 | 
						||
    array_addu "$@"
 | 
						||
}
 | 
						||
function array_insu() {
 | 
						||
# insérer la valeur $2 au début du tableau tableau dont le nom est $1, si la
 | 
						||
# valeur n'y est pas déjà. Retourner vrai si la valeur a été ajoutée.
 | 
						||
    local __as_v
 | 
						||
    eval 'for __as_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$__as_v" == '"$(qval "$2")"' ]; then
 | 
						||
    return 1
 | 
						||
  fi
 | 
						||
done'
 | 
						||
    array_ins "$1" "$2"
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function array_fillrange() {
 | 
						||
# Initialiser le tableau $1 avec les nombres de $2(=1) à $3(=10) avec un step de $4(=1)
 | 
						||
    local -a __af_vs
 | 
						||
    local __af_i="${2:-1}" __af_to="${3:-10}" __af_step="${4:-1}"
 | 
						||
    while [ "$__af_i" -le "$__af_to" ]; do
 | 
						||
        __af_vs=("${__af_vs[@]}" "$__af_i")
 | 
						||
        __af_i=$(($__af_i + $__af_step))
 | 
						||
    done
 | 
						||
    array_copy "$1" __af_vs
 | 
						||
}
 | 
						||
function array_eq() {
 | 
						||
# tester l'égalité des tableaux $1 et $2
 | 
						||
    local -a __ae_a1 __ae_a2
 | 
						||
    array_copy __ae_a1 "$1"
 | 
						||
    array_copy __ae_a2 "$2"
 | 
						||
    [ ${#__ae_a1[*]} -eq ${#__ae_a2[*]} ] || return 1
 | 
						||
    local __ae_v __ae_i=0
 | 
						||
    for __ae_v in "${__ae_a1[@]}"; do
 | 
						||
        [ "$__ae_v" == "${__ae_a2[$__ae_i]}" ] || return 1
 | 
						||
        __ae_i=$(($__ae_i + 1))
 | 
						||
    done
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function array_contains() {
 | 
						||
# tester si le tableau dont le nom est $1 contient la valeur $2
 | 
						||
    local __ac_v
 | 
						||
    eval 'for __ac_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$__ac_v" == '"$(qvalm "$2")"' ]; then
 | 
						||
    return 0
 | 
						||
  fi
 | 
						||
done'
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function array_icontains() {
 | 
						||
# tester si le tableau dont le nom est $1 contient la valeur $2, sans tenir
 | 
						||
# compte de la casse
 | 
						||
    local __ac_v
 | 
						||
    eval 'for __ac_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$(strlower "$__ac_v")" == '"$(strlower "$(qval "$2")")"' ]; then
 | 
						||
    return 0
 | 
						||
  fi
 | 
						||
done'
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function array_find() {
 | 
						||
# si le tableau $1 contient la valeur $2, retourner l'index de la valeur. Si le
 | 
						||
# tableau $3 est spécifié, retourner la valeur à l'index dans ce tableau
 | 
						||
    local __af_i __af_v
 | 
						||
    __af_i=0
 | 
						||
    eval 'for __af_v in "${'"$1"'[@]}"; do
 | 
						||
  if [ "$__af_v" == '"$(qval "$2")"' ]; then
 | 
						||
    if [ -n "$3" ]; then
 | 
						||
      echo "${'"$3"'[$__af_i]}"
 | 
						||
    else
 | 
						||
      echo "$__af_i"
 | 
						||
    fi
 | 
						||
    return 0
 | 
						||
  fi
 | 
						||
  __af_i=$(($__af_i + 1))
 | 
						||
done'
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function array_reverse() {
 | 
						||
# Inverser l'ordre des élément du tableau $1
 | 
						||
    local -a __ar_vs
 | 
						||
    local __ar_v
 | 
						||
    array_copy __ar_vs "$1"
 | 
						||
    array_new "$1"
 | 
						||
    for __ar_v in "${__ar_vs[@]}"; do
 | 
						||
        array_ins "$1" "$__ar_v"
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function array_replace() {
 | 
						||
# dans le tableau $1, remplacer toutes les occurences de $2 par $3..*
 | 
						||
    local __ar_sn="$1"; shift
 | 
						||
    local __ar_f="$1"; shift
 | 
						||
    local -a __ar_s __ar_d
 | 
						||
    local __ar_v
 | 
						||
    array_copy __ar_s "$__ar_sn"
 | 
						||
    for __ar_v in "${__ar_s[@]}"; do
 | 
						||
        if [ "$__ar_v" == "$__ar_f" ]; then
 | 
						||
            __ar_d=("${__ar_d[@]}" "$@")
 | 
						||
        else
 | 
						||
            __ar_d=("${__ar_d[@]}" "$__ar_v")
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    array_copy "$__ar_sn" __ar_d
 | 
						||
}
 | 
						||
function array_each() {
 | 
						||
# Pour chacune des valeurs 'v' du tableau $1, appeler la fonction $2 avec les
 | 
						||
# arguments '$v $3..$n'
 | 
						||
    local __ae_an="$1"; shift
 | 
						||
    local __ae_f="$1"; shift
 | 
						||
    local -a __ae_a
 | 
						||
    local __ae_v
 | 
						||
    array_copy __ae_a "$__ae_an"
 | 
						||
    for __ae_v in "${__ae_a[@]}"; do
 | 
						||
        "$__ae_f" "$__ae_v" "$@"
 | 
						||
    done
 | 
						||
}
 | 
						||
function array_map() {
 | 
						||
# Pour chacune des valeurs 'v' du tableau $1, appeler la fonction $2 avec les
 | 
						||
# arguments '$v $3..$n', et remplacer la valeur par le résultat de la fonction
 | 
						||
    local __am_an="$1"; shift
 | 
						||
    local __am_f="$1"; shift
 | 
						||
    local -a __am_a __am_vs
 | 
						||
    local __am_v
 | 
						||
    array_copy __am_a "$__am_an"
 | 
						||
    for __am_v in "${__am_a[@]}"; do
 | 
						||
        __am_vs=("${__am_vs[@]}" "$("$__am_f" "$__am_v" "$@")")
 | 
						||
    done
 | 
						||
    array_copy "$__am_an" __am_vs
 | 
						||
}
 | 
						||
function first_value() {
 | 
						||
# retourner la première valeur du tableau $1
 | 
						||
    eval "recho \"\${$1[@]:0:1}\""
 | 
						||
}
 | 
						||
function last_value() {
 | 
						||
# retourner la dernière valeur du tableau $1
 | 
						||
    eval "recho \"\${$1[@]:\$((-1)):1}\""
 | 
						||
}
 | 
						||
function array_copy() {
 | 
						||
# copier le contenu du tableau $2 dans le tableau $1
 | 
						||
    eval "$1=(\"\${$2[@]}\")"
 | 
						||
}
 | 
						||
function array_copy_firsts() {
 | 
						||
# copier tous les valeurs du tableau $2(=$1) dans le tableau $1, excepté la dernière
 | 
						||
    eval "$1=(\"\${${2:-$1}[@]:0:\$((\${#${2:-$1}[@]}-1))}\")"
 | 
						||
}
 | 
						||
function array_del_last() {
 | 
						||
    array_copy_firsts "$1"
 | 
						||
}
 | 
						||
function array_copy_lasts() {
 | 
						||
# copier tous les valeurs du tableau $2(=$1) dans le tableau $1, excepté la première
 | 
						||
    eval "$1=(\"\${${2:-$1}[@]:1}\")"
 | 
						||
}
 | 
						||
function array_del_first() {
 | 
						||
    array_copy_lasts "$1"
 | 
						||
}
 | 
						||
function array_extend() {
 | 
						||
# ajouter le contenu du tableau $2 au tableau $1
 | 
						||
    eval "$1=(\"\${$1[@]}\" \"\${$2[@]}\")"
 | 
						||
}
 | 
						||
function array_extendu() {
 | 
						||
# ajouter chacune des valeurs du tableau $2 au tableau $1, si ces valeurs n'y
 | 
						||
# sont pas déjà. Retourner vrai si au moins une valeur a été ajoutée
 | 
						||
    local __ae_v __ae_s=1
 | 
						||
    eval 'for __ae_v in "${'"$2"'[@]}"; do
 | 
						||
  array_addu "$1" "$__ae_v" && __ae_s=0
 | 
						||
done'
 | 
						||
    return "$__ae_s"
 | 
						||
}
 | 
						||
function array_extend_firsts() {
 | 
						||
# ajouter toutes les valeurs du tableau $2 dans le tableau $1, excepté la dernière
 | 
						||
    eval "$1=(\"\${$1[@]}\" \"\${$2[@]:0:\$((\${#$2[@]}-1))}\")"
 | 
						||
}
 | 
						||
function array_extend_lasts() {
 | 
						||
# ajouter toutes les valeurs du tableau $2 dans le tableau $1, excepté la première
 | 
						||
    eval "$1=(\"\${$1[@]}\" \"\${$2[@]:1}\")"
 | 
						||
}
 | 
						||
function array_xsplit() {
 | 
						||
# créer le tableau $1 avec chaque élément de $2 (un ensemble d'éléments séparés
 | 
						||
# par $3, qui vaut ':' par défaut).
 | 
						||
    eval "$1=($(recho_ "$2" | awkrun RS="${3:-:}" '
 | 
						||
{
 | 
						||
    gsub(/'\''/, "'\'\\\\\'\''")
 | 
						||
    print "'\''" $0 "'\''"
 | 
						||
}'))" #"
 | 
						||
}
 | 
						||
function array_split() {
 | 
						||
# créer le tableau $1 avec chaque élément de $2 (un ensemble d'éléments séparés
 | 
						||
# par $3, qui vaut ':' par défaut). Les éléments vides sont ignorés. par exemple
 | 
						||
# "a::b" est équivalent à "a:b"
 | 
						||
    eval "$1=($(recho_ "$2" | awkrun RS="${3:-:}" '
 | 
						||
/^$/ { next }
 | 
						||
{
 | 
						||
    gsub(/'\''/, "'\'\\\\\'\''")
 | 
						||
    print "'\''" $0 "'\''"
 | 
						||
}'))" #"
 | 
						||
}
 | 
						||
function array_from_path() {
 | 
						||
    array_split "$1" "$2" ":"
 | 
						||
}
 | 
						||
function array_from_xlines() {
 | 
						||
# créer le tableau $1 avec chaque ligne de $2.
 | 
						||
    eval "$1=($(recho_ "$2" | _nl2lf | awk '
 | 
						||
{
 | 
						||
    gsub(/'\''/, "'\'\\\\\'\''")
 | 
						||
    print "'\''" $0 "'\''"
 | 
						||
}'))" #"
 | 
						||
}
 | 
						||
function array_from_lines() {
 | 
						||
# créer le tableau $1 avec chaque ligne de $2. Les lignes vides sont ignorés.
 | 
						||
    eval "$1=($(recho_ "$2" | _nl2lf | awk '
 | 
						||
/^$/ { next }
 | 
						||
{
 | 
						||
    gsub(/'\''/, "'\'\\\\\'\''")
 | 
						||
    print "'\''" $0 "'\''"
 | 
						||
}'))" #"
 | 
						||
}
 | 
						||
function array_join() {
 | 
						||
# afficher le contenu du tableau dont le nom est $1 sous forme d'une liste de
 | 
						||
# valeurs séparées par $2 (par défaut, une virgule)
 | 
						||
# Si $1=="@", alors les éléments du tableaux sont les arguments de la fonction à
 | 
						||
# partir de $3
 | 
						||
# Si $1!="@" et que le tableau est vide, afficher $3
 | 
						||
# Si $1!="@", $4 et $5 sont des préfixes et suffixes à rajouter à chaque élément
 | 
						||
    local __aj_an __aj_l __aj_j __aj_s="${2:-,}" __aj_pf __aj_sf
 | 
						||
    if [ "$1" == "@" ]; then
 | 
						||
        __aj_an="\$@"
 | 
						||
        shift; shift
 | 
						||
    else
 | 
						||
        __aj_an="\${$1[@]}"
 | 
						||
        __aj_pf="$4"
 | 
						||
        __aj_sf="$5"
 | 
						||
    fi
 | 
						||
    eval 'for __aj_l in "'"$__aj_an"'"; do
 | 
						||
  __aj_j="${__aj_j:+$__aj_j'"$__aj_s"'}$__aj_pf$__aj_l$__aj_sf"
 | 
						||
done'
 | 
						||
    if [ -n "$__aj_j" ]; then
 | 
						||
        recho "$__aj_j"
 | 
						||
    elif [ "$__aj_an" != "\$@" -a -n "$3" ]; then
 | 
						||
        recho "$3"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function array_mapjoin() {
 | 
						||
# map le tableau $1 avec la fonction $2, puis afficher le résultat en séparant
 | 
						||
# chaque élément par $3. Les arguments et la sémantique sont les mêmes que pour
 | 
						||
# array_join en tenant compte de l'argument supplémentaire $2 qui est la
 | 
						||
# fonction pour array_map (les autres arguments sont décalés en conséquence)
 | 
						||
    local __amj_src="$1" __amj_func="$2" __amj_sep="$3"
 | 
						||
    shift; shift; shift
 | 
						||
    if [ "$__amj_src" == "@" ]; then
 | 
						||
        local -a __amj_tmpsrc
 | 
						||
        __amj_tmpsrc=("$@")
 | 
						||
        __amj_src=__amj_tmpsrc
 | 
						||
        set --
 | 
						||
    fi
 | 
						||
    local -a __amj_tmp
 | 
						||
    array_copy __amj_tmp "$__amj_src"
 | 
						||
    array_map __amj_tmp "$__amj_func"
 | 
						||
    array_join __amj_tmp "$__amj_sep" "$@"
 | 
						||
}
 | 
						||
function array_to_lines() {
 | 
						||
# afficher le tableau dont le nom est $1 sous forme de lignes
 | 
						||
    array_join "$1" "
 | 
						||
" "$2" "$3" "$4"
 | 
						||
}
 | 
						||
function array_to_path() {
 | 
						||
# afficher le tableau dont le nom est $1 sous forme d'une liste de chemins
 | 
						||
# séparés par ':')
 | 
						||
    array_join "$1" ":" "$2" "$3" "$4"
 | 
						||
}
 | 
						||
function array_fix_paths() {
 | 
						||
# Corriger les valeurs du tableau $1. Les valeurs contenant le séparateur
 | 
						||
# $2(=':') sont séparées en plusieurs valeurs. Par exemple avec le tableau
 | 
						||
# input=(a b:c), le résultat est input=(a b c)
 | 
						||
    local __afp_an="$1" __afp_s="${2:-:}"
 | 
						||
    local -a __afp_vs
 | 
						||
    local __afp_v
 | 
						||
    array_copy __afp_vs "$__afp_an"
 | 
						||
    array_new "$__afp_an"
 | 
						||
    for __afp_v in "${__afp_vs[@]}"; do
 | 
						||
        array_split __afp_v "$__afp_v" "$__afp_s"
 | 
						||
        array_extend "$__afp_an" __afp_v
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## dates
 | 
						||
 | 
						||
function get_date_rfc822() {
 | 
						||
    LC_TIME=C date +"%a, %d %b %Y %H:%M:%S %Z"
 | 
						||
}
 | 
						||
function get_date_fr() {
 | 
						||
    LC_TIME=C date +"%d/%m/%Y"
 | 
						||
}
 | 
						||
function get_time_fr() {
 | 
						||
    LC_TIME=C date +"%Hh%M"
 | 
						||
}
 | 
						||
function parse_date() {
 | 
						||
    local value="$1" type="${2:-date}"
 | 
						||
    local now="$(awk 'BEGIN { print mktime(strftime("%Y %m %d 00 00 00 +0400")) }')"
 | 
						||
    case "$value" in
 | 
						||
    =*) value="${value#=}";;
 | 
						||
    +*) value="$(($now + ${value#+} * 86400))";;
 | 
						||
    -*) value="$(($now - ${value#-} * 86400))";;
 | 
						||
    *) value="$(<<<"$value" awk -F/ '{
 | 
						||
        nd = strftime("%d"); nm = strftime("%m"); ny = strftime("%Y")
 | 
						||
        d = $1 + 0; if (d < 1) d = nd;
 | 
						||
        m = $2 + 0; if (m < 1) m = nm;
 | 
						||
        if ($3 == "") y = ny;
 | 
						||
        else { y = $3 + 0; if (y < 100) y = y + 2000; }
 | 
						||
        print mktime(sprintf("%04i %02i %02i 00 00 00 +0400", y, m, d));
 | 
						||
      }')";;
 | 
						||
    esac
 | 
						||
    case "$type" in
 | 
						||
    d|date) awk '{ print strftime("%d/%m/%Y", $0 + 0) }' <<<"$value";;
 | 
						||
    l|ldap) awk '{ print strftime("%Y%m%d%H%M%S+0400", $0 + 0) }' <<<"$value";;
 | 
						||
    m|mysql) awk '{ print strftime("%Y-%m-%d", $0 + 0) }' <<<"$value";;
 | 
						||
    t|ts|timestamp) echo "$value";;
 | 
						||
    *) recho "$value";;
 | 
						||
    esac
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## chemins
 | 
						||
 | 
						||
function udelpath() {
 | 
						||
    # supprimer le chemin $1 de $2(=PATH)
 | 
						||
    local _qdir="${1//\//\\/}"
 | 
						||
    eval "export ${2:-PATH}; ${2:-PATH}"'="${'"${2:-PATH}"'#$1:}"; '"${2:-PATH}"'="${'"${2:-PATH}"'%:$1}"; '"${2:-PATH}"'="${'"${2:-PATH}"'//:$_qdir:/:}"; [ "$'"${2:-PATH}"'" == "$1" ] && '"${2:-PATH}"'='
 | 
						||
}
 | 
						||
function uaddpath() {
 | 
						||
    # Ajouter le chemin $1 à la fin, dans $2(=PATH), s'il n'y existe pas déjà
 | 
						||
    local _qdir="${1//\//\\/}"
 | 
						||
    eval "export ${2:-PATH}; "'[ "${'"${2:-PATH}"'#$1:}" == "$'"${2:-PATH}"'" -a "${'"${2:-PATH}"'%:$1}" == "$'"${2:-PATH}"'" -a "${'"${2:-PATH}"'//:$_qdir:/:}" == "$'"${2:-PATH}"'" -a "$'"${2:-PATH}"'" != "$1" ] && '"${2:-PATH}"'="${'"${2:-PATH}"':+$'"${2:-PATH}"':}$1"'
 | 
						||
}
 | 
						||
function uinspathm() {
 | 
						||
    # Ajouter le chemin $1 au début, dans $2(=PATH), s'il n'y existe pas déjà
 | 
						||
    local _qdir="${1//\//\\/}"
 | 
						||
    eval "export ${2:-PATH}; "'[ "${'"${2:-PATH}"'#$1:}" == "$'"${2:-PATH}"'" -a "${'"${2:-PATH}"'%:$1}" == "$'"${2:-PATH}"'" -a "${'"${2:-PATH}"'//:$_qdir:/:}" == "$'"${2:-PATH}"'" -a "$'"${2:-PATH}"'" != "$1" ] && '"${2:-PATH}"'="$1${'"${2:-PATH}"':+:$'"${2:-PATH}"'}"'
 | 
						||
}
 | 
						||
function uinspath() {
 | 
						||
    # S'assurer que le chemin $1 soit au début de $2(=PATH)
 | 
						||
    udelpath "$@"
 | 
						||
    uinspathm "$@"
 | 
						||
}
 | 
						||
 | 
						||
function withpath() {
 | 
						||
# tester si le chemin est relatif à . ou à .., ou est absolu. i.e 'withpath a/b'
 | 
						||
# renvoie faux alors que 'withpath ./a/b' renvoie vrai
 | 
						||
    [ "${1#./}" != "$1" -o "${1#../}" != "$1" -o "${1#/}" != "$1" ]
 | 
						||
}
 | 
						||
function withext() {
 | 
						||
# tester si le fichier a une extension
 | 
						||
    local basename="$(basename -- "$1")"
 | 
						||
    [ "${basename%.*}" != "$basename" ]
 | 
						||
}
 | 
						||
function normpath() {
 | 
						||
# normaliser le chemin $1, qui est soit absolu, soit relatif à $2 (qui vaut
 | 
						||
# $(pwd) par défaut)
 | 
						||
    local -a parts
 | 
						||
    local part ap
 | 
						||
    array_split parts "$1" /
 | 
						||
    if [ "${1#/}" != "$1" ]; then
 | 
						||
        ap=/
 | 
						||
    elif [ -n "$2" ]; then
 | 
						||
        ap="$2"
 | 
						||
    else
 | 
						||
        ap="$(pwd)"
 | 
						||
    fi
 | 
						||
    for part in "${parts[@]}"; do
 | 
						||
        if [ "$part" == "." ]; then
 | 
						||
            continue
 | 
						||
        elif [ "$part" == ".." ]; then
 | 
						||
            ap="${ap%/*}"
 | 
						||
            [ -n "$ap" ] || ap=/
 | 
						||
        else
 | 
						||
            [ "$ap" != "/" ] && ap="$ap/"
 | 
						||
            ap="$ap$part"
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    recho "$ap"
 | 
						||
}
 | 
						||
function abspath() {
 | 
						||
# Retourner un chemin absolu vers $1. Si $2 est non nul et si $1 est un chemin
 | 
						||
# relatif, alors $1 est exprimé par rapport à $2, sinon il est exprimé par
 | 
						||
# rapport au répertoire courant.
 | 
						||
# Si le chemin n'existe pas, il n'est PAS normalisé. Sinon, les meilleurs
 | 
						||
# efforts sont faits pour normaliser le chemin.
 | 
						||
    local ap="$1"
 | 
						||
    if [ "${ap#/}" != "$ap" ]; then
 | 
						||
        # chemin absolu. on peut ignorer $2
 | 
						||
        __normpath "$ap" && return
 | 
						||
    else
 | 
						||
        # chemin relatif. il faut exprimer le chemin par rapport à $2
 | 
						||
        local cwd
 | 
						||
        if [ -n "$2" ]; then
 | 
						||
            cwd="$(abspath "$2")"
 | 
						||
        else
 | 
						||
            cwd="$(pwd)"
 | 
						||
        fi
 | 
						||
        ap="$cwd/$ap"
 | 
						||
        __normpath "$ap" && return
 | 
						||
    fi
 | 
						||
    # dans les cas spéciaux, il faut calculer "manuellement" le répertoire absolu
 | 
						||
    normpath "$ap"
 | 
						||
}
 | 
						||
function __normpath() {
 | 
						||
    # normaliser dans les cas simple le chemin absolu $1. sinon retourner 1.
 | 
						||
    # cette fonction est utilisée par abspath()
 | 
						||
    if [ -d "$1" ]; then
 | 
						||
        if [ -x "$1" ]; then
 | 
						||
            # le cas le plus simple: un répertoire dans lequel on peut entrer
 | 
						||
            (cd "$1"; pwd)
 | 
						||
            return 0
 | 
						||
        fi
 | 
						||
    elif [ -f "$1" ]; then
 | 
						||
        local dn="$(dirname -- "$1")" bn="$(basename -- "$1")"
 | 
						||
        if [ -x "$dn" ]; then
 | 
						||
            # autre cas simple: un fichier situé dans un répertoire dans lequel
 | 
						||
            # on peut entrer
 | 
						||
            (cd "$dn"; echo "$(pwd)/$bn")
 | 
						||
            return 0
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function parentdirs() {
 | 
						||
# Obtenir la liste de tous les parents du répertoire $2 dans le tableau $1, du
 | 
						||
# répertoire $2 vers la racine. Si $3 commence par 'r' (comme reverse), l'ordre
 | 
						||
# est inversé: le tableau contient les répertoire de la racine vers $2.
 | 
						||
    array_new "$1"
 | 
						||
    local __pd_d="$(abspath "$2")"
 | 
						||
    if [[ "$3" == r* ]]; then
 | 
						||
        while [ "$__pd_d" != "/" ]; do
 | 
						||
            array_ins "$1" "$__pd_d"
 | 
						||
            __pd_d="$(dirname "$__pd_d")"
 | 
						||
        done
 | 
						||
    else
 | 
						||
        while [ "$__pd_d" != "/" ]; do
 | 
						||
            array_add "$1" "$__pd_d"
 | 
						||
            __pd_d="$(dirname "$__pd_d")"
 | 
						||
        done
 | 
						||
    fi
 | 
						||
}
 | 
						||
function ppath() {
 | 
						||
# Dans un chemin *absolu*, remplacer "$HOME" par "~" et "$(pwd)/" par "", afin
 | 
						||
# que le chemin soit plus facile à lire. Le répertoire courant est spécifié par
 | 
						||
# $2 ou $(pwd) si $2 est vide
 | 
						||
    local path="$1" cwd="$2"
 | 
						||
 | 
						||
    path="$(abspath "$path")" # essayer de normaliser le chemin
 | 
						||
    [ -n "$cwd" ] || cwd="$(pwd)"
 | 
						||
 | 
						||
    [ "$path" = "$cwd" ] && path="."
 | 
						||
    [ "$cwd" != "/" -a "$cwd" != "$HOME" ] && path="${path#$cwd/}"
 | 
						||
    [ "${path#$HOME/}" != "$path" ] && path="~${path#$HOME}"
 | 
						||
 | 
						||
    recho "$path"
 | 
						||
}
 | 
						||
function ppath2() {
 | 
						||
# Comme ppath() mais afficher '.' comme '../$dirname'
 | 
						||
    local path="$1" cwd="$2"
 | 
						||
 | 
						||
    path="$(abspath "$path")" # essayer de normaliser le chemin
 | 
						||
    [ -n "$cwd" ] || cwd="$(pwd)"
 | 
						||
 | 
						||
    [ "$path" = "$cwd" ] && path="../$(basename -- "$path")"
 | 
						||
    [ "$cwd" != "/" -a "$cwd" != "$HOME" ] && path="${path#$cwd/}"
 | 
						||
    [ "${path#$HOME/}" != "$path" ] && path="~${path#$HOME}"
 | 
						||
 | 
						||
    recho "$path"
 | 
						||
}
 | 
						||
function relpath() {
 | 
						||
# Afficher le chemin relatif de $1 par rapport à $2. Si $2 n'est pas spécifié,
 | 
						||
# on prend le répertoire courant. Si $1 ou $2 ne sont pas des chemins absolus,
 | 
						||
# il sont transformés en chemins absolus par rapport à $3. Si $1==$2, retourner
 | 
						||
# une chaine vide
 | 
						||
    local p="$(abspath "$1" "$3")" cwd="$2"
 | 
						||
    if [ -z "$cwd" ]; then
 | 
						||
        cwd="$(pwd)"
 | 
						||
    else
 | 
						||
        cwd="$(abspath "$cwd" "$3")"
 | 
						||
    fi
 | 
						||
    if [ "$p" == "$cwd" ]; then
 | 
						||
        echo ""
 | 
						||
    elif [ "${p#$cwd/}" != "$p" ]; then
 | 
						||
        recho "${p#$cwd/}"
 | 
						||
    else
 | 
						||
        local rp
 | 
						||
        while [ -n "$cwd" -a "${p#$cwd/}" == "$p" ]; do
 | 
						||
            rp="${rp:+$rp/}.."
 | 
						||
            cwd="${cwd%/*}"
 | 
						||
        done
 | 
						||
        rp="$rp/${p#$cwd/}"
 | 
						||
        # ${rp%//} traite le cas $1==/
 | 
						||
        echo "${rp%//}"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function relpathx() {
 | 
						||
# Comme relpath, mais pour un chemin vers un exécutable qu'il faut lancer:
 | 
						||
# s'assurer qu'il y a une spécification de chemin, e.g. ./script
 | 
						||
    local p="$(relpath "$@")"
 | 
						||
    if [ -z "$p" ]; then
 | 
						||
        echo .
 | 
						||
    elif [ "${p#../}" != "$p" -o "${p#./}" != "$p" ]; then
 | 
						||
        echo "$p"
 | 
						||
    else
 | 
						||
        echo "./$p"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function withinpath() {
 | 
						||
# Tester si le chemin absolu $2 se trouve dans le chemin absolu "$1" (appelée
 | 
						||
# barrière). Soit un chemin P, on considère que P est dans P. Si ce comportement
 | 
						||
# n'est pas souhaité, $3(=N) doit valoir O, auquel cas P est dans Q implique que
 | 
						||
# P != Q.
 | 
						||
    local b="$1" p="$2" strict="${3:-N}"
 | 
						||
    b="$(abspath "$b")"
 | 
						||
    p="$(abspath "$p")"
 | 
						||
    if is_yes "$strict"; then
 | 
						||
        [ "${p#$b/}" != "$p" ]
 | 
						||
    else
 | 
						||
        [ "$p" == "$b" -o "${p#$b/}" != "$p" ]
 | 
						||
    fi
 | 
						||
}
 | 
						||
function safe_abspath() {
 | 
						||
# Afficher le chemin absolu de $1, par rapport à $2, si et seulement si le
 | 
						||
# chemin résultat ne se trouve pas en dehors de la barrière $3. Si $2 n'est pas
 | 
						||
# spécifié, prendre le répertoire courant. S'il est relatif, l'exprimer par
 | 
						||
# rapport au répertoire courant. Si $3 est relatif, l'exprimer par rapport à $2.
 | 
						||
# Si le chemin résultat est sité en dehors de la barrière, ne rien afficher et
 | 
						||
# retourner un code d'erreur.
 | 
						||
# Si le chemin $1 n'existe pas, il n'est PAS normalisé. Sinon, les meilleurs
 | 
						||
# efforts sont faits pour normaliser le chemin résultat.
 | 
						||
    local p="$1" ba="$2" br="$3"
 | 
						||
    # calculer basedir
 | 
						||
    if [ -n "$ba" ]; then
 | 
						||
        ba="$(abspath "$ba")"
 | 
						||
    else
 | 
						||
        ba="$(pwd)"
 | 
						||
    fi
 | 
						||
    # calculer barrier. l'exprimer relativement à basedir le cas échéant
 | 
						||
    [ -n "$br" ] || br="$ba"
 | 
						||
    br="$(abspath "$br" "$ba")"
 | 
						||
    # puis calculer le chemin absolu
 | 
						||
    p="$(abspath "$p" "$ba")"
 | 
						||
    # vérifier la barrière
 | 
						||
    if [ "$p" == "$br" -o "${p#$br/}" != "$p" ]; then
 | 
						||
        echo "$p"
 | 
						||
    else
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
function safe_relpath() {
 | 
						||
# Afficher le chemin relatif de $1 par rapport à $2 si et seulement si le chemin
 | 
						||
# résultat ne se trouve pas en dehors de la barrière $3. Si $2 n'est pas
 | 
						||
# spécifié, prendre le répertoire courant. S'il est relatif, l'exprimer par
 | 
						||
# rapport au répertoire courant. Si $3 est relatif, l'exprimer par rapport à $2.
 | 
						||
# Si le chemin résultat est sité en dehors de la barrière, ne rien afficher et
 | 
						||
# retourner un code d'erreur.
 | 
						||
    local p
 | 
						||
    if p="$(safe_abspath "$1" "$2" "$3")"; then
 | 
						||
        relpath "$p" "$2" "$(pwd)"
 | 
						||
    else
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
function splitwcs() {
 | 
						||
# Découper un nom de chemin $1 entre la partie sans wildcards, qui est placée dans
 | 
						||
# la variables $2(=basedir), et la partie avec wildcards, qui est placée dans la
 | 
						||
# variable $3(=filespec)
 | 
						||
    local __sw_p="$1"
 | 
						||
    local __sw_dd="${2:-basedir}" __sw_df="${3:-filespec}" __sw_part __sw_d __sw_f
 | 
						||
    local -a __sw_parts
 | 
						||
    array_split __sw_parts "$__sw_p" "/"
 | 
						||
    for __sw_part in "${__sw_parts[@]}"; do
 | 
						||
        if [[ "$__sw_part" == *\** ]] || [[ "$__sw_part" == *\?* ]] || [ -n "$__sw_f" ]; then
 | 
						||
            __sw_f="${__sw_f:+$__sw_f/}$__sw_part"
 | 
						||
        else
 | 
						||
            __sw_d="${__sw_d:+$__sw_d/}$__sw_part"
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    [ "${__sw_p#/}" != "$__sw_p" ] && __sw_d="/$__sw_d"
 | 
						||
    _setv "$__sw_dd" "$__sw_d"
 | 
						||
    _setv "$__sw_df" "$__sw_f"
 | 
						||
}
 | 
						||
function deref() {
 | 
						||
# Retourner un chemin absolu vers le fichier $1, dans lequel toutes les
 | 
						||
# composantes "lien symbolique" ont été supprimées.
 | 
						||
# DEPRECATED: Cette fonction est dépréciée. Utiliser à la place readlinkm()
 | 
						||
    local OENC="$UTF8"
 | 
						||
 | 
						||
    local max_deref=50
 | 
						||
    local file="$1"
 | 
						||
    local basedir link
 | 
						||
    while [ -L "$file" ]; do
 | 
						||
        basedir="$(dirname "$file")"
 | 
						||
        link="$(readlink "$file")"
 | 
						||
        if first_char_is "$link" "/"; then
 | 
						||
            # lien absolu
 | 
						||
            file="$link"
 | 
						||
        else
 | 
						||
            # lien relatif
 | 
						||
            file="$basedir/$link"
 | 
						||
        fi
 | 
						||
 | 
						||
        max_deref=$(($max_deref - 1))
 | 
						||
        [ $max_deref -eq 0 ] && die "Plus de 50 indirection. Le lien $file est-il récursif?"
 | 
						||
    done
 | 
						||
    abspath "$file"
 | 
						||
}
 | 
						||
function readlinka() {
 | 
						||
# Afficher un chemin absolu vers la destination du fichier $1. Si $1 n'est pas
 | 
						||
# un lien, afficher simplement le chemin du fichier
 | 
						||
    if [ -L "$1" ]; then
 | 
						||
        local linkdir="$(dirname -- "$1")"
 | 
						||
        abspath "$(readlink "$1")" "$linkdir"
 | 
						||
    else
 | 
						||
        abspath "$1"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function readlinkm() {
 | 
						||
# Retourner un chemin absolu vers le fichier $1, dans lequel toutes les
 | 
						||
# composantes "lien symbolique" ont été supprimées. Il n'est pas requis que les
 | 
						||
# composantes du chemin existent.
 | 
						||
    readlink -m "$1"
 | 
						||
}
 | 
						||
function path_if_test() {
 | 
						||
# afficher un chemin si le fichier $2 existe (en utilisant l'opérateur $1) dans
 | 
						||
# l'un des chemins absolus $4..n. si $3==relative, afficher le chemin relatif,
 | 
						||
# sinon le chemin absolu. note: $3 peut être de la forme relative:path, auquel
 | 
						||
# cas le chemin affiché est exprimé relativement à path
 | 
						||
    local op="$1"; shift
 | 
						||
    local file="$1"; shift
 | 
						||
    local rel="$1" reldir=; shift
 | 
						||
    if beginswith "$rel" relative; then
 | 
						||
        reldir="${rel#relative}"
 | 
						||
        if beginswith "$reldir" :; then
 | 
						||
            # on a un argument de la forme relative:path
 | 
						||
            reldir="${reldir#:}"
 | 
						||
            if [ -n "$reldir" ]; then
 | 
						||
                reldir="${reldir}/"
 | 
						||
            fi
 | 
						||
        else
 | 
						||
            # argument vide ou format non valide
 | 
						||
            reldir=
 | 
						||
        fi
 | 
						||
    else
 | 
						||
        rel=
 | 
						||
    fi
 | 
						||
 | 
						||
    while [ -n "$1" ]; do
 | 
						||
        local basedir="$1"
 | 
						||
        if [ $op "$basedir/$file" ]; then
 | 
						||
            if [ -n "$rel" ]; then
 | 
						||
                recho "$reldir$file"
 | 
						||
            else
 | 
						||
                recho "$basedir/$file"
 | 
						||
            fi
 | 
						||
            break
 | 
						||
        fi
 | 
						||
        shift
 | 
						||
    done
 | 
						||
}
 | 
						||
function update_link() {
 | 
						||
    # mettre à jour le lien $2 pour qu'il pointe vers le fichier $1
 | 
						||
    [ -L "$2" ] || return 1
 | 
						||
    local dest link="$2"
 | 
						||
    local linkdir="$(dirname "$link")"
 | 
						||
    local ldest="$(readlink "$link")"
 | 
						||
    if [ "${ldest#/}" != "$ldest" ]; then
 | 
						||
        # c'est un lien absolu, faire un lien absolu
 | 
						||
        dest="$(abspath "$1")"
 | 
						||
    else
 | 
						||
        # c'est un lien relatif, faire un lien relatif
 | 
						||
        dest="$(relpath "$1" "$linkdir")"
 | 
						||
    fi
 | 
						||
    if [ "$dest" == "$ldest" ]; then
 | 
						||
        : # pas besoin de mettre à jour
 | 
						||
    elif [ -d "$link" ]; then
 | 
						||
        rm -f "$link" && ln -s "$dest" "$link"
 | 
						||
    else
 | 
						||
        ln -sf "$dest" "$link"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function update_links() {
 | 
						||
    # Mettre à jour les liens $2..@ pour qu'ils pointent vers la nouvelle
 | 
						||
    # destination $1
 | 
						||
    [ -n "$1" ] || return 1
 | 
						||
    local dest="$1"; shift
 | 
						||
    local r=0 link
 | 
						||
    for link in "$@"; do
 | 
						||
        update_link "$dest" "$link" || r=$?
 | 
						||
    done
 | 
						||
    return $r
 | 
						||
}
 | 
						||
function move_link() {
 | 
						||
    # Déplacer le lien $1 vers $2, et mettre à jour la destination du lien si
 | 
						||
    # elle est exprimée de façon relative
 | 
						||
    # Si $1 n'est pas un lien, le déplacer normalement avec mv
 | 
						||
    [ -n "$1" -a -n "$2" ] || return 1
 | 
						||
    local link="$1" dest="$2"
 | 
						||
    [ -d "$dest" ] && dest="$dest/$(basename -- "$link")"
 | 
						||
    dest="$(abspath "$dest")"
 | 
						||
    if [ -L "$link" ]; then
 | 
						||
        link="$(abspath "$link")"
 | 
						||
        [ "$dest" == "$link" ] && return 0
 | 
						||
        ldest="$(readlinka "$link")"
 | 
						||
        mv "$link" "$dest" || return 1
 | 
						||
        update_link "$ldest" "$dest"
 | 
						||
    else
 | 
						||
        [ "$dest" == "$link" ] && return 0
 | 
						||
        mv "$link" "$dest"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function copy_link() {
 | 
						||
    # Copier le lien $1 vers $2, et mettre à jour la destination du lien si
 | 
						||
    # elle est exprimée de façon relative
 | 
						||
    # Si $1 n'est pas un lien, le copier normalement avec cp
 | 
						||
    [ -n "$1" -a -n "$2" ] || return 1
 | 
						||
    local link="$1" dest="$2"
 | 
						||
    [ -d "$dest" ] && dest="$dest/$(basename -- "$link")"
 | 
						||
    dest="$(abspath "$dest")"
 | 
						||
    if [ -L "$link" ]; then
 | 
						||
        link="$(abspath "$link")"
 | 
						||
        [ "$dest" == "$link" ] && return 0
 | 
						||
        ldest="$(readlinka "$link")"
 | 
						||
        cp -P "$link" "$dest" || return 1
 | 
						||
        update_link "$ldest" "$dest"
 | 
						||
    else
 | 
						||
        [ "$dest" == "$link" ] && return 0
 | 
						||
        cp "$link" "$dest"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function array_find_links() {
 | 
						||
    # Chercher dans le répertoire $3 (qui est par défaut le répertoire courant)
 | 
						||
    # les liens vers le fichier $2, et ajouter leurs chemins absolus dans le
 | 
						||
    # tableau $1
 | 
						||
    local -a __afl_links __afl_result
 | 
						||
    local __afl_dir="${3:-.}"
 | 
						||
    local __afl_dest __afl_destname __afl_link __afl_linkdir __afl_ldest
 | 
						||
    __afl_dest="$(abspath "$2")"
 | 
						||
    __afl_destname="${__afl_dest##*/}"
 | 
						||
    array_from_lines __afl_links "$(find "$__afl_dir" -type l)"
 | 
						||
    for __afl_link in "${__afl_links[@]}"; do
 | 
						||
        __afl_ldest="$(readlink "$__afl_link")"
 | 
						||
        # optimiser le calcul: pas besoin d'aller plus loin si les noms ne
 | 
						||
        # correspondent pas
 | 
						||
        if [ "$__afl_ldest" != "$__afl_destname" ]; then
 | 
						||
            [[ "$__afl_ldest" == */"$__afl_destname" ]] || continue
 | 
						||
        fi
 | 
						||
        # nous avons un candidate, tester si les chemins correspondent
 | 
						||
        __afl_link="$(abspath "$__afl_link" "$__afl_dir")"
 | 
						||
        __afl_linkdir="$(dirname -- "$__afl_link")"
 | 
						||
        __afl_ldest="$(abspath "$__afl_ldest" "$__afl_linkdir")"
 | 
						||
        if [ "$__afl_ldest" == "$__afl_dest" ]; then
 | 
						||
            array_add __afl_result "$__afl_link"
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    array_copy "$1" __afl_result
 | 
						||
}
 | 
						||
function list_links() {
 | 
						||
    # Chercher dans le répertoire $2 les liens vers le fichier $1, et les
 | 
						||
    # afficher, un par ligne.
 | 
						||
    local -a links
 | 
						||
    array_find_links links "$@"
 | 
						||
    array_to_lines links
 | 
						||
}
 | 
						||
function move_file() {
 | 
						||
    # Déplacer le fichier $1 vers $2, et mettre à jour les liens $3..@ pour
 | 
						||
    # qu'ils pointent vers la nouvelle destination
 | 
						||
    [ -n "$1" -a -n "$2" ] || return 1
 | 
						||
    local src="$1" dest="$2" link
 | 
						||
    shift; shift
 | 
						||
    [ -d "$dest" ] && dest="$dest/$(basename -- "$src")"
 | 
						||
    move_link "$src" "$dest" || return 1
 | 
						||
    update_links "$dest" "$@"
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## utilitaires
 | 
						||
function get_nblines() {
 | 
						||
# Afficher le nombre de lignes d'un fichier
 | 
						||
    [ -f "$1" ] && sed -ne '$=' "$1" || echo 0
 | 
						||
}
 | 
						||
function mktempf() {
 | 
						||
# générer un fichier temporaire et retourner son nom
 | 
						||
    mktemp "${1:-"$TMPDIR/tmp.XXXXXX"}"
 | 
						||
}
 | 
						||
function mktempd() {
 | 
						||
# générer un répertoire temporaire et retourner son nom
 | 
						||
    mktemp -d "${1:-"$TMPDIR/tmp.XXXXXX"}"
 | 
						||
}
 | 
						||
function mkdirof() {
 | 
						||
# Créer le répertoire correspondant à un fichier
 | 
						||
    mkdir -p "$(dirname -- "$1")"
 | 
						||
}
 | 
						||
function cp_a() {
 | 
						||
# copier des fichiers en gardant le maximum de propriétés
 | 
						||
    /bin/cp -a "$@"
 | 
						||
}
 | 
						||
function cp_R() {
 | 
						||
# copier des fichiers récursivement, en suivant les liens symboliques
 | 
						||
    /bin/cp -pR "$@"
 | 
						||
}
 | 
						||
function quietgrep() {
 | 
						||
# tester la présence d'un pattern dans un fichier
 | 
						||
    grep -q "$@" 2>/dev/null
 | 
						||
}
 | 
						||
function quietdiff() {
 | 
						||
# tester si deux fichiers sont identiques
 | 
						||
    diff -q "$@" >&/dev/null
 | 
						||
}
 | 
						||
function testsame() {
 | 
						||
# tester si deux fichiers sont identiques/différents
 | 
						||
    quietdiff "$@"
 | 
						||
}
 | 
						||
function testdiff() {
 | 
						||
    ! quietdiff "$@"
 | 
						||
}
 | 
						||
function testupdated() {
 | 
						||
# tester si $2 n'existe pas ou si $1 est différent de $2. Si $3 est non vide,
 | 
						||
# la valeur de retour est fixée à 0 (forcer à considérer le test comme vrai)
 | 
						||
    if [ -n "$3" ]; then return 0
 | 
						||
    elif [ -f "$2" ]; then testdiff "$1" "$2"
 | 
						||
    else return 0
 | 
						||
    fi
 | 
						||
}
 | 
						||
function testnewer() {
 | 
						||
# test si $2 n'existe pas ou si $1 est plus récent que $2. Si $3 est non vide,
 | 
						||
# la valeur de retour est fixée à 0 (forcer à considérer le test comme vrai)
 | 
						||
    [ -n "$3" ] && return 0
 | 
						||
    test ! -e "$2" -o "$1" -nt "$2"
 | 
						||
}
 | 
						||
function ps_all() {
 | 
						||
# afficher tous les processus avec le maximum d'informations
 | 
						||
    ps -axww
 | 
						||
}
 | 
						||
function progexists() {
 | 
						||
# tester l'existence d'un programme dans le PATH
 | 
						||
    test -n "$1" -a -x "$(which "$1" 2>/dev/null)"
 | 
						||
}
 | 
						||
function has_python() {
 | 
						||
# tester la présence de python
 | 
						||
    progexists python
 | 
						||
}
 | 
						||
function has_gawk() {
 | 
						||
# tester la présence de gnuawk
 | 
						||
    progexists gawk
 | 
						||
}
 | 
						||
function is_root() {
 | 
						||
# tester si on est root
 | 
						||
    test `id -u` -eq 0
 | 
						||
}
 | 
						||
function source_ifexists() {
 | 
						||
# sourcer un fichier s'il existe
 | 
						||
    if [ -f "$1" ]; then source "$1" || die; fi
 | 
						||
}
 | 
						||
function little_sleep {
 | 
						||
# s'endormir pour une très petite période de temps
 | 
						||
    LC_NUMERIC=C sleep 0.1
 | 
						||
}
 | 
						||
function random_sleep {
 | 
						||
# s'endormir pour une durée allant de 0 à $1 secondes. Par défaut, $1=1800 (soit 30 minutes)
 | 
						||
    sleep $(($RANDOM % ${1:-1800}))
 | 
						||
}
 | 
						||
function is_running() {
 | 
						||
# tester si un programme dont on donne le PID tourne
 | 
						||
    kill -0 "$1" >&/dev/null
 | 
						||
}
 | 
						||
function sedi() {
 | 
						||
# Lancer sed sur un fichier en le modifiant en place
 | 
						||
    sed -i "$@"
 | 
						||
}
 | 
						||
function csort() {
 | 
						||
# Lancer sort avec LANG=C pour éviter les problèmes avec la locale. en effet,
 | 
						||
# avec LANG!=C, sort utilise les règles de la locale pour le tri, et par
 | 
						||
# exemple, avec LANG=fr_FR.UTF-8, la locale indique que les ponctuations doivent
 | 
						||
# être ignorées.
 | 
						||
    LANG=C sort "$@"
 | 
						||
}
 | 
						||
function lsort() { sort "$@"; }
 | 
						||
function cgrep() {
 | 
						||
# Lancer grep avec LANG=C pour éviter les problèmes avec la locale. cf csort
 | 
						||
# pour une explication.
 | 
						||
    LANG=C grep "$@"
 | 
						||
}
 | 
						||
function lgrep() { grep "$@"; }
 | 
						||
function csed() {
 | 
						||
# Lancer sed avec LANG=C pour éviter les problèmes avec la locale. cf csort pour
 | 
						||
# une explication.
 | 
						||
    LANG=C sed "$@"
 | 
						||
}
 | 
						||
function lsed() { sed "$@"; }
 | 
						||
function cawk() {
 | 
						||
# Lancer awk avec LANG=C pour éviter les problèmes avec la locale. cf csort pour
 | 
						||
# une explication.
 | 
						||
    LANG=C awk "$@"
 | 
						||
}
 | 
						||
function lawk() { awk "$@"; }
 | 
						||
function cdiff() {
 | 
						||
# Lancer diff avec LANG=C pour éviter les problèmes avec la locale. cf csort
 | 
						||
# pour une explication.
 | 
						||
    LANG=C diff "$@"
 | 
						||
}
 | 
						||
function ldiff() { diff "$@"; }
 | 
						||
 | 
						||
################################################################################
 | 
						||
## utilitaires de haut niveau
 | 
						||
 | 
						||
function fix_mode() {
 | 
						||
# Si le fichier $1 n'est pas writable, le rendre writable temporairement. Si
 | 
						||
# nécessaire, le fichier est créé.
 | 
						||
# Cette fonction s'utilise de cette façon:
 | 
						||
#   mode="$(fix_mode file)"
 | 
						||
#   ...
 | 
						||
#   unfix_mode file "$mode"
 | 
						||
    local file="$1"
 | 
						||
    [ -f "$file" ] || touch "$file" || return 1
 | 
						||
    if [ ! -w "$file" ]; then
 | 
						||
        local mode="$(stat -c %a "$file")"
 | 
						||
        chmod ${mode:0:${#mode}-3}6${mode:${#mode}-2:2} "$file"
 | 
						||
        echo "$mode"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function unfix_mode() {
 | 
						||
# Restaurer le mode $2 du fichier $1 traité par fix_mode
 | 
						||
    [ -n "$2" ] && chmod "$2" "$1"
 | 
						||
}
 | 
						||
function get_mode() {
 | 
						||
# Obtenir le mode du fichier $1, en le créant si nécessaire. A utiliser avec
 | 
						||
# unfix_mode pour restaurer le mode d'un fichier qui a été traité avec un
 | 
						||
# fichier temporaire intermédiaire
 | 
						||
    [ -f "$1" ] || touch "$1" || return 1
 | 
						||
    stat -c %a "$1"
 | 
						||
}
 | 
						||
function rm_maybe() {
 | 
						||
# Supprimer les fichiers dont on donne la liste. Si aucun fichier n'est
 | 
						||
# spécifié, cette fonction est un NOP
 | 
						||
    local parse_opts=1 arg rm
 | 
						||
    for arg in "$@"; do
 | 
						||
        # chercher s'il y a un argument autre que des options
 | 
						||
        if [ -n "$parse_opts" ]; then
 | 
						||
            if [ "$arg" == "--" ]; then
 | 
						||
                parse_opts=
 | 
						||
            elif [[ "$arg" == "-*" ]]; then
 | 
						||
                continue
 | 
						||
            elif [ -n "$arg" ]; then
 | 
						||
                rm=1
 | 
						||
                break
 | 
						||
            fi
 | 
						||
        elif [ -n "$arg" ]; then
 | 
						||
            rm=1
 | 
						||
            break
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    [ -n "$rm" ] && /bin/rm "$@"
 | 
						||
}
 | 
						||
__CPDIR_RSYNC_SLOW=1 # synchro potentiellement plus lente, mais plus fidèle (option -c)
 | 
						||
__CPDIR_RSYNC_ARGS=(-q)
 | 
						||
function cpdir() {
 | 
						||
# copier un fichier dans un répertoire, ou le contenu d'un répertoire dans un
 | 
						||
# autre répertoire, que le répertoire source soit un lien symbolique ou
 | 
						||
# non. Cette fonction existe parce que le comportement de "cp_a src dest" n'est
 | 
						||
# pas consistant selon les plateformes, surtout si src est un lien symbolique
 | 
						||
# sur un répertoire: parfois on copie le lien, parfois on copie le contenu du
 | 
						||
# répertoire, parfois on copie le répertoire...
 | 
						||
# La copie est faite avec rsync si possible. Les options du tableau
 | 
						||
# __CPDIR_RSYNC_ARGS sont rajoutées aux options standard de rsync.
 | 
						||
 | 
						||
    if progexists rsync; then
 | 
						||
        [ -d "$2" ] || mkdir -p "$2" || return 1
 | 
						||
        if [ -d "$1" ]; then
 | 
						||
            rsync -a ${__CPDIR_RSYNC_SLOW:+-c} "${__CPDIR_RSYNC_ARGS[@]}" "$1/" "$2/"
 | 
						||
        else
 | 
						||
            rsync -a ${__CPDIR_RSYNC_SLOW:+-c} "${__CPDIR_RSYNC_ARGS[@]}" "$1" "$2/"
 | 
						||
        fi
 | 
						||
    else
 | 
						||
        __cpdir "$@"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function __cpdir() {
 | 
						||
    # implémentation avec bash de cpdir(). cette fonction est utilisée par
 | 
						||
    # cpdir() quand rsync n'est pas disponible
 | 
						||
    # $1=src, $2=dest, $3=method (cp_a par défaut)
 | 
						||
    local src="$1" dest="$2" method="${3:-cp_a}"
 | 
						||
 | 
						||
    if [ -d "$src" ]; then
 | 
						||
        # si c'est un répertoire, traitement particulier
 | 
						||
        # tout d'abord, s'assurer que la destination existe
 | 
						||
        [ -d "$dest" ] || mkdir -p "$dest" || return 1
 | 
						||
 | 
						||
        # ensuite on fait la copie
 | 
						||
        local prevdir="$(pwd)"
 | 
						||
 | 
						||
        dest="$(abspath "$dest")"
 | 
						||
        cd "$src"
 | 
						||
        if [ -n "$(/bin/ls -a1)" ]; then
 | 
						||
            # copier les fichiers
 | 
						||
            [ -n "$(/bin/ls -1)" ] && "$method" * "$dest"
 | 
						||
            # ne pas oublier les fichiers cachés...
 | 
						||
            local i
 | 
						||
            for i in .*; do
 | 
						||
                [ "$i" == "." -o "$i" == ".." ] && continue
 | 
						||
                "$method" "$i" "$dest"
 | 
						||
            done
 | 
						||
        fi
 | 
						||
        cd "$prevdir"
 | 
						||
    else
 | 
						||
        # sinon, on assume que c'est un fichier
 | 
						||
        if [ -f "$dest" ]; then
 | 
						||
            # copie du fichier avec remplacement
 | 
						||
            "$method" "$src" "$dest"
 | 
						||
        elif [ -d "$dest" ]; then
 | 
						||
            # copie du fichier dans le répertoire
 | 
						||
            "$method" "$src" "$dest"
 | 
						||
        else
 | 
						||
            # Copie du fichier dans le répertoire $dest qui est créé pour la
 | 
						||
            # circonstance
 | 
						||
            mkdir -p "$dest"
 | 
						||
            "$method" "$src" "$dest"
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
__CPNOVCS_RSYNC_SLOW=1 # synchro potentiellement plus lente, mais plus fidèle (option -c)
 | 
						||
__CPNOVCS_RSYNC_ARGS=(-q)
 | 
						||
__CPNOVCS_INCLUDE_VCS= # ne pas ignorer les répertoires de VCS
 | 
						||
function cpnovcs() {
 | 
						||
# copier le fichier/répertoire $1 *dans* le *répertoire* $2 avec rsync. Les
 | 
						||
# options du tableau __CPNOVCS_RSYNC_ARGS sont rajoutées aux options standard
 | 
						||
# de rsync.
 | 
						||
# Si $1 est un répertoire, la copie est faite en ignorant les sous-répertoires
 | 
						||
# de VCS (.svn, CVS). En ce qui concerne les répertoire de VCS, git aussi est
 | 
						||
# supporté, mais uniquement s'il est à la racine du transfert.
 | 
						||
# Si $1 se termine par un '/', c'est le contenu du répertoire qui est copié, pas
 | 
						||
# le répertoire lui-même. Si rsync n'est pas trouvé sur le système, alors on
 | 
						||
# fait une copie standard qui inclue les répertoires de VCS.
 | 
						||
    local src="$1" destdir="$2"
 | 
						||
    [ -d "$destdir" ] || mkdir -p "$destdir" || return 1
 | 
						||
    if progexists rsync; then
 | 
						||
        local -a novcs
 | 
						||
        if [ -z "$__CPNOVCS_INCLUDE_VCS" ]; then
 | 
						||
            local gitexclude=/.git/
 | 
						||
            [ "${src%/}" == "$src" ] && gitexclude="/$(basename -- "$src")$gitexclude"
 | 
						||
            novcs=(--exclude CVS/ --exclude .svn/ --exclude "$gitexclude")
 | 
						||
        fi
 | 
						||
        rsync -a ${__CPNOVCS_RSYNC_SLOW:+-c} "${novcs[@]}" "${__CPNOVCS_RSYNC_ARGS[@]}" "$src" "$destdir/"
 | 
						||
    elif [ "${src%/}" != "$src" ]; then
 | 
						||
        __cpdir "$src" "$destdir"
 | 
						||
    else
 | 
						||
        local srcname="$(basename -- "$src")"
 | 
						||
        mkdir -p "$destdir/$srcname"
 | 
						||
        __cpdir "$src" "$destdir/$srcname"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function cpvcs() {
 | 
						||
# comme cpnovcs, mais ne pas ignorer les répertoires de VCS
 | 
						||
    local __CPNOVCS_INCLUDE_VCS=1
 | 
						||
    cpnovcs "$@"
 | 
						||
}
 | 
						||
function cpdirnovcs() {
 | 
						||
# Le pendant de cpdir, mais en ignorant les sous-répertoires de VCS: copier le
 | 
						||
# contenu du répertoire $1 dans le répertoire $2
 | 
						||
    if [ -d "$1" ]; then
 | 
						||
        cpnovcs "$1/" "$2"
 | 
						||
    else
 | 
						||
        cpnovcs "$1" "$2"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function doinplace() {
 | 
						||
# Filtrer le fichier $1 à travers la commande $2..$*, puis remplacer le fichier
 | 
						||
# s'il n'y a pas eu d'erreur. Retourner le code d'erreur de la commande. Si $1
 | 
						||
# n'est pas spécifié ou vaut -, filtrer l'entrée standard vers la sortie
 | 
						||
# standard.
 | 
						||
# La variante doinplacef remplace le fichier quelque soit le code de retour de
 | 
						||
# la commande. A utiliser avec des commandes comme grep qui peuvent retourner
 | 
						||
# FAUX s'ils ne trouvent pas le motif
 | 
						||
    if [ -n "$1" -a "$1" != "-" ]; then
 | 
						||
        local __dip_file="$1"; shift
 | 
						||
        autoclean "$__dip_file.tmp.$$"
 | 
						||
        "$@" <"$__dip_file" >"$__dip_file.tmp.$$"
 | 
						||
        local s=$?
 | 
						||
        [ "$s" == 0 ] && /bin/cat "$__dip_file.tmp.$$" >"$__dip_file"
 | 
						||
        /bin/rm -f "$__dip_file.tmp.$$"
 | 
						||
        return $s
 | 
						||
    else
 | 
						||
        shift
 | 
						||
        "$@"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function doinplacef() {
 | 
						||
    if [ -n "$1" -a "$1" != "-" ]; then
 | 
						||
        local __dip_file="$1"; shift
 | 
						||
        autoclean "$__dip_file.tmp.$$"
 | 
						||
        "$@" <"$__dip_file" >"$__dip_file.tmp.$$"
 | 
						||
        local s=$?
 | 
						||
        /bin/cat "$__dip_file.tmp.$$" >"$__dip_file"
 | 
						||
        /bin/rm -f "$__dip_file.tmp.$$"
 | 
						||
        return $s
 | 
						||
    else
 | 
						||
        shift
 | 
						||
        "$@"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function stripnl() {
 | 
						||
# Supprimer les caractères de fin de ligne de la chaine en entrée
 | 
						||
    tr -d '\r\n'
 | 
						||
}
 | 
						||
function _nl2lf() {
 | 
						||
# transformer les fins de ligne en LF
 | 
						||
    awk 'BEGIN {RS="\r|\r\n|\n"} {print}'
 | 
						||
}
 | 
						||
function nl2lf() {
 | 
						||
    doinplace "$1" _nl2lf
 | 
						||
}
 | 
						||
function _nl2crlf() {
 | 
						||
# transformer les fins de ligne en CRLF
 | 
						||
    awk 'BEGIN {RS="\r|\r\n|\n"} {print $0 "\r"}'
 | 
						||
}
 | 
						||
function nl2crlf() {
 | 
						||
    doinplace "$1" _nl2crlf
 | 
						||
}
 | 
						||
function _nl2cr() {
 | 
						||
# transformer les fins de ligne en CR
 | 
						||
    awk 'BEGIN {RS="\r|\r\n|\n"; ORS=""} {print $0 "\r"}'
 | 
						||
}
 | 
						||
function nl2cr() {
 | 
						||
    doinplace "$1" _nl2cr
 | 
						||
}
 | 
						||
function _latin1compat() {
 | 
						||
# transformer certains caractères UTF-8 en leur équivalent UTF-8 transformable
 | 
						||
# en latin1.
 | 
						||
    LANG=fr_FR.UTF-8 sed $'
 | 
						||
s/[\xE2\x80\x90\xE2\x80\x91\xE2\x80\x92\xE2\x80\x93\xE2\x80\x94\xE2\x80\x95]/-/g
 | 
						||
s/[‘’]/\x27/g
 | 
						||
s/[«»“”]/"/g
 | 
						||
s/[\xC2\xA0\xE2\x80\x87\xE2\x80\xAF\xE2\x81\xA0]/ /g
 | 
						||
s/[\xE2\x80\xA6]/.../g
 | 
						||
s/[œ]/oe/g
 | 
						||
s/[Œ]/OE/g
 | 
						||
s/[æ]/ae/g
 | 
						||
s/[Æ]/AE/g
 | 
						||
s/a\xCC\x80/à/g
 | 
						||
s/e\xCC\x81/é/g; s/e\xCC\x80/è/g; s/e\xCC\x82/ê/g; s/e\xCC\x88/ë/g
 | 
						||
s/i\xCC\x88/ï/g; s/i\xCC\x82/î/g
 | 
						||
s/o\xCC\x82/ô/g; s/o\xCC\x88/ö/g
 | 
						||
s/u\xCC\x88/ü/g; s/u\xCC\x82/û/g
 | 
						||
s/c\xCC\xA7/ç/g
 | 
						||
s/A\xCC\x80/À/g
 | 
						||
s/E\xCC\x81/É/g; s/E\xCC\x80/È/g; s/E\xCC\x82/Ê/g; s/E\xCC\x88/Ë/g
 | 
						||
s/I\xCC\x88/Ï/g; s/I\xCC\x82/Î/g
 | 
						||
s/O\xCC\x82/Ô/g; s/O\xCC\x88/Ö/g
 | 
						||
s/U\xCC\x88/Ü/g; s/U\xCC\x82/Û/g
 | 
						||
s/C\xCC\xA7/Ç/g
 | 
						||
'
 | 
						||
}
 | 
						||
function _noaccents() {
 | 
						||
# supprimer les accents d'un flux en UTF-8
 | 
						||
    LANG=fr_FR.UTF-8 sed '
 | 
						||
s/[à]/a/g
 | 
						||
s/[éèêë]/e/g
 | 
						||
s/[ïî]/i/g
 | 
						||
s/[ôö]/o/g
 | 
						||
s/[üû]/u/g
 | 
						||
s/[ç]/c/g
 | 
						||
s/[À]/A/g
 | 
						||
s/[ÉÈÊË]/E/g
 | 
						||
s/[ÏÎ]/I/g
 | 
						||
s/[ÔÖ]/O/g
 | 
						||
s/[ÜÛ]/U/g
 | 
						||
s/[Ç]/C/g
 | 
						||
'
 | 
						||
}
 | 
						||
function list_all() {
 | 
						||
# Lister les fichiers ou répertoires du répertoire $1, un par ligne
 | 
						||
# Les répertoires . et .. sont enlevés de la liste
 | 
						||
# $1=un répertoire dont le contenu doit être listé
 | 
						||
# $2..@=un ensemble de patterns pour le listage
 | 
						||
    local curdir="$(pwd)"
 | 
						||
    local b="${1:-.}"; shift
 | 
						||
 | 
						||
    cd "$b" 2>/dev/null || return
 | 
						||
    eval "$(__la_cmd "$@")" | while read f; do
 | 
						||
        [ "$f" == "." -o "$f" == ".." ] && continue
 | 
						||
        recho "$f"
 | 
						||
    done
 | 
						||
    cd "$curdir"
 | 
						||
}
 | 
						||
function __la_cmd() {
 | 
						||
    [ $# -gt 0 ] || set '*'
 | 
						||
    local arg
 | 
						||
    local cmd="/bin/ls -1d"
 | 
						||
    for arg in "$@"; do
 | 
						||
        arg="$(qwc "$arg")"
 | 
						||
        cmd="$cmd $arg"
 | 
						||
    done
 | 
						||
    cmd="$cmd 2>/dev/null"
 | 
						||
    echo "$cmd"
 | 
						||
}
 | 
						||
function list_files() {
 | 
						||
# Lister les fichiers du répertoire $1, un par ligne
 | 
						||
# $1=un répertoire dont le contenu doit être listé.
 | 
						||
# $2..@=un ensemble de patterns pour le listage
 | 
						||
    local f
 | 
						||
    local curdir="$(pwd)"
 | 
						||
    local b="${1:-.}"; shift
 | 
						||
 | 
						||
    cd "$b" 2>/dev/null || return
 | 
						||
    eval "$(__la_cmd "$@")" | while read f; do
 | 
						||
        [ -f "$f" ] && recho "$f"
 | 
						||
    done
 | 
						||
    cd "$curdir"
 | 
						||
}
 | 
						||
function list_dirs() {
 | 
						||
# Lister les répertoires du répertoire $1, un par ligne
 | 
						||
# Les répertoires . et .. sont enlevés de la liste
 | 
						||
# $1=un répertoire dont le contenu doit être listé.
 | 
						||
# $2..@=un ensemble de patterns pour le listage
 | 
						||
    local f
 | 
						||
    local curdir="$(pwd)"
 | 
						||
    local b="${1:-.}"; shift
 | 
						||
 | 
						||
    cd "$b" 2>/dev/null || return
 | 
						||
    eval "$(__la_cmd "$@")" | while read f; do
 | 
						||
        [ "$f" == "." -o "$f" == ".." ] && continue
 | 
						||
        [ -d "$f" ] && recho "$f"
 | 
						||
    done
 | 
						||
    cd "$curdir"
 | 
						||
}
 | 
						||
function __array_ls() {
 | 
						||
# Lister les fichiers avec `list_$1 $3 $4...`, et les mettre dans le tableau
 | 
						||
# $2. Note: le tableau contient les chemins complets, par seulement les noms
 | 
						||
# comme avec list_$1
 | 
						||
    local __al_l="list_${1:-all}"; shift
 | 
						||
    local __al_an="$1"; shift
 | 
						||
    local __al_d="${1:-.}"; shift
 | 
						||
    local -a __al_fs
 | 
						||
    array_from_lines __al_fs "$("$__al_l" "$__al_d" "$@")"
 | 
						||
    local __al_f
 | 
						||
    array_new "$__al_an"
 | 
						||
    for __al_f in "${__al_fs[@]}"; do
 | 
						||
        array_add "$__al_an" "$__al_d/$__al_f"
 | 
						||
    done
 | 
						||
}
 | 
						||
function array_lsall() {
 | 
						||
# Lister les fichiers avec `list_all $2 $3...`, et les mettre dans le
 | 
						||
# tableau $1. Le tableau contient les chemins complets, par seulement les
 | 
						||
# noms comme avec list_all
 | 
						||
    __array_ls all "$@"
 | 
						||
}
 | 
						||
function array_lsdirs() {
 | 
						||
# Lister les fichiers avec `list_dirs $2 $3...`, et les mettre dans le
 | 
						||
# tableau $1. Le tableau contient les chemins complets, par seulement les
 | 
						||
# noms comme avec list_dirs
 | 
						||
    __array_ls dirs "$@"
 | 
						||
}
 | 
						||
function array_lsfiles() {
 | 
						||
# Lister les fichiers avec `list_files $2 $3...`, et les mettre dans le
 | 
						||
# tableau $1. Le tableau contient les chemins complets, par seulement les
 | 
						||
# noms comme avec list_files
 | 
						||
    __array_ls files "$@"
 | 
						||
}
 | 
						||
function filter_empty() {
 | 
						||
# Filtrer l'entrée standard en enlevant les lignes vides
 | 
						||
    sed '/^$/d'
 | 
						||
}
 | 
						||
function filter_vcspath() {
 | 
						||
# L'entrée standard étant une liste de chemins, filtrer les fichiers et
 | 
						||
# répertoire qui ont un rapport avec subversion ou git
 | 
						||
    sed '
 | 
						||
/^.git$/d
 | 
						||
/^.git\//d
 | 
						||
/\/.git$/d
 | 
						||
/\/.git\//d
 | 
						||
/^.svn$/d
 | 
						||
/^.svn\//d
 | 
						||
/\/.svn$/d
 | 
						||
/\/.svn\//d
 | 
						||
'
 | 
						||
}
 | 
						||
function merge_contlines() {
 | 
						||
# Avec les lignes lues sur stdin, fusionner celles qui se terminent par \ avec
 | 
						||
# les suivantes.
 | 
						||
    awk 'substr($0, length($0)) == "\\" {
 | 
						||
  while (getline nextline) {
 | 
						||
    $0 = substr($0, 1, length($0) - 1) nextline
 | 
						||
    if (substr($0, length($0)) != "\\") break
 | 
						||
  }
 | 
						||
  print
 | 
						||
  next
 | 
						||
}
 | 
						||
{print}'
 | 
						||
}
 | 
						||
function filter_comment() {
 | 
						||
# Filtrer un fichier de configuration lu sur stdin en enlevant les commentaires
 | 
						||
# et les lignes vides.
 | 
						||
# Avec $1==-m, fusionner les lignes qui se terminent par \ avec les suivantes
 | 
						||
# Comme filter_conf(), les commentaires doivent être sur une ligne à part.
 | 
						||
# Contrairement à filter_conf, il n'est pas nécessaire que le caractère '#' soit
 | 
						||
# en début de ligne: il peut apparaitre après des espaces et des tabulations. De
 | 
						||
# même, une ligne qui ne contient que des espaces et des tabulations est
 | 
						||
# considérée comme vide.
 | 
						||
    local -a merge
 | 
						||
    [ "$1" == -m ] && merge=(merge_contlines) || merge=(cat)
 | 
						||
    awk '
 | 
						||
 /^[ \t]*#/ { next }
 | 
						||
 /^[ \t]*$/ { next }
 | 
						||
 { print }' | "${merge[@]}"
 | 
						||
}
 | 
						||
function filter_conf() {
 | 
						||
# filtrer un fichier de configuration lu sur stdin en enlevant les commentaires
 | 
						||
# et les lignes vides. Une ligne n'est considérée commentaire que si '#' est un
 | 
						||
# première position. Utiliser filter_comment() si les commentaire peuvent
 | 
						||
# commencer par des caractères espace et tabulation.
 | 
						||
# Si $1==-m, fusionner les lignes qui se terminent par \ avec les suivantes
 | 
						||
    local -a merge
 | 
						||
    [ "$1" == -m ] && merge=(merge_contlines) || merge=(cat)
 | 
						||
    grep -v '^#' | grep -v '^$' | "${merge[@]}"
 | 
						||
}
 | 
						||
function is_archive() {
 | 
						||
# tester si l'extension d'un fichier indique que c'est une archive
 | 
						||
    local name="${1%.zip}"
 | 
						||
    name="${name%.tgz}"
 | 
						||
    name="${name%.tbz2}"
 | 
						||
    name="${name%.tar.gz}"
 | 
						||
    name="${name%.tar.bz2}"
 | 
						||
    name="${name%.tar}"
 | 
						||
    name="${name%.jar}"
 | 
						||
    name="${name%.war}"
 | 
						||
    name="${name%.ear}"
 | 
						||
    [ "$name" != "$1" ]
 | 
						||
}
 | 
						||
function extract_archive() {
 | 
						||
# Extraire le contenu de l'archive $1 dans le répertoire ${2:-.}
 | 
						||
# Les autres arguments, s'ils sont spécifiés, indiquent les fichiers à extraire
 | 
						||
    local arch="$1" destdir="${2:-.}"
 | 
						||
    shift; shift
 | 
						||
    if endswith "$arch" .zip; then
 | 
						||
        unzip -q -d "$destdir" "$arch" "$@" || return
 | 
						||
    elif endswith "$arch" .tgz || endswith "$arch" .tar.gz; then
 | 
						||
        tar xzf "$arch" -C "$destdir" "$@" || return
 | 
						||
    elif endswith "$arch" .tbz2 || endswith "$arch" .tar.bz2; then
 | 
						||
        tar xjf "$arch" -C "$destdir" "$@" || return
 | 
						||
    elif endswith "$arch" .tar; then
 | 
						||
        tar xf "$arch" -C "$destdir" "$@" || return
 | 
						||
    elif endswith "$arch" .jar || endswith "$arch" .war || endswith "$arch" .ear; then
 | 
						||
        (
 | 
						||
            arch="$(abspath "$arch")"
 | 
						||
            cd "$destdir"
 | 
						||
            jar xf "$arch" "$@"
 | 
						||
        ) || return
 | 
						||
    else
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
}
 | 
						||
function get_archive_basename() {
 | 
						||
# Obtenir le nom de base de l'archive $1
 | 
						||
    local basename="$(basename -- "$1")"
 | 
						||
    # supprimer l'extension
 | 
						||
    basename="${basename%.zip}"
 | 
						||
    basename="${basename%.tgz}"
 | 
						||
    basename="${basename%.tbz2}"
 | 
						||
    basename="${basename%.gz}"
 | 
						||
    basename="${basename%.bz2}"
 | 
						||
    basename="${basename%.tar}"
 | 
						||
    basename="${basename%.jar}"
 | 
						||
    basename="${basename%.war}"
 | 
						||
    basename="${basename%.ear}"
 | 
						||
    # résultat
 | 
						||
    echo "$basename"
 | 
						||
}
 | 
						||
function get_archive_appname() {
 | 
						||
# Obtenir le nom probable de l'application ou du framework contenu dans
 | 
						||
# l'archive $1, e.g:
 | 
						||
#     get_archive_appname app-0.1.tgz
 | 
						||
#     --> app
 | 
						||
    local appname="$(basename -- "$1")"
 | 
						||
    # supprimer l'extension
 | 
						||
    appname="${appname%.zip}"
 | 
						||
    appname="${appname%.tgz}"
 | 
						||
    appname="${appname%.tbz2}"
 | 
						||
    appname="${appname%.gz}"
 | 
						||
    appname="${appname%.bz2}"
 | 
						||
    appname="${appname%.tar}"
 | 
						||
    appname="${appname%.jar}"
 | 
						||
    appname="${appname%.war}"
 | 
						||
    appname="${appname%.ear}"
 | 
						||
    # supprimer la version et afficher
 | 
						||
    echo "$appname" | awk '{
 | 
						||
  if (match($0, /[-_.]([0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/)) {
 | 
						||
    print substr($0, 1, RSTART - 1)
 | 
						||
  } else if (match($0, /([0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/)) {
 | 
						||
    print substr($0, 1, RSTART - 1)
 | 
						||
  } else if (match($0, /([0-9]+[a-z][a-z][a-z0-9]?)$/, vs)) {
 | 
						||
    # version style AMUE, e.g. 430la
 | 
						||
    print substr($0, 1, RSTART - 1)
 | 
						||
  } else {
 | 
						||
    print $0
 | 
						||
  }
 | 
						||
}'
 | 
						||
}
 | 
						||
function get_archive_versionsuffix() {
 | 
						||
# Obtenir la valeur probable de la version de l'application ou du framework
 | 
						||
# contenu dans l'archive $1, avec le caractère de séparation, e.g:
 | 
						||
#     get_archive_versionsuffix app-0.1.tgz
 | 
						||
#     --> -0.1
 | 
						||
    local basename="$(get_archive_basename "$1")"
 | 
						||
    echo "$basename" | awk '{
 | 
						||
  if (match($0, /([-_.][0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/, vs)) {
 | 
						||
    print vs["1"]
 | 
						||
  } else if (match($0, /([0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/, vs)) {
 | 
						||
    print vs["1"]
 | 
						||
  } else if (match($0, /([0-9]+[a-z][a-z][a-z0-9]?)$/, vs)) {
 | 
						||
    # version style AMUE, e.g. 430la
 | 
						||
    print vs["1"]
 | 
						||
  }
 | 
						||
}'
 | 
						||
}
 | 
						||
function get_archive_version() {
 | 
						||
# Obtenir la valeur probable de la version de l'application ou du framework
 | 
						||
# contenu dans l'archive $1, e.g:
 | 
						||
#     get_archive_version app-0.1.tgz
 | 
						||
#     --> 0.1
 | 
						||
    local basename="$(get_archive_basename "$1")"
 | 
						||
    echo "$basename" | awk '{
 | 
						||
  if (match($0, /[-_.]([0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/, vs)) {
 | 
						||
    print vs["1"]
 | 
						||
  } else if (match($0, /([0-9]+([-_.][0-9]+)*([a-zA-Z][0-9]*|[-_.][0-9]+[a-zA-Z][0-9]*)?)$/, vs)) {
 | 
						||
    print vs["1"]
 | 
						||
  } else if (match($0, /([0-9]+[a-z][a-z][a-z0-9]?)$/, vs)) {
 | 
						||
    # version style AMUE, e.g. 430la
 | 
						||
    print vs["1"]
 | 
						||
  }
 | 
						||
}'
 | 
						||
}
 | 
						||
function __dump_usernames() {
 | 
						||
    </etc/passwd awkrun FS=: '($3 + 0) >= 500 && $6 ~ /^\/home\// { print $1 }'
 | 
						||
}
 | 
						||
function dump_usernames() {
 | 
						||
# Placer dans le tableau $1 la liste des utilisateurs du système
 | 
						||
# Cette implémentation consulte /etc/passwd et liste tous les utilisateurs dont
 | 
						||
# le homedir se trouve dans /home, et dont l'uid est >=500
 | 
						||
    array_from_lines "${1:-usernames}" "$(__dump_usernames)"
 | 
						||
}
 | 
						||
function __resolv_ips() {
 | 
						||
    LANG=C host "$1" 2>/dev/null | awk '/address / { gsub(/^.*address /, ""); print }'
 | 
						||
}
 | 
						||
function resolv_ips() {
 | 
						||
    # Placer dans le tableau $1(=ips) la liste des adresses ip correspondant à
 | 
						||
    # l'hôte $2. La résolution est effectuée avec la commande host.
 | 
						||
    array_from_lines "${1:-ips}" "$(__resolv_ips "$2")"
 | 
						||
}
 | 
						||
function __resolv_hosts() {
 | 
						||
    LANG=C host "$1" 2>/dev/null | awk '/domain name pointer / { gsub(/^.*domain name pointer /, ""); gsub(/\.$/, ""); print }'
 | 
						||
}
 | 
						||
function resolv_hosts() {
 | 
						||
    # Placer dans le tableau $1(=hosts) la liste des hôtes correspondant à
 | 
						||
    # l'adresse ip $2. La résolution est effectuée avec la commande host.
 | 
						||
    array_from_lines "${1:-hosts}" "$(__resolv_hosts "$2")"
 | 
						||
}
 | 
						||
function runscript_as() {
 | 
						||
# Utiliser bash pour lancer le script $2 avec les arguments $3..$n afin qu'il
 | 
						||
# tourne avec les droits d'un autre user $1(=root). Si $2=exec, utiliser exec
 | 
						||
# pour lancer le script et ses arguments qui commencent à partir de $3, ce qui
 | 
						||
# fait que cette fonction ne retourne pas.
 | 
						||
# Attention! cette fonction ne teste pas avec si on est déjà le user $1. Il y a
 | 
						||
# donc un risque de boucle infinie si on ne teste pas le user courant.
 | 
						||
    local OENC="$UTF8"
 | 
						||
    local user="${1:-root}"; shift
 | 
						||
    local exec_maybe=
 | 
						||
    if [ "$1" = "exec" ]; then
 | 
						||
        exec_maybe=exec
 | 
						||
        shift
 | 
						||
    fi
 | 
						||
 | 
						||
    local cmd
 | 
						||
    cmd="\
 | 
						||
__estack=$(qval "$__estack")
 | 
						||
__tlevel=$(qval "$__tlevel")
 | 
						||
export __estack __tlevel
 | 
						||
exec ${BASH:-/bin/sh} $(qvals "$@")"
 | 
						||
 | 
						||
    if is_yes "$UTOOLS_USES_SU" || ! progexists sudo; then
 | 
						||
        eecho "Entrez le mot de passe de root"
 | 
						||
        $exec_maybe su "$user" -c "$cmd"
 | 
						||
    else
 | 
						||
        if [ "$user" == "root" ]; then
 | 
						||
            $exec_maybe sudo -p "Entrez le mot de passe de %u: " "${BASH:-/bin/sh}" -c "$cmd"
 | 
						||
        else
 | 
						||
            $exec_maybe sudo -p "Entrez le mot de passe de %u: " su "$user" -c "$cmd"
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
function runscript_as_root() {
 | 
						||
# Utiliser bash pour lancer le script $1 avec les arguments $2..$* avec les
 | 
						||
# droits de root. Si on est déjà en root, le script est simplement lancé. Sinon,
 | 
						||
# utiliser runscript_as pour lancer le script avec les droits de root.
 | 
						||
    if is_root; then
 | 
						||
        local exec_maybe=
 | 
						||
        if [ "$1" = "exec" ]; then
 | 
						||
            exec_maybe=exec
 | 
						||
            shift
 | 
						||
        fi
 | 
						||
        $exec_maybe "${BASH:-/bin/sh}" "$@"
 | 
						||
    else
 | 
						||
        runscript_as root "$@"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function run_as() {
 | 
						||
# Relancer le script courant afin qu'il tourne avec les droits d'un autre user
 | 
						||
# $1(=root)
 | 
						||
# Attention! cette fonction ne teste pas avec si on est déjà ce user. Il y a
 | 
						||
# donc un risque de boucle infinie si on ne teste pas le user courant.
 | 
						||
# Il faut lancer cette fonction avec les arguments du script en cours. Par
 | 
						||
# exemple::
 | 
						||
#     run_as root "$@"
 | 
						||
# Si $2=--noexec, on n'utilise pas la fonction exec, ce qui fait que la fonction
 | 
						||
# retourne. Sinon, on peut considérer que cette fonction ne retourne jamais
 | 
						||
    local user="${1:-root}"; shift
 | 
						||
    local exec_maybe=exec
 | 
						||
    if [ "$1" = "--noexec" ]; then
 | 
						||
        exec_maybe=
 | 
						||
        shift
 | 
						||
    fi
 | 
						||
 | 
						||
    runscript_as "$user" $exec_maybe "$0" "$@"
 | 
						||
}
 | 
						||
function run_as_root() {
 | 
						||
# relancer le script courant afin qu'il tourne en root si on est pas en déjà
 | 
						||
# root. Sinon, cette fonction est un nop.
 | 
						||
    is_root || run_as root "$@"
 | 
						||
}
 | 
						||
function check_user() {
 | 
						||
# Vérifier si le user courant est l'un des users $1..*
 | 
						||
    local user
 | 
						||
    for user in "$@"; do
 | 
						||
        [ "$USER" == "$user" ] && return 0
 | 
						||
    done
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function ensure_user() {
 | 
						||
# Vérifier si le user courant est l'un des users $1..N où N est la position du
 | 
						||
# premier "--". Si ce n'est pas le cas et que l'on est root, relancer le script
 | 
						||
# avec ce user grâce à la fonction run_as()
 | 
						||
# Retourner 1 si ce n'était pas le bon user. Retourner 10 si ce n'était pas le
 | 
						||
# bon user et que l'on n'est pas root (donc impossible à priori de relancer le
 | 
						||
# script avec le bon user). Retourner 11 si l'utilisateur a choisi de ne pas
 | 
						||
# lancer le script avec le bon utilisateur
 | 
						||
# A utiliser de cette manière:
 | 
						||
#     if ensure_user users... -- args; then
 | 
						||
#         # ... on est avec le bon user; faire les opérations
 | 
						||
#     else
 | 
						||
#         # ... ce code n'est exécuté que si une erreur s'est produite, ou si ce
 | 
						||
#         # n'était pas le bon user et que l'option --noexec est utilisée
 | 
						||
#     fi
 | 
						||
    local -a users
 | 
						||
    while [ $# -gt 0 -a "$1" != "--" ]; do
 | 
						||
        array_add users "$1"
 | 
						||
        shift
 | 
						||
    done
 | 
						||
    [ "$1" == "--" ] && shift
 | 
						||
 | 
						||
    if ! check_user "${users[@]}"; then
 | 
						||
        if [ ${#users[*]} -gt 1 ]; then
 | 
						||
            ewarn "Cette commande doit être lancée avec l'un des users ${users[*]}"
 | 
						||
        else
 | 
						||
            ewarn "Cette commande doit être lancée avec le user ${users[0]}"
 | 
						||
        fi
 | 
						||
        if ask_yesno "Voulez-vous tenter de relancer la commande avec le bon user?" O; then
 | 
						||
            estep "Lancement du script avec le user ${users[0]}"
 | 
						||
            run_as "${users[0]}" "$@"
 | 
						||
            return 1
 | 
						||
        elif is_root; then
 | 
						||
            return 11
 | 
						||
        else
 | 
						||
            return 10
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function check_hostname() {
 | 
						||
# Vérifier si le hostname courant est l'un des hôtes $1..*
 | 
						||
# localhost matche toujours
 | 
						||
    local userhost user host path
 | 
						||
    for userhost in "$@"; do
 | 
						||
        splitfsep "$userhost" : userhost path
 | 
						||
        splituserhost "$userhost" user host
 | 
						||
        host="${host%%.*}"
 | 
						||
        [ "$host" == localhost -o "$host" == "$MYHOSTNAME" ] && return 0
 | 
						||
    done
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function check_userhostname() {
 | 
						||
# Vérifier si le hostname et éventuellement le user courant sont l'un des
 | 
						||
# arguments $1..*
 | 
						||
# Chaque argument est de la forme [user@]host, mais le test ne tient compte que
 | 
						||
# du nom de l'hôte, sans tenir compte du domaine. Si le user n'est pas spécifié,
 | 
						||
# le test ne porte que sur hostname.
 | 
						||
    local userhost path user host
 | 
						||
    for userhost in "$@"; do
 | 
						||
        if check_hostname "$userhost"; then
 | 
						||
            [[ "$userhost" == *@* ]] || return 0
 | 
						||
            splitfsep "$userhost" : userhost path
 | 
						||
            splituserhost "$userhost" user host
 | 
						||
            check_user "$user" && return 0
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    return 1
 | 
						||
}
 | 
						||
UTOOLS_ENSURE_HOSTNAME_SSH_OPTS=()
 | 
						||
function ensure_hostname() {
 | 
						||
# Vérifier si le hostname et le user courant sont l'un des arguments $1..*
 | 
						||
# Chaque argument est de la forme [user@]host, mais le test ne tient compte que
 | 
						||
# du nom de l'hôte, sans tenir compte du domaine.
 | 
						||
# Si user est spécifié:
 | 
						||
# - Si on est sur le bon hôte mais pas le bon user, ensure_user est lancé avec
 | 
						||
#   l'argument approprié pour relancer le script
 | 
						||
#   Si l'argument était de la forme userhost:path, le répertoire courant est
 | 
						||
#   changé avant de lancer le script avec le bon utilisateur.
 | 
						||
# Sinon (si user n'est pas spécifié):
 | 
						||
# - Si on n'est pas sur le bon hôte, après confirmation le script est lancé avec
 | 
						||
#   ssh sur l'hôte distant avec le user spécifié (qui vaut par défaut root). Ce
 | 
						||
#   script DOIT exister sur l'hôte distant avec le même chemin.
 | 
						||
#   Si l'argument était de la forme userhost:path, le répertoire courant distant
 | 
						||
#   est changé avant de lancer le script
 | 
						||
# Si on est avec le bon user sur le bon hôte, le répertoire courant n'est jamais
 | 
						||
# changé.
 | 
						||
# Retourner 1 si ce n'était pas le bon user. Retourner 10 si ce n'était pas le
 | 
						||
# bon user et que l'on n'est pas root (donc impossible à priori de relancer le
 | 
						||
# script avec le bon user). Retourner 11 si l'utilisateur a choisi de ne pas
 | 
						||
# lancer le script sur l'hôte distant. Retourner 12 si une erreur s'est produite
 | 
						||
# avec ssh.
 | 
						||
# A utiliser de cette manière:
 | 
						||
#     if ensure_hostname user@host... -- args; then
 | 
						||
#         # ... on est [avec le bon user] sur le bon hôte; faire les opérations
 | 
						||
#     else
 | 
						||
#         # ... ce code n'est exécuté que si une erreur s'est produite, ou si ce
 | 
						||
#         # n'était pas le bon user et que l'option --noexec est utilisée
 | 
						||
#     fi
 | 
						||
    local -a userhosts
 | 
						||
    while [ $# -gt 0 -a "$1" != "--" ]; do
 | 
						||
        array_add userhosts "$1"
 | 
						||
        shift
 | 
						||
    done
 | 
						||
    [ "$1" == "--" ] && shift
 | 
						||
 | 
						||
    local userhost user host path
 | 
						||
    if ! check_hostname "${userhosts[@]}"; then
 | 
						||
        if [ ${#userhosts[*]} -gt 1 ]; then
 | 
						||
            ewarn "Cette commande n'est valide que sur l'un des hôtes ${userhosts[*]}"
 | 
						||
        else
 | 
						||
            ewarn "Cette commande n'est valide que sur l'hôte ${userhosts[0]}"
 | 
						||
        fi
 | 
						||
 | 
						||
        eimportant "Vous pouvez tenter de relancer le script sur ${userhosts[0]}"
 | 
						||
        eimportant "Cela requière que ce script ET les données dont il a besoin soient installés dans la même version et dans le même répertoire sur l'hôte distant.
 | 
						||
En l'occurence, ce script est accédé par le chemin $script et ce chemin doit exister aussi sur le serveur distant."
 | 
						||
        if ask_yesno "Voulez-vous tenter de relancer le script sur l'hôte distant?" X; then
 | 
						||
            splitfsep "${userhosts[0]}" : userhost path
 | 
						||
            splituserhost "$userhost" user host
 | 
						||
            [ -n "$user" ] || user=root
 | 
						||
 | 
						||
            estep "Lancement de la commande sur l'hôte distant $user@$host"
 | 
						||
            local cmd
 | 
						||
            [ -n "$path" ] && cmd="$(qvals cd "$path"); "
 | 
						||
            cmd="$cmd$(qvals "$script" "$@")"
 | 
						||
            ssh -qt "${UTOOLS_ENSURE_HOSTNAME_SSH_OPTS[@]}" "$user@$host" "$cmd"
 | 
						||
            [ $? -eq 255 ] && return 12
 | 
						||
            return 1
 | 
						||
        else
 | 
						||
            return 11
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    # nous sommes sur le bon hôte. Si user est spécifié, le vérifier aussi
 | 
						||
    local userhost user host
 | 
						||
    for userhost in "${userhosts[@]}"; do
 | 
						||
        [[ "$userhost" == *@* ]] || continue
 | 
						||
        if check_hostname "$userhost"; then
 | 
						||
            splitfsep "$userhost" : userhost path
 | 
						||
            splituserhost "$userhost" user host
 | 
						||
            [ -n "$path" ] && cd "$path"
 | 
						||
            ensure_user "$user" -- "$@"
 | 
						||
            return $?
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    return 0
 | 
						||
}
 | 
						||
 | 
						||
__AWKDEF_FUNCTIONS='
 | 
						||
function num(s) {
 | 
						||
  if (s ~ /^[0-9]+$/) return int(s)
 | 
						||
  else return s
 | 
						||
}
 | 
						||
function ord(s,    i) {
 | 
						||
  s = substr(s, 1, 1)
 | 
						||
  i = index(" !\"#$%&'\''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", s)
 | 
						||
  if (i != 0) i += 32 - 1
 | 
						||
  return i
 | 
						||
}
 | 
						||
function hex(i,    s) {
 | 
						||
  s = sprintf("%x", i)
 | 
						||
  if (length(s) < 2) s = "0" s
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qhtml(s) {
 | 
						||
  gsub(/&/, "\\&", s)
 | 
						||
  gsub(/"/, "\\"", s)
 | 
						||
  gsub(/>/, "\\>", s)
 | 
						||
  gsub(/</, "\\<", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function unquote_html(s) {
 | 
						||
  gsub(/</, "<", s)
 | 
						||
  gsub(/>/, ">", s)
 | 
						||
  gsub(/"/, "\"", s)
 | 
						||
  gsub(/&/, "\\&", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qawk(s) {
 | 
						||
  gsub(/\\/, "\\\\", s)
 | 
						||
  gsub(/"/, "\\\"", s)
 | 
						||
  gsub(/\n/, "\\n", s)
 | 
						||
  return "\"" s "\""
 | 
						||
}
 | 
						||
function qval(s) {'"
 | 
						||
  gsub(/'/, \"'\\\\''\", s)
 | 
						||
  return \"'\" s \"'\"
 | 
						||
"'}
 | 
						||
function sqval(s) {
 | 
						||
  return " " qval(s)
 | 
						||
}
 | 
						||
function qvals(             i, line) {
 | 
						||
  line = ""
 | 
						||
  for (i = 1; i <= NF; i++) {
 | 
						||
    if (i > 1) line = line " "
 | 
						||
    line = line qval($i)
 | 
						||
  }
 | 
						||
  return line
 | 
						||
}
 | 
						||
function sqvals() {
 | 
						||
  return " " qvals()
 | 
						||
}
 | 
						||
function qarr(values, prefix,    i, count, line) {
 | 
						||
  line = prefix
 | 
						||
  count = array_len(values)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    if (i > 1 || line != "") line = line " "
 | 
						||
    line = line qval(values[i])
 | 
						||
  }
 | 
						||
  return line
 | 
						||
}
 | 
						||
function qregexp(s) {
 | 
						||
  gsub(/[[\\.^$*+?()|{]/, "\\\\&", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qsubrepl(s) {
 | 
						||
  gsub(/\\/, "\\\\", s)
 | 
						||
  gsub(/&/, "\\\\&", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qgrep(s) {
 | 
						||
  gsub(/[[\\.^$*]/, "\\\\&", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qegrep(s) {
 | 
						||
  gsub(/[[\\.^$*+?()|{]/, "\\\\&", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function qsql(s, suffix) {'"
 | 
						||
  gsub(/'/, \"''\", s)
 | 
						||
  return \"'\" s \"'\" (suffix != \"\"? \" \" suffix: \"\")
 | 
						||
"'}
 | 
						||
function cqsql(s, suffix) {
 | 
						||
  return "," qsql(s, suffix)
 | 
						||
}
 | 
						||
function unquote_mysqlcsv(s) {
 | 
						||
  gsub(/\\n/, "\n", s)
 | 
						||
  gsub(/\\t/, "\t", s)
 | 
						||
  gsub(/\\0/, "\0", s)
 | 
						||
  gsub(/\\\\/, "\\", s)
 | 
						||
  return s
 | 
						||
}
 | 
						||
function sval(s) {
 | 
						||
  if (s == "") return s
 | 
						||
  else return " " s
 | 
						||
}
 | 
						||
function cval(s, suffix) {
 | 
						||
  if (s == "") return s
 | 
						||
  else return "," s (suffix != ""? " " suffix: "")
 | 
						||
}
 | 
						||
# aliases pour compatibilité
 | 
						||
function quote_html(s) { return qhtml(s) }
 | 
						||
function quote_value(s) { return qval(s) }
 | 
						||
function qsval(s) { return sqval(s) }
 | 
						||
function quoted_values() { return qvals() }
 | 
						||
function qsvals(s) { return sqvals(s) }
 | 
						||
function quote_regexp(s) { return qregexp(s) }
 | 
						||
function quote_subrepl(s) { return qsubrepl(s) }
 | 
						||
function quote_grep(s) { return qgrep(s) }
 | 
						||
function quote_egrep(s) { return qegrep(s) }
 | 
						||
function quote_sql(s) { return qsql(s) }
 | 
						||
 | 
						||
function __parse_date_fr(date,            parts, y, m, d) {
 | 
						||
  if (match(date, /([0-9][0-9]?)\/([0-9][0-9]?)\/([0-9][0-9][0-9][0-9])/, parts)) {
 | 
						||
    y = int(parts[3])
 | 
						||
    m = int(parts[2])
 | 
						||
    d = int(parts[1])
 | 
						||
    return mktime(sprintf("%04i %02i %02i 00 00 00 +0400", y, m, d))
 | 
						||
  } else if (match(date, /([0-9][0-9]?)\/([0-9][0-9]?)\/([0-9][0-9])/, parts)) {
 | 
						||
    basey = int(strftime("%Y")); basey = basey - basey % 100
 | 
						||
    y = basey + int(parts[3])
 | 
						||
    m = int(parts[2])
 | 
						||
    d = int(parts[1])
 | 
						||
    return mktime(sprintf("%04i %02i %02i 00 00 00 +0400", y, m, d))
 | 
						||
  }
 | 
						||
  return -1
 | 
						||
}
 | 
						||
function __parse_date_mysql(date,            parts, y, m, d) {
 | 
						||
  if (match(date, /([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])/, parts)) {
 | 
						||
    y = int(parts[1])
 | 
						||
    m = int(parts[2])
 | 
						||
    d = int(parts[3])
 | 
						||
    return mktime(sprintf("%04i %02i %02i 00 00 00 +0400", y, m, d))
 | 
						||
  }
 | 
						||
  return -1
 | 
						||
}
 | 
						||
function __parse_date_any(date,          serial) {
 | 
						||
  serial = __parse_date_fr(date)
 | 
						||
  if (serial == -1) serial = __parse_date_mysql(date)
 | 
						||
  return serial
 | 
						||
}
 | 
						||
function date_serial(date) {
 | 
						||
  return __parse_date_any(date)
 | 
						||
}
 | 
						||
function date_parse(date,         serial) {
 | 
						||
  serial = __parse_date_any(date)
 | 
						||
  if (serial == -1) return date
 | 
						||
  return strftime("%d/%m/%Y", serial)
 | 
						||
}
 | 
						||
function date_monday(date,             serial, dow) {
 | 
						||
  serial = __parse_date_any(date)
 | 
						||
  if (serial == -1) return date
 | 
						||
  dow = strftime("%u", serial)
 | 
						||
  serial -= (dow - 1) * 86400
 | 
						||
  return strftime("%d/%m/%Y", serial)
 | 
						||
}
 | 
						||
function date_add(date, nbdays,         serial) {
 | 
						||
  serial = __parse_date_any(date)
 | 
						||
  if (serial == -1) return date
 | 
						||
  serial += nbdays * 86400
 | 
						||
  return strftime("%d/%m/%Y", serial)
 | 
						||
}
 | 
						||
 | 
						||
function mkindices(values, indices,        i, j) {
 | 
						||
  array_new(indices)
 | 
						||
  j = 1
 | 
						||
  for (i in values) {
 | 
						||
    indices[j++] = int(i)
 | 
						||
  }
 | 
						||
  return asort(indices)
 | 
						||
}
 | 
						||
function array_new(dest) {
 | 
						||
  dest[0] = 0 # forcer awk à considérer dest comme un tableau
 | 
						||
  delete dest
 | 
						||
}
 | 
						||
function array_newsize(dest, size,              i) {
 | 
						||
  dest[0] = 0 # forcer awk à considérer dest comme un tableau
 | 
						||
  delete dest
 | 
						||
  size = int(size)
 | 
						||
  for (i = 1; i <= size; i++) {
 | 
						||
    dest[i] = ""
 | 
						||
  }
 | 
						||
}
 | 
						||
function array_len(values,    count, i) {
 | 
						||
  # length(array) a un bug sur awk 3.1.5
 | 
						||
  # cette version est plus lente mais fonctionne toujours
 | 
						||
  count = 0
 | 
						||
  for (i in values) {
 | 
						||
    count++
 | 
						||
  }
 | 
						||
  return count
 | 
						||
}
 | 
						||
function array_copy(dest, src,             count, indices, i) {
 | 
						||
  array_new(dest)
 | 
						||
  count = mkindices(src, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    dest[indices[i]] = src[indices[i]]
 | 
						||
  }
 | 
						||
}
 | 
						||
function array_getlastindex(src,           count, indices) {
 | 
						||
  count = mkindices(src, indices)
 | 
						||
  if (count == 0) return 0
 | 
						||
  return indices[count]
 | 
						||
}
 | 
						||
function array_add(dest, value,              lastindex) {
 | 
						||
  lastindex = array_getlastindex(dest)
 | 
						||
  dest[lastindex + 1] = value
 | 
						||
}
 | 
						||
function array_deli(dest, i,                 l) {
 | 
						||
  i = int(i)
 | 
						||
  if (i == 0) return
 | 
						||
  l = array_len(dest)
 | 
						||
  while (i < l) {
 | 
						||
    dest[i] = dest[i + 1]
 | 
						||
    i++
 | 
						||
  }
 | 
						||
  delete dest[l]
 | 
						||
}
 | 
						||
function array_del(dest, value, ignoreCase,              i) {
 | 
						||
  do {
 | 
						||
    i = key_index(value, dest, ignoreCase)
 | 
						||
    if (i != 0) array_deli(dest, i)
 | 
						||
  } while (i != 0)
 | 
						||
}
 | 
						||
function array_extend(dest, src,             count, lastindex, indices, i) {
 | 
						||
  lastindex = array_getlastindex(dest)
 | 
						||
  count = mkindices(src, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    dest[lastindex + i] = src[indices[i]]
 | 
						||
  }
 | 
						||
}
 | 
						||
function array_fill(dest,           i) {
 | 
						||
  array_new(dest)
 | 
						||
  for (i = 1; i <= NF; i++) {
 | 
						||
    dest[i] = $i
 | 
						||
  }
 | 
						||
}
 | 
						||
function array_getline(src,             count, indices, i, j) {
 | 
						||
  $0 = ""
 | 
						||
  count = mkindices(src, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    j = indices[i]
 | 
						||
    $j = src[j]
 | 
						||
  }
 | 
						||
}
 | 
						||
function array_appendline(src,             count, indices, i, nf, j) {
 | 
						||
  count = mkindices(src, indices)
 | 
						||
  nf = NF
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    j = nf + indices[i]
 | 
						||
    $j = src[indices[i]]
 | 
						||
  }
 | 
						||
}
 | 
						||
function in_array(value, values, ignoreCase,         i) {
 | 
						||
  if (ignoreCase) {
 | 
						||
    value = tolower(value)
 | 
						||
    for (i in values) {
 | 
						||
      if (tolower(values[i]) == value) return 1
 | 
						||
    }
 | 
						||
  } else {
 | 
						||
    for (i in values) {
 | 
						||
      if (values[i] == value) return 1
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return 0
 | 
						||
}
 | 
						||
function key_index(value, values, ignoreCase,         i) {
 | 
						||
  if (ignoreCase) {
 | 
						||
    value = tolower(value)
 | 
						||
    for (i in values) {
 | 
						||
      if (tolower(values[i]) == value) return int(i)
 | 
						||
    }
 | 
						||
  } else {
 | 
						||
    for (i in values) {
 | 
						||
      if (values[i] == value) return int(i)
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return 0
 | 
						||
}
 | 
						||
function array2s(values, prefix, sep, suffix, noindices,     first, i, s) {
 | 
						||
  if (!prefix) prefix = "["
 | 
						||
  if (!sep) sep = ", "
 | 
						||
  if (!suffix) suffix = "]"
 | 
						||
  s = prefix
 | 
						||
  first = 1
 | 
						||
  for (i in values) {
 | 
						||
    if (first) first = 0
 | 
						||
    else s = s sep
 | 
						||
    if (!noindices) s = s "[" i "]="
 | 
						||
    s = s values[i]
 | 
						||
  }
 | 
						||
  s = s suffix
 | 
						||
  return s
 | 
						||
}
 | 
						||
function array2so(values, prefix, sep, suffix, noindices,      count, indices, i, s) {
 | 
						||
  if (!prefix) prefix = "["
 | 
						||
  if (!sep) sep = ", "
 | 
						||
  if (!suffix) suffix = "]"
 | 
						||
  s = prefix
 | 
						||
  count = mkindices(values, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    if (i > 1) s = s sep
 | 
						||
    if (!noindices) s = s "[" indices[i] "]="
 | 
						||
    s = s values[indices[i]]
 | 
						||
  }
 | 
						||
  s = s suffix
 | 
						||
  return s
 | 
						||
}
 | 
						||
function array_join(values, sep, prefix, suffix,            count, indices, i, s) {
 | 
						||
  s = prefix
 | 
						||
  count = mkindices(values, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    if (i > 1) s = s sep
 | 
						||
    s = s values[indices[i]]
 | 
						||
  }
 | 
						||
  s = s suffix
 | 
						||
  return s
 | 
						||
}
 | 
						||
function printto(s, output) {
 | 
						||
  if (output == "") {
 | 
						||
    print s
 | 
						||
  } else if (output ~ /^>>/) {
 | 
						||
    sub(/^>>/, "", output)
 | 
						||
    print s >>output
 | 
						||
  } else if (output ~ /^>/) {
 | 
						||
    sub(/^>/, "", output)
 | 
						||
    print s >output
 | 
						||
  # XXX désactivé pour le moment parce que affiche une erreur de syntaxe si
 | 
						||
  # utilisé avec une version de awk autre que gnuawk
 | 
						||
  #} else if (output ~ /^\|&/) {
 | 
						||
  #  sub(/^\|&/, "", output)
 | 
						||
  #  print s |&output
 | 
						||
  #} else if (output ~ /^\|/) {
 | 
						||
  #  sub(/^\|/, "", output)
 | 
						||
  #  print s |output
 | 
						||
  } else {
 | 
						||
    print s >output
 | 
						||
  }
 | 
						||
}
 | 
						||
function find_line(input, field, value,              orig, line) {
 | 
						||
  orig = $0
 | 
						||
  line = ""
 | 
						||
  while ((getline <input) > 0) {
 | 
						||
    if ($field == value) {
 | 
						||
      line = $0
 | 
						||
      break
 | 
						||
    }
 | 
						||
  }
 | 
						||
  close(input)
 | 
						||
  $0 = orig
 | 
						||
  return line
 | 
						||
}
 | 
						||
function merge_line(input, field, key,               line) {
 | 
						||
  line = find_line(input, field, $key)
 | 
						||
  if (line != "") $0 = $0 FS line
 | 
						||
}
 | 
						||
function __csv_parse_quoted(line, destl, colsep, qchar, echar,       pos, tmpl, nextc, resl) {
 | 
						||
  line = substr(line, 2)
 | 
						||
  resl = ""
 | 
						||
  while (1) {
 | 
						||
    pos = index(line, qchar)
 | 
						||
    if (pos == 0) {
 | 
						||
      # chaine mal terminee
 | 
						||
      resl = resl line
 | 
						||
      destl[0] = ""
 | 
						||
      destl[1] = 0
 | 
						||
      return resl
 | 
						||
    }
 | 
						||
    if (echar != "" && pos > 1) {
 | 
						||
      # tenir compte du fait qu"un caratère peut être mis en échappement
 | 
						||
      prevc = substr(line, pos - 1, 1)
 | 
						||
      quotec = substr(line, pos, 1)
 | 
						||
      nextc = substr(line, pos + 1, 1)
 | 
						||
      if (prevc == echar) {
 | 
						||
        # qchar en échappement
 | 
						||
        tmpl = substr(line, 1, pos - 2)
 | 
						||
        resl = resl tmpl quotec
 | 
						||
        line = substr(line, pos + 1)
 | 
						||
        continue
 | 
						||
      }
 | 
						||
      tmpl = substr(line, 1, pos - 1)
 | 
						||
      if (nextc == colsep || nextc == "") {
 | 
						||
        # fin de champ ou fin de ligne
 | 
						||
        resl = resl tmpl
 | 
						||
        destl[0] = substr(line, pos + 2)
 | 
						||
        destl[1] = nextc == colsep
 | 
						||
        return resl
 | 
						||
      } else {
 | 
						||
        # erreur de syntaxe: guillemet non mis en échappement
 | 
						||
        # ignorer cette erreur et prendre le guillemet quand meme
 | 
						||
        resl = resl tmpl quotec
 | 
						||
        line = substr(line, pos + 1)
 | 
						||
      }
 | 
						||
    } else {
 | 
						||
      # pas d"échappement pour qchar. il est éventuellement doublé
 | 
						||
      tmpl = substr(line, 1, pos - 1)
 | 
						||
      quotec = substr(line, pos, 1)
 | 
						||
      nextc = substr(line, pos + 1, 1)
 | 
						||
      if (nextc == colsep || nextc == "") {
 | 
						||
        # fin de champ ou fin de ligne
 | 
						||
        resl = resl tmpl
 | 
						||
        destl[0] = substr(line, pos + 2)
 | 
						||
        destl[1] = nextc == colsep
 | 
						||
        return resl
 | 
						||
      } else if (nextc == qchar) {
 | 
						||
        # qchar en echappement
 | 
						||
        resl = resl tmpl quotec
 | 
						||
        line = substr(line, pos + 2)
 | 
						||
      } else {
 | 
						||
        # erreur de syntaxe: guillemet non mis en échappement
 | 
						||
        # ignorer cette erreur et prendre le guillemet quand meme
 | 
						||
        resl = resl tmpl quotec
 | 
						||
        line = substr(line, pos + 1)
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
function __csv_parse_unquoted(line, destl, colsep, qchar, echar,     pos) {
 | 
						||
  pos = index(line, colsep)
 | 
						||
  if (pos == 0) {
 | 
						||
    destl[0] = ""
 | 
						||
    destl[1] = 0
 | 
						||
    return line
 | 
						||
  } else {
 | 
						||
    destl[0] = substr(line, pos + 1)
 | 
						||
    destl[1] = 1
 | 
						||
    return substr(line, 1, pos - 1)
 | 
						||
  }
 | 
						||
}
 | 
						||
function __array_parsecsv(fields, line, nbfields, colsep, qchar, echar,     shouldparse, destl, i) {
 | 
						||
  array_new(fields)
 | 
						||
  array_new(destl)
 | 
						||
  i = 1
 | 
						||
  shouldparse = 0
 | 
						||
  # shouldparse permet de gérer le cas où un champ vide est en fin de ligne.
 | 
						||
  # en effet, après "," il faut toujours parser, même si line==""
 | 
						||
  while (shouldparse || line != "") {
 | 
						||
    if (index(line, qchar) == 1) {
 | 
						||
      value = __csv_parse_quoted(line, destl, colsep, qchar, echar)
 | 
						||
      line = destl[0]
 | 
						||
      shouldparse = destl[1]
 | 
						||
    } else {
 | 
						||
      value = __csv_parse_unquoted(line, destl, colsep, qchar, echar)
 | 
						||
      line = destl[0]
 | 
						||
      shouldparse = destl[1]
 | 
						||
    }
 | 
						||
    fields[i] = value
 | 
						||
    i = i + 1
 | 
						||
  }
 | 
						||
  if (nbfields) {
 | 
						||
    nbfields = int(nbfields)
 | 
						||
    i = array_len(fields)
 | 
						||
    while (i < nbfields) {
 | 
						||
      i++
 | 
						||
      fields[i] = ""
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return array_len(fields)
 | 
						||
}
 | 
						||
BEGIN {
 | 
						||
  DEFAULT_COLSEP = ","
 | 
						||
  DEFAULT_QCHAR = "\""
 | 
						||
  DEFAULT_ECHAR = ""
 | 
						||
}
 | 
						||
function array_parsecsv2(fields, line, nbfields, colsep, qchar, echar) {
 | 
						||
  return __array_parsecsv(fields, line, nbfields, colsep, qchar, echar)
 | 
						||
}
 | 
						||
function array_parsecsv(fields, line, nbfields, colsep, qchar, echar) {
 | 
						||
  if (colsep == "") colsep = DEFAULT_COLSEP
 | 
						||
  if (qchar == "") qchar = DEFAULT_QCHAR
 | 
						||
  if (echar == "") echar = DEFAULT_ECHAR
 | 
						||
  return __array_parsecsv(fields, line, nbfields, colsep, qchar, echar)
 | 
						||
}
 | 
						||
function parsecsv(line,             fields) {
 | 
						||
  array_parsecsv(fields, line)
 | 
						||
  array_getline(fields)
 | 
						||
  return NF
 | 
						||
}
 | 
						||
function getlinecsv(file,          fields) {
 | 
						||
  if (file) {
 | 
						||
    getline <file
 | 
						||
  } else {
 | 
						||
    getline
 | 
						||
  }
 | 
						||
  return parsecsv($0)
 | 
						||
}
 | 
						||
function __csv_should_quote(s) {
 | 
						||
  if (s ~ /^[[:blank:][:cntrl:][:space:]]/) return 1
 | 
						||
  if (s ~ /[[:blank:][:cntrl:][:space:]]$/) return 1
 | 
						||
  return 0
 | 
						||
}
 | 
						||
function array_formatcsv2(fields, colsep, mvsep, qchar, echar,      count, indices, line, i, value) {
 | 
						||
  line = ""
 | 
						||
  count = mkindices(fields, indices)
 | 
						||
  for (i = 1; i <= count; i++) {
 | 
						||
    value = fields[indices[i]]
 | 
						||
    if (i > 1) line = line colsep
 | 
						||
    if (qchar != "" && index(value, qchar) != 0) {
 | 
						||
      if (echar != "") gsub(qchar, quote_subrepl(echar) "&", value);
 | 
						||
      else gsub(qchar, "&&", value);
 | 
						||
    }
 | 
						||
    if (qchar != "" && (index(value, mvsep) != 0 || index(value, colsep) != 0 || index(value, qchar) != 0 || __csv_should_quote(value))) {
 | 
						||
      line = line qchar value qchar
 | 
						||
    } else {
 | 
						||
      line = line value
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return line
 | 
						||
}
 | 
						||
function array_formatcsv(fields) {
 | 
						||
  return array_formatcsv2(fields, ",", ";", "\"", "")
 | 
						||
}
 | 
						||
function array_printcsv(fields, output) {
 | 
						||
  printto(array_formatcsv(fields), output)
 | 
						||
}
 | 
						||
function get_formatcsv(                 fields) {
 | 
						||
  array_fill(fields)
 | 
						||
  return array_formatcsv(fields)
 | 
						||
}
 | 
						||
function formatcsv() {
 | 
						||
  $0 = get_formatcsv()
 | 
						||
}
 | 
						||
function printcsv(output,           fields) {
 | 
						||
  array_fill(fields)
 | 
						||
  array_printcsv(fields, output)
 | 
						||
}
 | 
						||
function array_findcsv(fields, input, field, value, nbfields,          orig, found, i) {
 | 
						||
  array_new(orig)
 | 
						||
  array_fill(orig)
 | 
						||
  array_new(fields)
 | 
						||
  found = 0
 | 
						||
  while ((getline <input) > 0) {
 | 
						||
    array_parsecsv(fields, $0, nbfields)
 | 
						||
    if (fields[field] == value) {
 | 
						||
      found = 1
 | 
						||
      break
 | 
						||
    }
 | 
						||
  }
 | 
						||
  close(input)
 | 
						||
  array_getline(orig)
 | 
						||
  if (!found) {
 | 
						||
    delete fields
 | 
						||
    if (nbfields) {
 | 
						||
      nbfields = int(nbfields)
 | 
						||
      i = array_len(fields)
 | 
						||
      while (i < nbfields) {
 | 
						||
        i++
 | 
						||
        fields[i] = ""
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return found
 | 
						||
}
 | 
						||
 | 
						||
function __and(var, x, l_res, l_i) {
 | 
						||
  l_res=0;
 | 
						||
  for (l_i=0; l_i < 8; l_i++){
 | 
						||
    if (var%2 == 1 && x%2 == 1) l_res=l_res/2 + 128;
 | 
						||
    else l_res/=2;
 | 
						||
    var=int(var/2);
 | 
						||
    x=int(x/2);
 | 
						||
  }
 | 
						||
  return l_res;
 | 
						||
}
 | 
						||
# Rotate bytevalue left x times
 | 
						||
function __lshift(var, x) {
 | 
						||
  while(x > 0){
 | 
						||
    var*=2;
 | 
						||
    x--;
 | 
						||
  }
 | 
						||
  return var;
 | 
						||
}
 | 
						||
# Rotate bytevalue right x times
 | 
						||
function __rshift(var, x) {
 | 
						||
  while(x > 0){
 | 
						||
    var=int(var/2);
 | 
						||
    x--;
 | 
						||
  }
 | 
						||
  return var;
 | 
						||
}
 | 
						||
BEGIN {
 | 
						||
  __BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 | 
						||
}
 | 
						||
function b64decode(src,      result, base1, base2, base3, base4) {
 | 
						||
  result = ""
 | 
						||
  while (length(src) > 0) {
 | 
						||
    # Specify byte values
 | 
						||
    base1 = substr(src, 1, 1)
 | 
						||
    base2 = substr(src, 2, 1)
 | 
						||
    base3 = substr(src, 3, 1); if (base3 == "") base3 = "="
 | 
						||
    base4 = substr(src, 4, 1); if (base4 == "") base4 = "="
 | 
						||
    # Now find numerical position in BASE64 string
 | 
						||
    byte1 = index(__BASE64, base1) - 1
 | 
						||
    if (byte1 < 0) byte1 = 0
 | 
						||
    byte2 = index(__BASE64, base2) - 1
 | 
						||
    if (byte2 < 0) byte2 = 0
 | 
						||
    byte3 = index(__BASE64, base3) - 1
 | 
						||
    if (byte3 < 0) byte3 = 0
 | 
						||
    byte4 = index(__BASE64, base4) - 1
 | 
						||
    if (byte4 < 0) byte4 = 0
 | 
						||
    # Reconstruct ASCII string
 | 
						||
    result = result sprintf( "%c", __lshift(__and(byte1, 63), 2) + __rshift(__and(byte2, 48), 4) )
 | 
						||
    if (base3 != "=") result = result sprintf( "%c", __lshift(__and(byte2, 15), 4) + __rshift(__and(byte3, 60), 2) )
 | 
						||
    if (base4 != "=") result = result sprintf( "%c", __lshift(__and(byte3, 3), 6) + byte4 )
 | 
						||
    # Decrease incoming string with 4
 | 
						||
    src = substr(src, 5)
 | 
						||
  }
 | 
						||
  return result
 | 
						||
}
 | 
						||
'
 | 
						||
function awkdef() {
 | 
						||
# Afficher un script à insérer au début d'un script awk. Ce script définit dans
 | 
						||
# une section BEGIN{} les variables donnés en arguments, et avec l'option -f,
 | 
						||
# des fonctions utiles. Si une valeur ne ressemble pas à une définition de
 | 
						||
# variable, l'analyse des variables s'arrête et le reste des arguments est
 | 
						||
# inséré tel quel. Cette fonction peut être utilisée de cette manière:
 | 
						||
#     awk "$(awkdef -f var=value... 'script awk')"
 | 
						||
# Normalement, les variables définies sont scalaires, avec une syntaxe de la
 | 
						||
# forme var[:type]=value. type peut valoir str ou int, pour forcer le type de la
 | 
						||
# variable créée dans awk.
 | 
						||
# Il est possible d'utiliser la syntaxe awk_array[@]=bash_array ou array[@] (qui
 | 
						||
# est équivalente à array[@]=array) pour initialiser le tableau awk_array, qui
 | 
						||
# contiendra toute les valeurs du tableau nommé bash_array, avec les indices de
 | 
						||
# 1 à N, N étant le nombre d'éléments du tableau bash_array. La variable
 | 
						||
# awk_array_count est aussi initialisée, et contient le nombre d'éléments du
 | 
						||
# tableau
 | 
						||
# La syntaxe "awk_array[@]=<\n..." permet de spécifier les valeurs du tableau,
 | 
						||
# une par ligne, e.g:
 | 
						||
#     $'values[@]=<\nvalue1\nvalue2'
 | 
						||
# pour un tableau values qui contiendra deux valeurs: value1 et value2
 | 
						||
# Avec l'option -f, des fonctions supplémentaires sont définies. Elles sont
 | 
						||
# décrites dans le module awk.
 | 
						||
    [ -z "$__ULIB_NO_DISABLE_SET_X" ] && [[ $- == *x* ]] && { set +x; local __ULIB_AWKDEF_SET_X=1; } # désactiver set -x pour cette fonction
 | 
						||
 | 
						||
    if [ "${1:0:3}" == "-f" ]; then
 | 
						||
        shift
 | 
						||
        echo "$__AWKDEF_FUNCTIONS"
 | 
						||
    fi
 | 
						||
    if [ $# -gt 0 ]; then
 | 
						||
        local __ad_arg __ad_vpos __ad_name __ad_value
 | 
						||
        echo "BEGIN {"
 | 
						||
        while [ -n "${1:0:1}" ]; do
 | 
						||
            # considérer seulement les 256 premiers caractères. en effet, si
 | 
						||
            # l'argument a une taille trop importante, il y a des délais
 | 
						||
            # insupportables
 | 
						||
            __ad_arg="${1:0:256}"
 | 
						||
            local __ad_array=
 | 
						||
            if [ "${__ad_arg%\[@\]}" != "$__ad_arg" ]; then
 | 
						||
                __ad_array=1
 | 
						||
                __ad_name="${__ad_arg%\[@\]}"
 | 
						||
                [ -z "${__ad_name//[a-zA-Z0-9_]/}" ] || break
 | 
						||
                __ad_value="$__ad_name"
 | 
						||
            elif [[ "$__ad_arg" == *\[@\]=* ]]; then
 | 
						||
                __ad_array=1
 | 
						||
                __ad_name="${__ad_arg%%\[@\]=*}"
 | 
						||
                [ -z "${__ad_name//[a-zA-Z0-9_]/}" ] || break
 | 
						||
                __ad_vpos=$((${#__ad_name} + 4))
 | 
						||
                __ad_value="${1:$__ad_vpos}"
 | 
						||
                [ ${#__ad_value} -ne 0 ] || __ad_value="$__ad_name"
 | 
						||
            elif [[ "$__ad_arg" == *=* ]]; then
 | 
						||
                local __ad_int= __ad_str=
 | 
						||
                __ad_name="${__ad_arg%%=*}"
 | 
						||
                __ad_vpos=$((${#__ad_name} + 1))
 | 
						||
                if [ "${__ad_name%:int}" != "$__ad_name" ]; then
 | 
						||
                    __ad_int=1
 | 
						||
                    __ad_name="${__ad_name%:int}"
 | 
						||
                elif [ "${__ad_name%:str}" != "$__ad_name" ]; then
 | 
						||
                    __ad_str=1
 | 
						||
                    __ad_name="${__ad_name%:str}"
 | 
						||
                fi
 | 
						||
                [ -z "${__ad_name//[a-zA-Z0-9_]/}" ] || break
 | 
						||
                __ad_value="${1:$__ad_vpos}"
 | 
						||
                if [ -n "$__ad_int" ]; then
 | 
						||
                    # valeur entière
 | 
						||
                    echo "$__ad_name = int($(qawk "$__ad_value") + 0)"
 | 
						||
                elif [ -n "$__ad_str" ]; then
 | 
						||
                    # valeur chaine
 | 
						||
                    echo "$__ad_name = $(qawk "$__ad_value")"
 | 
						||
                elif [ ${#__ad_value} -lt 256 ] && isnum "$__ad_value"; then
 | 
						||
                    # considérer qu'une nombre fait moins de 256 chiffres
 | 
						||
                    echo "$__ad_name = $__ad_value"
 | 
						||
                else
 | 
						||
                    echo "$__ad_name = $(qawk "$__ad_value")"
 | 
						||
                fi
 | 
						||
            else
 | 
						||
                # fin de l'analyse des définitions de variable
 | 
						||
                break
 | 
						||
            fi
 | 
						||
            if [ -n "$__ad_array" ]; then
 | 
						||
                if [ "${__ad_value:0:2}" == $'<\n' ]; then
 | 
						||
                    local -a __ad_values
 | 
						||
                    array_from_lines __ad_values "${__ad_value:2}"
 | 
						||
                    __ad_value=__ad_values
 | 
						||
                fi
 | 
						||
                __ad_value="${__ad_value}[@]"
 | 
						||
                local __ad_i=1
 | 
						||
                echo "$__ad_name[0] = 0; delete $__ad_name"
 | 
						||
                for __ad_arg in "${!__ad_value}"; do
 | 
						||
                    echo "$__ad_name[$__ad_i]=$(qawk "$__ad_arg")"
 | 
						||
                    __ad_i=$(($__ad_i + 1))
 | 
						||
                done
 | 
						||
                eval "echo \"\${__ad_name}_count = \${#$__ad_value}\""
 | 
						||
            fi
 | 
						||
            shift
 | 
						||
        done
 | 
						||
        echo "}"
 | 
						||
        for __ad_arg in "$@"; do
 | 
						||
            recho "$__ad_arg"
 | 
						||
        done
 | 
						||
    fi
 | 
						||
 | 
						||
    [ -n "$__ULIB_AWKDEF_SET_X" ] && set -x; return 0
 | 
						||
}
 | 
						||
function lawkrun() {
 | 
						||
# wrapper pour lancer awk avec un script préparé par awkdef. Les définitions et
 | 
						||
# les arguments sont séparés par --, e.g.
 | 
						||
#     awkrun var0=value0 var1=value1 script -- input0 input1
 | 
						||
    local -a __ar_defs __ar_args
 | 
						||
    while [ $# -gt 0 -a "$1" != "--" ]; do
 | 
						||
        __ar_defs=("${__ar_defs[@]}" "$1")
 | 
						||
        shift
 | 
						||
    done
 | 
						||
    shift
 | 
						||
    while [ $# -gt 0 ]; do
 | 
						||
        __ar_args=("${__ar_args[@]}" "$1")
 | 
						||
        shift
 | 
						||
    done
 | 
						||
    local __ar_script="$(awkdef "${__ar_defs[@]}")"
 | 
						||
    [ -n "$UTOOLS_AWKRUN_DEBUG" ] && estep "Script awkrun: $__ar_script"
 | 
						||
    #pgawk -p "$__ar_script" "${__ar_args[@]}"
 | 
						||
    awk "$__ar_script" "${__ar_args[@]}"
 | 
						||
}
 | 
						||
function cawkrun() { LANG=C lawkrun "$@"; }
 | 
						||
function awkrun() { LANG=C lawkrun "$@"; }
 | 
						||
 | 
						||
function __lf_get_age() {
 | 
						||
    local y=$(date "+%Y")
 | 
						||
    local dy=$(date "+%j"); while [ "${dy#0}" != "$dy" ]; do dy="${dy#0}"; done
 | 
						||
    [ -n "$dy" ] || dy=0
 | 
						||
    local h=$(date "+%H"); while [ "${h#0}" != "$h" ]; do h="${h#0}"; done
 | 
						||
    [ -n "$h" ] || h=0
 | 
						||
    echo $((($y * 365 + $dy) * 24 + $h))
 | 
						||
}
 | 
						||
function lf_trylock() {
 | 
						||
# USAGE
 | 
						||
#     lf_trylock [-h max_hours] /path/to/lockfile
 | 
						||
# OPTIONS
 | 
						||
#     lockfile
 | 
						||
#         fichier qui doit contenir le verrou
 | 
						||
#     -h max_hours
 | 
						||
#         Nombre d'heures (par défaut 4) au bout duquel afficher stale
 | 
						||
#         Sinon, afficher locked
 | 
						||
# Retourne 0 si le verrou a été placé correctement. Il ne faut pas oublier de
 | 
						||
# supprimer le fichier. Le mieux est de le faire supprimer automatiquement par
 | 
						||
# autoclean:
 | 
						||
#     lockfile=...
 | 
						||
#     case "$(lf_trylock "$lockfile")" in
 | 
						||
#     locked) ...;;
 | 
						||
#     stale) ...;;
 | 
						||
#     esac
 | 
						||
#     autoclean "$lockfile"
 | 
						||
# Sinon, retourner 1 et afficher l'une des deux valeurs suivantes:
 | 
						||
# - stale si le verrou a déjà été placé, depuis au moins max_hours heures
 | 
						||
# - locked si le verrou a déjà été placé
 | 
						||
# - retry si une erreur s'est produite pendant la pose du verrou ou sa
 | 
						||
#   lecture. Cela peut se produire si les droits ne sont pas suffisants pour
 | 
						||
#   écrire dans le répertoire destination, ou si le fichier a été supprimé
 | 
						||
#   avant sa lecture (race-condition). Dans ce dernier cas, reessayer permettra
 | 
						||
#   d'acquérir le verrou
 | 
						||
    local eoo lockfile max_hours=4
 | 
						||
    while [ -n "$1" ]; do
 | 
						||
        case "$1" in
 | 
						||
        -h) shift; max_hours="$1";;
 | 
						||
        --) shift; eoo=1;;
 | 
						||
        *) eoo=1;;
 | 
						||
        esac
 | 
						||
        [ -n "$eoo" ] && break
 | 
						||
        shift
 | 
						||
    done
 | 
						||
 | 
						||
    lockfile="$1"
 | 
						||
    [ -n "$lockfile" ] || die "il faut spécifier un fichier pour le verrou"
 | 
						||
 | 
						||
    local now="$(__lf_get_age)"
 | 
						||
    if (set -C; echo "$now" >"$lockfile") 2>/dev/null; then
 | 
						||
        return 0
 | 
						||
    fi
 | 
						||
    local prev diff
 | 
						||
    if prev="$(<"$lockfile")"; then
 | 
						||
        diff="$(($now - $prev))"
 | 
						||
        if [ "$diff" -gt "$max_hours" ]; then
 | 
						||
            echo stale
 | 
						||
        else
 | 
						||
            echo locked
 | 
						||
        fi
 | 
						||
    elif [ -f "$lockfile" ]; then
 | 
						||
        echo retry
 | 
						||
    fi
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function pidfile_set() {
 | 
						||
# USAGE
 | 
						||
#     pidfile_set [-p pid] /path/to/pidfile
 | 
						||
# OPTIONS
 | 
						||
#     pidfile
 | 
						||
#         fichier qui doit contenir le pid du script
 | 
						||
#     -p pid
 | 
						||
#         spécifier le pid. par défaut, utiliser $$
 | 
						||
#     -r  si pidfile existe mais que le processus ne tourne plus, faire
 | 
						||
#         comme si le fichier n'existe pas.
 | 
						||
# Retourner 0 si le pid a été correctement écrit dans le fichier. Ce fichier
 | 
						||
# sera supprimé automatiquement en fin de script
 | 
						||
# Retourner 1 si le fichier existe déjà et que le processus est en train de
 | 
						||
# tourner.
 | 
						||
# Retourner 2 si le fichier existe déjà mais que le processus ne tourne plus.
 | 
						||
# Retourner 10 si autre erreur grave s'est produite (par exemple, s'il manque le
 | 
						||
# chemin vers pidfile, ou si le fichier n'est pas accessible en écriture.)
 | 
						||
    local eoo pidfile pid=$$ replace=
 | 
						||
    while [ -n "$1" ]; do
 | 
						||
        case "$1" in
 | 
						||
        -p)
 | 
						||
            shift
 | 
						||
            pid="$1"
 | 
						||
            ;;
 | 
						||
        -r)
 | 
						||
            replace=1
 | 
						||
            ;;
 | 
						||
        --)
 | 
						||
            shift
 | 
						||
            eoo=1
 | 
						||
            ;;
 | 
						||
        *)
 | 
						||
            eoo=1
 | 
						||
            ;;
 | 
						||
        esac
 | 
						||
        [ -n "$eoo" ] && break
 | 
						||
        shift
 | 
						||
    done
 | 
						||
 | 
						||
    pidfile="$1"
 | 
						||
    [ -n "$pidfile" ] || return 10
 | 
						||
 | 
						||
    if [ -f "$pidfile" ]; then
 | 
						||
        local curpid="$(<"$pidfile")"
 | 
						||
        if is_running "$curpid"; then
 | 
						||
            return 1
 | 
						||
        elif [ -n "$replace" ]; then
 | 
						||
            /bin/rm -f "$pidfile" || return 10
 | 
						||
        else
 | 
						||
            return 2
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
 | 
						||
    echo_ "$pid" >"$pidfile" || return 10
 | 
						||
    autoclean "$pidfile"
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function pidfile_check() {
 | 
						||
# USAGE
 | 
						||
#     pidfile_check /path/to/pidfile
 | 
						||
# OPTIONS
 | 
						||
#     pidfile
 | 
						||
#         fichier qui doit contenir le pid d'un processus
 | 
						||
# Cette fonction permet de vérifier si le processus associé à un fichier de pid
 | 
						||
# est en train de tourner.
 | 
						||
# Retourner 0 si le fichier de pid existe et que le process du pid spécifié est
 | 
						||
# en train de tourner. Retourner 1 sinon.
 | 
						||
# Retourner 10 si erreur grave s'est produite (par exemple, s'il manque le
 | 
						||
# chemin vers pidfile, ou si le fichier n'est pas accessible en lecture.)
 | 
						||
    local pidfile="$1"
 | 
						||
    [ -n "$pidfile" ] || return 10
 | 
						||
 | 
						||
    if [ -f "$pidfile" ]; then
 | 
						||
        [ -r "$pidfile" ] || return 10
 | 
						||
        local pid="$(<"$pidfile")"
 | 
						||
        is_running "$pid" && return 0
 | 
						||
    fi
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function page_maybe() {
 | 
						||
# Utiliser less, si possible, pour afficher le flux en entrée. Si le terminal
 | 
						||
# n'est pas interactif ou si le nombre de lignes en entrée est inférieur au
 | 
						||
# nombre de lignes du terminal, afficher simplement le flux.
 | 
						||
# Les arguments de cette fonction sont passés à less
 | 
						||
    if isatty; then
 | 
						||
        less -XFR "$@"
 | 
						||
    else
 | 
						||
        cat
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## entrée/sortie
 | 
						||
 | 
						||
function utools_local() {
 | 
						||
# Afficher les commandes pour rendre locales certaines variables en fonction des
 | 
						||
# arguments:
 | 
						||
# - opts rend locale args, pour utiliser parse_opts() à l'intérieur d'une
 | 
						||
# fonction.
 | 
						||
# - verbosity et interaction rendent respectivement locales __verbosity et
 | 
						||
#   __interaction. Ceci est utile pour pouvoir appeler sans risque de pollution
 | 
						||
#   de l'environnement une fonction qui utilise parse_opts() avec les
 | 
						||
#   définitions de PRETTYOPTS.
 | 
						||
# Si aucun arguments n'est fourni, toutes les définitions sont affichées.
 | 
						||
    local arg
 | 
						||
    [ $# -gt 0 ] || set -- opts verbosity interaction
 | 
						||
    for arg in "$@"; do
 | 
						||
        case "$arg" in
 | 
						||
        parse_opts|opts|o|args) echo "local -a args";;
 | 
						||
        verbosity|v) echo "local __verbosity='$__verbosity'";;
 | 
						||
        interaction|i) echo "local __interaction='$__interaction'";;
 | 
						||
        esac
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function stdredir() {
 | 
						||
    # Lancer la commande $4..@ en redirigeant stdin depuis $1, stdout vers $2,
 | 
						||
    # stderr vers $3. Si $1 est vide ou vaut /dev/stdin, la redirection n'est
 | 
						||
    # pas faite. Si $2 est vide ou vaut /dev/stdout, la redirection n'est pas
 | 
						||
    # faite. Si $3 est vide ou vaut /dev/stderr, la redirection n'est pas faite.
 | 
						||
    # Cette fonction existe parce que sur certaines versions de bash, il semble
 | 
						||
    # que les redirections /dev/std* ne sont pas traitées de façon particulière.
 | 
						||
    # De plus, sur des technologies telles que OpenVZ, les chemins /dev/std* ne
 | 
						||
    # sont pas créés (parce que /proc/self/fd/* n'est pas accessible). Donc,
 | 
						||
    # dans de rares cas où le script tourne sur OpenVZ avec une version de bash
 | 
						||
    # qui est buggée, la redirection n'est pas faite correctement.
 | 
						||
    local __redirs __in __out __err
 | 
						||
    if [ -n "$1" -o "$1" == /dev/stdin ]; then
 | 
						||
        if [ "${1#<}" != "$1" ]; then
 | 
						||
            __in="${1#<}"
 | 
						||
        else
 | 
						||
            __in="$1"
 | 
						||
        fi
 | 
						||
        __redirs="$__redirs"' <"$__in"'
 | 
						||
    fi; shift
 | 
						||
    if [ -n "$1" -o "$1" == /dev/stdout ]; then
 | 
						||
        if [ "${1#>>}" != "$1" ]; then
 | 
						||
            __out="${1#>>}"
 | 
						||
            __redirs="$__redirs"' >>"$__out"'
 | 
						||
        elif [ "${1#>}" != "$1" ]; then
 | 
						||
            __out="${1#>}"
 | 
						||
            __redirs="$__redirs"' >"$__out"'
 | 
						||
        else
 | 
						||
            __out="$1"
 | 
						||
            __redirs="$__redirs"' >"$__out"'
 | 
						||
        fi
 | 
						||
    fi; shift
 | 
						||
    if [ -n "$1" -o "$1" == /dev/stderr ]; then
 | 
						||
        if [ "${1#>>}" != "$1" ]; then
 | 
						||
            __err="${1#>>}"
 | 
						||
            __redirs="$__redirs"' 2>>"$__err"'
 | 
						||
        elif [ "${1#>}" != "$1" ]; then
 | 
						||
            __err="${1#>}"
 | 
						||
            __redirs="$__redirs"' 2>"$__err"'
 | 
						||
        else
 | 
						||
            __err="$1"
 | 
						||
            __redirs="$__redirs"' 2>"$__err"'
 | 
						||
        fi
 | 
						||
    fi; shift
 | 
						||
    eval '"$@"'"$__redirs"
 | 
						||
}
 | 
						||
 | 
						||
function isatty() {
 | 
						||
# tester si STDOUT n'est pas une redirection
 | 
						||
    tty -s <&1
 | 
						||
}
 | 
						||
function in_isatty() {
 | 
						||
# tester si STDIN n'est pas une redirection
 | 
						||
    tty -s
 | 
						||
}
 | 
						||
function out_isatty() {
 | 
						||
# tester si STDOUT n'est pas une redirection
 | 
						||
    tty -s <&1
 | 
						||
}
 | 
						||
function err_isatty() {
 | 
						||
# tester si STDERR n'est pas une redirection
 | 
						||
    tty -s <&2
 | 
						||
}
 | 
						||
function die() { [ $# -gt 0 ] && eerror "$@"; exit 1; }
 | 
						||
function exit_with { if [ $# -gt 0 ]; then "$@"; fi; exit $?; }
 | 
						||
function die_with { [ $# -gt 0 ] && eerror "$1"; shift; [ $# -gt 0 ] && "$@"; exit 1; }
 | 
						||
function die_unless() {
 | 
						||
# Afficher $-1 et quitter le script avec die() si la commande $1..-2 retourne
 | 
						||
# FAUX
 | 
						||
    local count=$#
 | 
						||
    if [ $count -eq 0 ]; then
 | 
						||
        exit 1
 | 
						||
    elif [ $count -eq 1 ]; then
 | 
						||
        "$@" || exit $?
 | 
						||
    else
 | 
						||
        local m r
 | 
						||
        m="${@:$count}"
 | 
						||
        count=$(($count - 1))
 | 
						||
        set -- "${@:1:$count}"
 | 
						||
        if "$@"; then
 | 
						||
            :
 | 
						||
        else
 | 
						||
            r=$?
 | 
						||
            eerror "$m"
 | 
						||
            exit $r
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
function eerror_unless() {
 | 
						||
# Afficher $-1 avec eerror() et retourner $? si la commande $1..-2 retourne FAUX
 | 
						||
    local count=$#
 | 
						||
    if [ $count -eq 0 ]; then
 | 
						||
        return 1
 | 
						||
    elif [ $count -eq 1 ]; then
 | 
						||
        "$@" || return $?
 | 
						||
    else
 | 
						||
        local m r
 | 
						||
        m="${@:$count}"
 | 
						||
        count=$(($count - 1))
 | 
						||
        set -- "${@:1:$count}"
 | 
						||
        if "$@"; then
 | 
						||
            :
 | 
						||
        else
 | 
						||
            r=$?
 | 
						||
            eerror "$m"
 | 
						||
            return $r
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
function die_if() {
 | 
						||
# Afficher $-1 et quitter le script avec die() si la commande $1..-2 retourne
 | 
						||
# VRAI
 | 
						||
    local count=$#
 | 
						||
    if [ $count -eq 0 ]; then
 | 
						||
        :
 | 
						||
    elif [ $count -eq 1 ]; then
 | 
						||
        "$@" && exit 1
 | 
						||
    else
 | 
						||
        local m r
 | 
						||
        m="${@:$count}"
 | 
						||
        count=$(($count - 1))
 | 
						||
        set -- "${@:1:$count}"
 | 
						||
        if "$@"; then
 | 
						||
            eerror "$m"
 | 
						||
            exit 1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
function eerror_if() {
 | 
						||
# Afficher $-1 avec eerror() et retourner le code d'erreur 1 si la commande
 | 
						||
# $1..-2 retourne VRAI
 | 
						||
    local count=$#
 | 
						||
    if [ $count -eq 0 ]; then
 | 
						||
        :
 | 
						||
    elif [ $count -eq 1 ]; then
 | 
						||
        "$@" && return 1
 | 
						||
    else
 | 
						||
        local m r
 | 
						||
        m="${@:$count}"
 | 
						||
        count=$(($count - 1))
 | 
						||
        set -- "${@:1:$count}"
 | 
						||
        if "$@"; then
 | 
						||
            eerror "$m"
 | 
						||
            return 1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
function noerror() {
 | 
						||
# lancer la commande "$@" et masquer son code de retour
 | 
						||
    [ $# -gt 0 ] || set :
 | 
						||
    "$@" || return 0
 | 
						||
}
 | 
						||
function noout() {
 | 
						||
# lancer la commande "$@" en supprimant sa sortie standard
 | 
						||
    [ $# -gt 0 ] || return 0
 | 
						||
    "$@" >/dev/null
 | 
						||
}
 | 
						||
function noerr() {
 | 
						||
# lancer la commande "$@" en supprimant sa sortie d'erreur
 | 
						||
    [ $# -gt 0 ] || return 0
 | 
						||
    "$@" 2>/dev/null
 | 
						||
}
 | 
						||
 | 
						||
TAB=$'\t'
 | 
						||
LATIN1=iso-8859-1
 | 
						||
LATIN9=iso-8859-15
 | 
						||
UTF8=utf-8
 | 
						||
OENC="$UTF8"
 | 
						||
 | 
						||
if ! progexists iconv; then
 | 
						||
    function iconv() { cat; }
 | 
						||
fi
 | 
						||
 | 
						||
function __lang_encoding() {
 | 
						||
    local lang="$(<<<"$LANG" awk '{ print tolower($0) }')"
 | 
						||
    case "$lang" in
 | 
						||
    *@euro) echo "iso-8859-15";;
 | 
						||
    *.utf-8|*.utf8) echo "utf-8";;
 | 
						||
    *) echo "iso-8859-1";;
 | 
						||
    esac
 | 
						||
}
 | 
						||
function __norm_encoding() {
 | 
						||
    awk '{
 | 
						||
        enc = tolower($0)
 | 
						||
        gsub(/^latin$/, "latin1", enc)
 | 
						||
        gsub(/^latin1$/, "iso-8859-1", enc)
 | 
						||
        gsub(/^latin9$/, "iso-8859-15", enc)
 | 
						||
        gsub(/[-_]/, "", enc)
 | 
						||
        if (enc == "iso8859" || enc == "iso88591" || enc == "8859" || enc == "88591") print "iso-8859-1"
 | 
						||
        else if (enc == "iso885915"  || enc == "885915") print "iso-8859-15"
 | 
						||
        else if (enc == "utf" || enc == "utf8") print "utf-8"
 | 
						||
        else print $0
 | 
						||
    }' <<<"$1"
 | 
						||
}
 | 
						||
function __init_encoding() {
 | 
						||
    local DEFAULT_ENCODING="$(__lang_encoding)"
 | 
						||
    [ -n "$DEFAULT_ENCODING" ] || DEFAULT_ENCODING=utf-8
 | 
						||
    [ -n "$UTOOLS_OUTPUT_ENCODING" ] || UTOOLS_OUTPUT_ENCODING="$DEFAULT_ENCODING"
 | 
						||
    UTOOLS_OUTPUT_ENCODING="$(__norm_encoding "$UTOOLS_OUTPUT_ENCODING")"
 | 
						||
    [ -n "$UTOOLS_INPUT_ENCODING" ] || UTOOLS_INPUT_ENCODING="$UTOOLS_OUTPUT_ENCODING"
 | 
						||
    UTOOLS_INPUT_ENCODING="$(__norm_encoding "$UTOOLS_INPUT_ENCODING")"
 | 
						||
    [ -n "$UTOOLS_EDITOR_ENCODING" ] || UTOOLS_EDITOR_ENCODING="$UTOOLS_INPUT_ENCODING"
 | 
						||
    UTOOLS_EDITOR_ENCODING="$(__norm_encoding "$UTOOLS_EDITOR_ENCODING")"
 | 
						||
 | 
						||
    IENC="$UTOOLS_INPUT_ENCODING"
 | 
						||
    OENC="$UTOOLS_OUTPUT_ENCODING"
 | 
						||
}
 | 
						||
 | 
						||
if [ -n "$UTOOLS_LANG" -a -z "$LANG" ]; then
 | 
						||
    export UTOOLS_LANG
 | 
						||
    export LANG="$UTOOLS_LANG"
 | 
						||
fi
 | 
						||
__init_encoding
 | 
						||
 | 
						||
function tooenc() {
 | 
						||
# Transformer la valeur $1 de l'encoding $2(=$OENC) vers l'encoding de sortie
 | 
						||
# $3=($UTOOLS_OUTPUT_ENCODING)
 | 
						||
    local src="$1" from="${2:-$OENC}" to="${3:-$UTOOLS_OUTPUT_ENCODING}"
 | 
						||
    if [ "$from" == "$to" ]; then
 | 
						||
        recho "$src"
 | 
						||
    else
 | 
						||
        iconv -f "$from" -t "$to" <<<"$src"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function uecho() {
 | 
						||
    tooenc "$*"
 | 
						||
}
 | 
						||
function tooenc_() {
 | 
						||
# Transformer la valeur $1 de l'encoding $2(=$OENC) vers l'encoding de sortie
 | 
						||
# $3=($UTOOLS_OUTPUT_ENCODING)
 | 
						||
    local src="$1" from="${2:-$OENC}" to="${3:-$UTOOLS_OUTPUT_ENCODING}"
 | 
						||
    if [ "$from" == "$to" ]; then
 | 
						||
        recho_ "$src"
 | 
						||
    else
 | 
						||
        recho_ "$src" | iconv -f "$from" -t "$to"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function uecho_() {
 | 
						||
    tooenc_ "$*"
 | 
						||
}
 | 
						||
function toienc() {
 | 
						||
# Transformer la valeur $1 de $2(=$IENC) vers l'encoding d'entrée
 | 
						||
# $3(=$UTOOLS_INPUT_ENCODING)
 | 
						||
    local __tie_var="$1" __tie_to="${2:-$IENC}" __tie_from="${3:-$UTOOLS_INPUT_ENCODING}"
 | 
						||
    if [ "$__tie_from" != "$__tie_to" ]; then
 | 
						||
        _setv "$__tie_var" "$(iconv -f "$__tie_from" -t "$__tie_to" <<<"${!__tie_var}")"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function uread() {
 | 
						||
# Lire une valeur sur stdin et la placer dans la variable $1. On assume que la
 | 
						||
# valeur en entrée est encodée dans l'encoding d'entrée par défaut
 | 
						||
    [ $# -gt 0 ] || set -- REPLY
 | 
						||
    local __r_var
 | 
						||
    read "$@"
 | 
						||
    for __r_var in "$@"; do
 | 
						||
        [ -z "$__r_var" -o "${__r_var:0:1}" == "-" ] && continue # ignorer les options
 | 
						||
        toienc "$__r_var"
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function stooenc() {
 | 
						||
# Transformer la valeur lue sur stdin de $OENC vers l'encoding de sortie par
 | 
						||
# défaut ($UTOOLS_OUTPUT_ENCODING)
 | 
						||
    local from="${1:-$OENC}" to="${2:-$UTOOLS_OUTPUT_ENCODING}"
 | 
						||
    if [ "$from" == "$to" ]; then
 | 
						||
        cat
 | 
						||
    else
 | 
						||
        iconv -f "$from" -t "$to"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function stoienc() {
 | 
						||
# Transformer la valeur lue sur stdin de $IENC vers l'encoding d'entrée par
 | 
						||
# défaut ($UTOOLS_INPUT_ENCODING)
 | 
						||
    local to="${1:-$IENC}" from="${2:-$UTOOLS_INPUT_ENCODING}"
 | 
						||
    if [ "$from" == "$to" ]; then
 | 
						||
        cat
 | 
						||
    else
 | 
						||
        iconv -f "$from" -t "$to"
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
# faut-il dater les messages de etitle, estep, ebegin?
 | 
						||
# Faire UTOOLS_EDATE=1 en début de script pour activer cette fonctionnalité
 | 
						||
export UTOOLS_EDATE
 | 
						||
function __edate() { [ -n "$UTOOLS_EDATE" ] && date +"[%d/%m/%Y-%H:%M:%S] "; }
 | 
						||
 | 
						||
export UTOOLS_ELOG_OVERWRITE
 | 
						||
function __set_no_colors() { :; }
 | 
						||
function elogto() {
 | 
						||
# Activer UTOOLS_EDATE et rediriger STDOUT et STDERR vers le fichier $1
 | 
						||
# Si deux fichiers sont spécifiés, rediriger STDOUT vers $1 et STDERR vers $2
 | 
						||
# Si aucun fichier n'est spécifié, ne pas faire de redirection
 | 
						||
# Si la redirection est activée, forcer l'utilisation de l'encoding UTF8
 | 
						||
# Si UTOOLS_ELOG_OVERWRITE=1, alors le fichier en sortie est écrasé. Sinon, les
 | 
						||
# lignes en sortie lui sont ajoutées
 | 
						||
    UTOOLS_EDATE=1
 | 
						||
    if [ -n "$1" -a -n "$2" ]; then
 | 
						||
        LANG=fr_FR.UTF8
 | 
						||
        UTOOLS_OUTPUT_ENCODING="$UTF8"
 | 
						||
        __set_no_colors 1
 | 
						||
        if [ -n "$UTOOLS_ELOG_OVERWRITE" ]; then
 | 
						||
            exec >"$1" 2>"$2"
 | 
						||
        else
 | 
						||
            exec >>"$1" 2>>"$2"
 | 
						||
        fi
 | 
						||
    elif [ -n "$1" ]; then
 | 
						||
        LANG=fr_FR.UTF8
 | 
						||
        UTOOLS_OUTPUT_ENCODING="$UTF8"
 | 
						||
        __set_no_colors 1
 | 
						||
        if [ -n "$UTOOLS_ELOG_OVERWRITE" ]; then
 | 
						||
            exec >"$1" 2>&1
 | 
						||
        else
 | 
						||
            exec >>"$1" 2>&1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
# variables utilisées pour l'affichage indenté des messages et des titres
 | 
						||
# __estack est la liste des invocations de 'ebegin' et 'etitle' en cours
 | 
						||
# __tlevel est l'indentation à appliquer avant d'afficher le message
 | 
						||
export __estack __tlevel
 | 
						||
function __indent() {
 | 
						||
# indenter les lignes de $1, sauf la première
 | 
						||
    if [ "${1/
 | 
						||
/}" != "$1" ]; then
 | 
						||
        sed "2,\$s/^/${__tlevel}/g" <<<"$1"
 | 
						||
    else
 | 
						||
        recho "$1"
 | 
						||
    fi
 | 
						||
}
 | 
						||
# fonctions à surcharger pour modifier la façon dont les messages sont affichés
 | 
						||
function __eerror() { tooenc "$(__edate)${__tlevel}ERROR $(__indent "$1")"; }
 | 
						||
function __ewarn() { tooenc "$(__edate)${__tlevel}WARNING $(__indent "$1")"; }
 | 
						||
function __enote() { tooenc "$(__edate)${__tlevel}NOTE $(__indent "$1")"; }
 | 
						||
function __ebanner() {
 | 
						||
    local maxi="${COLUMNS:-80}"
 | 
						||
    local -a lines
 | 
						||
    local psfix line
 | 
						||
 | 
						||
    psfix="$(__edate)${__tlevel}"
 | 
						||
    while [ ${#psfix} -lt $maxi ]; do psfix="$psfix="; done
 | 
						||
 | 
						||
    tooenc "$psfix"
 | 
						||
    maxi=$(($maxi - 1))
 | 
						||
    array_from_xlines lines "$1"
 | 
						||
    for line in "" "${lines[@]}" ""; do
 | 
						||
        line="$(__edate)${__tlevel}= $line"
 | 
						||
        if [ ${#line} -le $maxi ]; then
 | 
						||
            while [ ${#line} -lt $maxi ]; do line="$line "; done
 | 
						||
            line="$line="
 | 
						||
        fi
 | 
						||
        tooenc "$line"
 | 
						||
    done
 | 
						||
    tooenc "$psfix"
 | 
						||
}
 | 
						||
function __eimportant() { tooenc "$(__edate)${__tlevel}IMPORTANT $(__indent "$1")"; }
 | 
						||
function __eattention() { tooenc "$(__edate)${__tlevel}ATTENTION $(__indent "$1")"; }
 | 
						||
function __einfo() { tooenc "$(__edate)${__tlevel}INFO $(__indent "$1")"; }
 | 
						||
function __eecho() { tooenc "$(__edate)${__tlevel}$(__indent "$1")"; }
 | 
						||
function __eecho_() { tooenc_ "$(__edate)${__tlevel}$(__indent "$1")"; }
 | 
						||
function __edebug() { tooenc "$(__edate)${__tlevel}DEBUG $(__indent "$1")"; }
 | 
						||
function __estep() { tooenc "$(__edate)${__tlevel}. $(__indent "$1")"; }
 | 
						||
function __estepe() { tooenc "$(__edate)${__tlevel}.E $(__indent "$1")"; }
 | 
						||
function __estepw() { tooenc "$(__edate)${__tlevel}.W $(__indent "$1")"; }
 | 
						||
function __estepn() { tooenc "$(__edate)${__tlevel}.N $(__indent "$1")"; }
 | 
						||
function __estepi() { tooenc "$(__edate)${__tlevel}.I $(__indent "$1")"; }
 | 
						||
function __estep_() { tooenc_ "$(__edate)${__tlevel}. $(__indent "$1")"; }
 | 
						||
function __estepe_() { tooenc_ "$(__edate)${__tlevel}.E $(__indent "$1")"; }
 | 
						||
function __estepw_() { tooenc_ "$(__edate)${__tlevel}.W $(__indent "$1")"; }
 | 
						||
function __estepn_() { tooenc_ "$(__edate)${__tlevel}.N $(__indent "$1")"; }
 | 
						||
function __estepi_() { tooenc_ "$(__edate)${__tlevel}.I $(__indent "$1")"; }
 | 
						||
function __etitle() { tooenc "$(__edate)${__tlevel}=== $(__indent "$1")"; }
 | 
						||
function __ebegin() { tooenc_ "$(__edate)${__tlevel}. $(__indent "$1"): "; }
 | 
						||
function __edoto() { echo_ "."; }
 | 
						||
function __edotw() { echo_ "w"; }
 | 
						||
function __edotx() { echo_ "x"; }
 | 
						||
function __edotp() { echo_ "+"; }
 | 
						||
function __edotd() { tooenc "($1)"; }
 | 
						||
function __eendo() { echo "[ok]"; }
 | 
						||
function __eendx() { echo "[error]"; }
 | 
						||
PRETTYOPTS=()
 | 
						||
function set_verbosity() { :;}
 | 
						||
function set_interaction() { :;}
 | 
						||
function show_error() {
 | 
						||
# tester respectivement si on doit afficher les messages d'erreur,
 | 
						||
# d'avertissement, d'information, de debug
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function show_warn() {
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function show_info() {
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function show_verbose() {
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function show_debug() {
 | 
						||
    [ -n "$DEBUG" ]
 | 
						||
}
 | 
						||
function check_verbosity() {
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function get_verbosity_option() { :;}
 | 
						||
function check_interaction() {
 | 
						||
    return 0
 | 
						||
}
 | 
						||
function is_interaction() {
 | 
						||
    return 1
 | 
						||
}
 | 
						||
function get_interaction_option() { :;}
 | 
						||
# note: toutes les fonctions d'affichage e* écrivent sur stderr
 | 
						||
__epending=
 | 
						||
function eflush() {
 | 
						||
# Afficher les messages en attente
 | 
						||
    if [ -n "$__epending" ]; then recho "$__epending" 1>&2; __epending=; fi
 | 
						||
}
 | 
						||
function eclearp() {
 | 
						||
# Supprimer les message en attente
 | 
						||
    __epending=
 | 
						||
}
 | 
						||
function eerror() {
 | 
						||
# Afficher un message d'erreur
 | 
						||
    show_error || return; eflush; __eerror "$*" 1>&2
 | 
						||
}
 | 
						||
function ewarn() {
 | 
						||
# Afficher un message d'avertissement
 | 
						||
    show_warn || return; eflush; __ewarn "$*" 1>&2
 | 
						||
}
 | 
						||
function enote() {
 | 
						||
# Afficher un message d'information de même niveau qu'un avertissement
 | 
						||
    show_info || return; eflush; __enote "$*" 1>&2
 | 
						||
}
 | 
						||
function ebanner() {
 | 
						||
# Afficher un message très important encadré, puis attendre 5 secondes
 | 
						||
    show_error || return; eflush; __ebanner "$*" 1>&2; sleep 5
 | 
						||
}
 | 
						||
function eimportant() {
 | 
						||
# Afficher un message très important
 | 
						||
    show_error || return; eflush; __eimportant "$*" 1>&2
 | 
						||
}
 | 
						||
function eattention() {
 | 
						||
# Afficher un message important
 | 
						||
    show_warn || return; eflush; __eattention "$*" 1>&2
 | 
						||
}
 | 
						||
function einfo() {
 | 
						||
# Afficher un message d'information
 | 
						||
    show_info || return; eflush; __einfo "$*" 1>&2
 | 
						||
}
 | 
						||
function eecho() {
 | 
						||
# Afficher un message d'information sans préfixe
 | 
						||
    show_info || return; eflush; __eecho "$*" 1>&2
 | 
						||
}
 | 
						||
function eecho_() {
 | 
						||
    show_info || return; eflush; __eecho_ "$*" 1>&2
 | 
						||
}
 | 
						||
function edebug() {
 | 
						||
# Afficher un message de debug
 | 
						||
    show_debug || return; eflush; __edebug "$*" 1>&2
 | 
						||
}
 | 
						||
function trace() {
 | 
						||
# Afficher la commande $1..@, la lancer, puis afficher son code d'erreur si une
 | 
						||
# erreur se produit
 | 
						||
    local r cmd="$(qvals "$@")"
 | 
						||
    show_info && { eflush; __eecho "\$ $cmd" 1>&2; }
 | 
						||
    "$@"; r=$?
 | 
						||
    if [ $r -ne 0 ]; then
 | 
						||
        if show_info; then
 | 
						||
            eflush; __eecho "^ [EC #$r]" 1>&2
 | 
						||
        elif show_error; then
 | 
						||
            eflush; __eecho "^ $cmd [EC #$r]" 1>&2;
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    return $r
 | 
						||
}
 | 
						||
function trace_error() {
 | 
						||
# Lancer la commande $1..@, puis afficher son code d'erreur si une erreur se
 | 
						||
# produit. La différence avec trace() est que la commande n'est affichée que si
 | 
						||
# une erreur se produit.
 | 
						||
    local r
 | 
						||
    "$@"; r=$?
 | 
						||
    if [ $r -ne 0 ]; then
 | 
						||
        local cmd="$(qvals "$@")"
 | 
						||
        show_error && { eflush; __eecho "^ $cmd [EC #$r]" 1>&2; }
 | 
						||
    fi
 | 
						||
    return $r
 | 
						||
}
 | 
						||
 | 
						||
function etitle() {
 | 
						||
# Afficher le titre $1, qui est le début éventuel d'une section. Les section
 | 
						||
# imbriquées sont affichées indentées. La section n'est pas terminée, et il faut
 | 
						||
# la terminer explicitement avec eend, sauf dans certains cas précis:
 | 
						||
# - Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le
 | 
						||
# contexte de la section. Puis, la section est automatiquement terminée sauf si
 | 
						||
# l'option -s est spécifiée, auquel cas la section reste ouverte. Si l'option -p
 | 
						||
# est spécifiée, eclearp() est appelé pour purger les messages en attente
 | 
						||
# - Dans le cas contraire, l'option -s est ignorée: la section doit toujours
 | 
						||
# être terminée explicitement.
 | 
						||
# La fonction etitled() est comme etitle(), mais le titre n'est pas affiché
 | 
						||
# immédiatement. L'affichage effectif est effectué dès qu'une fonction e* est
 | 
						||
# utilisée. Ceci permet, avec la fonction eclearp(), de ne pas afficher de titre
 | 
						||
# pour une section vide.
 | 
						||
    local __t_deferred=
 | 
						||
    __t_etitle "$@"
 | 
						||
}
 | 
						||
function etitled() {
 | 
						||
    local __t_deferred=1
 | 
						||
    __t_etitle "$@"
 | 
						||
}
 | 
						||
function __t_etitle() {
 | 
						||
    local __t_eend=default
 | 
						||
    local __t_clearp=
 | 
						||
    while [ -n "$1" ]; do
 | 
						||
        if [ "$1" == "--" ]; then
 | 
						||
            shift
 | 
						||
            break
 | 
						||
        elif [ "$1" == "-s" ]; then
 | 
						||
            __t_eend=
 | 
						||
            shift
 | 
						||
        elif [ "$1" == "--eend" ]; then
 | 
						||
            __t_eend=1
 | 
						||
            shift
 | 
						||
        elif [ "$1" == "-p" ]; then
 | 
						||
            __t_clearp=1
 | 
						||
            shift
 | 
						||
        else
 | 
						||
            break
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    local __t_title="$1"; shift
 | 
						||
    local __t_s=0
 | 
						||
    # etitle
 | 
						||
    [ -n "$__estack" ] && __tlevel="${__tlevel}  "
 | 
						||
    __estack="$__estack:t"
 | 
						||
    if show_info; then
 | 
						||
        if [ -n "$__t_deferred" ]; then
 | 
						||
            __epending="${__epending:+$__epending
 | 
						||
}$(__etitle "$__t_title")"
 | 
						||
        else
 | 
						||
            eflush
 | 
						||
            __etitle "$__t_title" 1>&2
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    # commande
 | 
						||
    if [ $# -gt 0 ]; then
 | 
						||
        "$@"
 | 
						||
        __t_s=$?
 | 
						||
        [ "$__t_eend" == "default" ] && __t_eend=1
 | 
						||
    fi
 | 
						||
    # eend
 | 
						||
    [ "$__t_eend" == "default" ] && __t_eend=
 | 
						||
    if [ -n "$__t_eend" ]; then
 | 
						||
        eend $__t_s
 | 
						||
        [ -n "$__t_clearp" ] && eclearp
 | 
						||
    fi
 | 
						||
    return $__t_s
 | 
						||
}
 | 
						||
function estep() {
 | 
						||
# Afficher la description d'une opération. Cette fonction est particulièrement
 | 
						||
# appropriée dans le contexte d'un etitle.
 | 
						||
# Les variantes e (error), w (warning), n (note), i (info) permettent d'afficher
 | 
						||
# des couleurs différentes, mais toutes sont du niveau info.
 | 
						||
    show_info || return; eflush; __estep "$*" 1>&2
 | 
						||
}
 | 
						||
function estepe() {
 | 
						||
    show_info || return; eflush; __estepe "$*" 1>&2
 | 
						||
}
 | 
						||
function estepw() {
 | 
						||
    show_info || return; eflush; __estepw "$*" 1>&2
 | 
						||
}
 | 
						||
function estepn() {
 | 
						||
    show_info || return; eflush; __estepn "$*" 1>&2
 | 
						||
}
 | 
						||
function estepi() {
 | 
						||
    show_info || return; eflush; __estepi "$*" 1>&2
 | 
						||
}
 | 
						||
function estep_() {
 | 
						||
    show_info || return; eflush; __estep_ "$*" 1>&2
 | 
						||
}
 | 
						||
function estepe_() {
 | 
						||
    show_info || return; eflush; __estepe_ "$*" 1>&2
 | 
						||
}
 | 
						||
function estepw_() {
 | 
						||
    show_info || return; eflush; __estepw_ "$*" 1>&2
 | 
						||
}
 | 
						||
function estepn_() {
 | 
						||
    show_info || return; eflush; __estepn_ "$*" 1>&2
 | 
						||
}
 | 
						||
function estepi_() {
 | 
						||
    show_info || return; eflush; __estepi_ "$*" 1>&2
 | 
						||
}
 | 
						||
function ebegin() {
 | 
						||
# Afficher le message $1, qui décrit le début d'une opération. Cette fonction
 | 
						||
# débute une section, qu'il faut terminer avec eend.
 | 
						||
# Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le
 | 
						||
# contexte de la section. Puis, la section est terminée automatiquement, sauf si
 | 
						||
# l'option -s est spécifiée, auquel cas la section reste ouverte.
 | 
						||
    local __b_eend=default
 | 
						||
    while [ -n "$1" ]; do
 | 
						||
        if [ "$1" == "--" ]; then
 | 
						||
            shift
 | 
						||
            break
 | 
						||
        elif [ "$1" == "-s" ]; then
 | 
						||
            __b_eend=
 | 
						||
            shift
 | 
						||
        elif [ "$1" == "--eend" ]; then
 | 
						||
            __b_eend=1
 | 
						||
            shift
 | 
						||
        else
 | 
						||
            break
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    local __b_msg="$1"; shift
 | 
						||
    local __b_s=0
 | 
						||
    # ebegin
 | 
						||
    __estack="$__estack:b"
 | 
						||
    if show_info; then
 | 
						||
        eflush
 | 
						||
        __ebegin "$__b_msg" 1>&2
 | 
						||
    fi
 | 
						||
    # commande
 | 
						||
    if [ $# -gt 0 ]; then
 | 
						||
        "$@"
 | 
						||
        __b_s=$?
 | 
						||
        [ "$__b_eend" == "default" ] && __b_eend=1
 | 
						||
    fi
 | 
						||
    # eend
 | 
						||
    [ "$__b_eend" == "default" ] && __b_eend=
 | 
						||
    [ -n "$__b_eend" ] && eend $__b_s
 | 
						||
    return $__b_s
 | 
						||
}
 | 
						||
function edot() {
 | 
						||
# Afficher une étape d'une opération, matérialisée par un point '.' ou une
 | 
						||
# croix 'x' en cas de succès ou d'erreur. Cette fonction est particulièrement
 | 
						||
# appropriée dans le contexte d'un ebegin.
 | 
						||
    local s=$?
 | 
						||
    show_info || return
 | 
						||
    eflush
 | 
						||
    [ -n "$1" ] && s="$1"
 | 
						||
    shift
 | 
						||
    if [ "$s" == "0" ]; then
 | 
						||
        __edoto 1>&2
 | 
						||
    else
 | 
						||
        __edotx 1>&2
 | 
						||
    fi
 | 
						||
    show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2
 | 
						||
    return $s
 | 
						||
}
 | 
						||
function edotw() {
 | 
						||
# Afficher un avertissement comme étape d'une opération, matérialisée par une
 | 
						||
# lettre 'w' (typiquement de couleur jaune). Cette fonction est particulièrement
 | 
						||
# appropriée dans le contexte d'un ebegin.
 | 
						||
    local s=$?
 | 
						||
    show_info || return
 | 
						||
    eflush
 | 
						||
    [ -n "$1" ] && s="$1"
 | 
						||
    shift
 | 
						||
    __edotw 1>&2
 | 
						||
    show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2
 | 
						||
    return $s
 | 
						||
}
 | 
						||
function ewait() {
 | 
						||
# Afficher les étapes d'une opération qui dure, matérialisées par des '+' toutes
 | 
						||
# les secondes tant que le processus $1 tourne.
 | 
						||
# A utiliser de cette manière:
 | 
						||
#     ebegin "msg"
 | 
						||
#     cmd &
 | 
						||
#     ewait $!
 | 
						||
#     eend
 | 
						||
    [ -n "$1" ] || return 1
 | 
						||
    if show_info; then
 | 
						||
        local count=2
 | 
						||
        eflush
 | 
						||
        little_sleep # certains processus retournent tout de suite
 | 
						||
        while is_running "$1"; do
 | 
						||
            sleep 1
 | 
						||
            if [ $count -gt 0 ]; then
 | 
						||
                # attendre 2 secondes avant de commencer à afficher des '+'
 | 
						||
                count=$(($count - 1))
 | 
						||
            else
 | 
						||
                __edotp 1>&2
 | 
						||
            fi
 | 
						||
        done
 | 
						||
        # terminer par un '.'
 | 
						||
        __edoto 1>&2
 | 
						||
    else
 | 
						||
        # ne rien afficher, mais attendre quand même la fin de l'opération
 | 
						||
        wait "$1"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function eend() {
 | 
						||
# Terminer une section.
 | 
						||
# Avec l'option -c, remettre à zéro toutes les informations de section
 | 
						||
# Si la section en cours est un ebegin, afficher la fin de l'opération: [ok] ou
 | 
						||
# [error] en fonction du code de retour de la dernière commande (ou de $1 si
 | 
						||
# cette valeur est donnée)
 | 
						||
# Si la section en cours est un etitle, marquer la fin de la section concernée
 | 
						||
# par le titre.
 | 
						||
    local s=$?
 | 
						||
    if [ "$1" == "-c" ]; then
 | 
						||
        __estack=
 | 
						||
        __tlevel=
 | 
						||
    elif [ "${__estack%:b}" != "$__estack" ]; then
 | 
						||
        # terminer ebegin
 | 
						||
        __estack="${__estack%:b}"
 | 
						||
        show_info || return
 | 
						||
        eflush
 | 
						||
        [ -n "$1" ] && s="$1"
 | 
						||
        if [ "$s" == "0" ]; then
 | 
						||
            __eendo 1>&2
 | 
						||
        else
 | 
						||
            __eendx 1>&2
 | 
						||
        fi
 | 
						||
    elif [ "${__estack%:t}" != "$__estack" ]; then
 | 
						||
        # terminer etitle -s
 | 
						||
        __estack="${__estack%:t}"
 | 
						||
        __tlevel="${__tlevel%  }"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function __elinedots() {
 | 
						||
    ebegin "$1"
 | 
						||
    local line
 | 
						||
    if show_debug; then
 | 
						||
        while read line; do
 | 
						||
            __edoto 1>&2
 | 
						||
            __edotd "$line" 1>&2
 | 
						||
        done
 | 
						||
    else
 | 
						||
        while read line; do
 | 
						||
            __edoto 1>&2
 | 
						||
        done
 | 
						||
    fi
 | 
						||
    eend
 | 
						||
}
 | 
						||
function elinedots() {
 | 
						||
# Afficher un message comme avec ebegin "$1", puis afficher un point '.' pour
 | 
						||
# chaque ligne lue sur stdin. Cela permet de suivre une opération. En mode
 | 
						||
# DEBUG, afficher la ligne affichée plutôt qu'un point.
 | 
						||
# Si $2..$* sont spécifiés, lancer la commande et suivre sa sortie. Ainsi,
 | 
						||
# 'elinedots msg cmd args' est un raccourci pour 'cmd args | elinedots msg'
 | 
						||
    local msg="$1"; shift
 | 
						||
    if [ $# -gt 0 ]; then
 | 
						||
        "$@" | __elinedots "$msg"
 | 
						||
    else
 | 
						||
        __elinedots "$msg"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function ask_yesno() {
 | 
						||
# Afficher le message $1 suivi de [oN] ou [On] suivant que $2 vaut O ou N, puis
 | 
						||
# lire la réponse. Retourner 0 si la réponse est vrai, 1 sinon.
 | 
						||
# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si
 | 
						||
# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées
 | 
						||
# ($2=message, $3=default)
 | 
						||
# Si $2 vaut C, la valeur par défaut est N si on est interactif, O sinon
 | 
						||
# Si $2 vaut X, la valeur par défaut est O si on est interactif, N sinon
 | 
						||
    local interactive=1
 | 
						||
    if [[ "$1" == -* ]]; then
 | 
						||
        if [ "$1" != -- ]; then
 | 
						||
            check_interaction "$1" || interactive=
 | 
						||
        fi
 | 
						||
        shift
 | 
						||
    else
 | 
						||
        check_interaction -c || interactive=
 | 
						||
    fi
 | 
						||
    local default="${2:-N}"
 | 
						||
    if [ "$default" == "C" ]; then
 | 
						||
        [ -n "$interactive" ] && default=N || default=O
 | 
						||
    elif [ "$default" == "X" ]; then
 | 
						||
        [ -n "$interactive" ] && default=O || default=N
 | 
						||
    fi
 | 
						||
    if [ -n "$interactive" ]; then
 | 
						||
        eflush
 | 
						||
        local message="$1"
 | 
						||
        local prompt="[oN]"
 | 
						||
        local r
 | 
						||
        is_yes "$default" && prompt="[On]"
 | 
						||
        if [ -n "$message" ]; then
 | 
						||
            __eecho_ "$message" 1>&2
 | 
						||
        else
 | 
						||
            OENC="$UTF8" __eecho_ "Voulez-vous continuer?" 1>&2
 | 
						||
        fi
 | 
						||
        OENC="$UTF8" tooenc_ " $prompt " 1>&2
 | 
						||
        uread r
 | 
						||
        is_yes "${r:-$default}"
 | 
						||
    else
 | 
						||
        is_yes "$default"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function ask_any() {
 | 
						||
# Afficher le message $1 suivi du texte "[$2]" (qui vaut par défaut +Oq), puis
 | 
						||
# lire la réponse. Les lettres de la chaine de format $2 sont numérotées de 0 à
 | 
						||
# $((${#2} - 1)). Le code de retour est le numéro de la lettre qui a été
 | 
						||
# sélectionnée. Cette fonction est une généralisation de ask_yesno() pour
 | 
						||
# n'importe quel ensemble de lettres.
 | 
						||
# La première lettre en majuscule est la lettre sélectionnée par défaut.
 | 
						||
# La lettre O matche toutes les lettres qui signifient oui: o, y, 1, v, t
 | 
						||
# La lettre N matche toutes les lettres qui signifient non: n, f, 0
 | 
						||
# Il y a des raccourcis:
 | 
						||
# +O --> On
 | 
						||
# +N --> oN
 | 
						||
# +C --> oN si on est en mode interactif, On sinon
 | 
						||
# +X --> On si on est en mode interactifn oN sinon
 | 
						||
# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si
 | 
						||
# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées
 | 
						||
# ($2=message, $3=format)
 | 
						||
    local interactive=1
 | 
						||
    if [[ "$1" == -* ]]; then
 | 
						||
        if [ "$1" != -- ]; then
 | 
						||
            check_interaction "$1" || interactive=
 | 
						||
        fi
 | 
						||
        shift
 | 
						||
    else
 | 
						||
        check_interaction -c || interactive=
 | 
						||
    fi
 | 
						||
    local format="${2:-+Oq}"
 | 
						||
    format="${format/+O/On}"
 | 
						||
    format="${format/+N/oN}"
 | 
						||
    if [ -n "$interactive" ]; then
 | 
						||
        format="${format/+C/oN}"
 | 
						||
        format="${format/+X/On}"
 | 
						||
    else
 | 
						||
        format="${format/+C/On}"
 | 
						||
        format="${format/+X/oN}"
 | 
						||
    fi
 | 
						||
    local i count="${#format}"
 | 
						||
 | 
						||
    if [ -n "$interactive" ]; then
 | 
						||
        eflush
 | 
						||
        local message="${1:-Voulez-vous continuer?}"
 | 
						||
        local prompt="[$format]"
 | 
						||
        local r f lf defi
 | 
						||
        while true; do
 | 
						||
            __eecho_ "$message $prompt " 1>&2
 | 
						||
            uread r
 | 
						||
            r="$(strlower "${r:0:1}")"
 | 
						||
            i=0; defi=
 | 
						||
            while [ $i -lt $count ]; do
 | 
						||
                f="${format:$i:1}"
 | 
						||
                lf="$(strlower "$f")"
 | 
						||
                [ "$r" == "$lf" ] && return $i
 | 
						||
                if [ -z "$defi" ]; then
 | 
						||
                    [ -z "${f/[A-Z]/}" ] && defi="$i"
 | 
						||
                fi
 | 
						||
                if [ "$lf" == o ]; then
 | 
						||
                    case "$r" in o|y|1|v|t) return $i;; esac
 | 
						||
                elif [ "$lf" == n ]; then
 | 
						||
                    case "$r" in n|f|0) return $i;; esac
 | 
						||
                fi
 | 
						||
                i=$(($i + 1))
 | 
						||
            done
 | 
						||
            [ -z "$r" ] && return ${defi:-0}
 | 
						||
        done
 | 
						||
    else
 | 
						||
        i=0
 | 
						||
        while [ $i -lt $count ]; do
 | 
						||
            f="${format:$i:1}"
 | 
						||
            [ -z "${f/[A-Z]/}" ] && return $i
 | 
						||
            i=$(($i + 1))
 | 
						||
        done
 | 
						||
        return 0
 | 
						||
    fi
 | 
						||
}
 | 
						||
function read_value() {
 | 
						||
# Afficher le message $1 suivi de la valeur par défaut [$3] si elle est non
 | 
						||
# vide, puis lire la valeur donnée par l'utilisateur. Cette valeur doit être non
 | 
						||
# vide si $4(=O) est vrai. La valeur saisie est placée dans la variable
 | 
						||
# $2(=value)
 | 
						||
# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si
 | 
						||
# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées
 | 
						||
# ($2=message, $3=variable, $4=default, $5=required)
 | 
						||
# En mode non interactif, c'est la valeur par défaut qui est sélectionnée. Si
 | 
						||
# l'utilisateur requière que la valeur soit non vide et que la valeur par défaut
 | 
						||
# est vide, afficher un message d'erreur et retourner faux
 | 
						||
# read_password() est comme read_value(), mais la valeur saisie n'est pas
 | 
						||
# affichée, ce qui la rend appropriée pour la lecture d'un mot de passe.
 | 
						||
    local -a __rv_opts __rv_readline=1 __rv_showdef=1 __rv_nl=
 | 
						||
    __rv_opts=()
 | 
						||
    [ -n "$UTOOLS_NO_READLINE" ] && __rv_readline=
 | 
						||
    __rv_read "$@"
 | 
						||
}
 | 
						||
function read_password() {
 | 
						||
    local -a __rv_opts __rv_readline= __rv_showdef= __rv_nl=1
 | 
						||
    __rv_opts=(-s)
 | 
						||
    __rv_read "$@"
 | 
						||
}
 | 
						||
function __rv_read() {
 | 
						||
    local __rv_int=1
 | 
						||
    if [[ "$1" == -* ]]; then
 | 
						||
        if [ "$1" != -- ]; then
 | 
						||
            check_interaction "$1" || __rv_int=
 | 
						||
        fi
 | 
						||
        shift
 | 
						||
    else
 | 
						||
        check_interaction -c || __rv_int=
 | 
						||
    fi
 | 
						||
    local __rv_msg="$1" __rv_v="${2:-value}" __rv_d="$3" __rv_re="${4:-O}"
 | 
						||
    if [ -z "$__rv_int" ]; then
 | 
						||
        # En mode non interactif, retourner la valeur par défaut
 | 
						||
        if is_yes "$__rv_re" && [ -z "$__rv_d" ]; then
 | 
						||
            OENC="$UTF8" eerror "La valeur par défaut de $__rv_v doit être non vide"
 | 
						||
            return 1
 | 
						||
        fi
 | 
						||
        _setv "$__rv_v" "$__rv_d"
 | 
						||
        return 0
 | 
						||
    fi
 | 
						||
 | 
						||
    eflush
 | 
						||
    local __rv_r
 | 
						||
    while true; do
 | 
						||
        if [ -n "$__rv_msg" ]; then
 | 
						||
            __eecho_ "$__rv_msg" 1>&2
 | 
						||
        else
 | 
						||
            OENC="$UTF8" __eecho_ "Entrez la valeur" 1>&2
 | 
						||
        fi
 | 
						||
        if [ -n "$__rv_readline" ]; then
 | 
						||
            OENC="$UTF8" tooenc_ ": " 1>&2
 | 
						||
            uread -e ${__rv_d:+-i"$__rv_d"} "${__rv_opts[@]}" __rv_r
 | 
						||
        else
 | 
						||
            if [ -n "$__rv_d" ]; then
 | 
						||
                if [ -n "$__rv_showdef" ]; then
 | 
						||
                    tooenc_ " [$__rv_d]" 1>&2
 | 
						||
                else
 | 
						||
                    tooenc_ " [****]" 1>&2
 | 
						||
                fi
 | 
						||
            fi
 | 
						||
            OENC="$UTF8" tooenc_ ": " 1>&2
 | 
						||
            uread "${__rv_opts[@]}" __rv_r
 | 
						||
            [ -n "$__rv_nl" ] && echo
 | 
						||
        fi
 | 
						||
        __rv_r="${__rv_r:-$__rv_d}"
 | 
						||
        if [ -n "$__rv_r" ] || ! is_yes "$__rv_re"; then
 | 
						||
            _setv "$__rv_v" "$__rv_r"
 | 
						||
            return 0
 | 
						||
        fi
 | 
						||
    done
 | 
						||
}
 | 
						||
function simple_menu() {
 | 
						||
# Afficher un menu simple dont les éléments sont les valeurs du tableau
 | 
						||
# $2(=options). L'option choisie est placée dans la variable $1(=option)
 | 
						||
# -t TITLE: spécifier le titre du menu
 | 
						||
# -m YOUR_CHOICE: spécifier le message d'invite pour la sélection de l'option
 | 
						||
# -d DEFAULT: spécifier l'option par défaut. Par défaut, prendre la valeur
 | 
						||
# actuelle de la variable $1(=option)
 | 
						||
    local __sm_title= __sm_yourchoice= __sm_default=
 | 
						||
    local -a __sm_args
 | 
						||
    parse_opts -t: __sm_title= -m: __sm_yourchoice= -d: __sm_default= @ __sm_args -- "$@" &&
 | 
						||
    set -- "${__sm_args[@]}" || ewarn "$__sm_args"
 | 
						||
 | 
						||
    local __sm_option_var="${1:-option}" __sm_options_var="${2:-options}"
 | 
						||
    local __sm_option __sm_options
 | 
						||
    __sm_options="$__sm_options_var[*]"
 | 
						||
    if [ -z "${!__sm_options}" ]; then
 | 
						||
        OENC="$UTF8" eerror "Le tableau $__sm_options_var doit être non vide"
 | 
						||
        return 1
 | 
						||
    fi
 | 
						||
    [ -z "$__sm_default" ] && __sm_default="${!__sm_option_var}"
 | 
						||
 | 
						||
    eflush
 | 
						||
    array_copy __sm_options "$__sm_options_var"
 | 
						||
    local __sm_c=0 __sm_i __sm_choice
 | 
						||
    while true; do
 | 
						||
        if [ "$__sm_c" == "0" ]; then
 | 
						||
            # Afficher le menu
 | 
						||
            [ -n "$__sm_title" ] && __eecho "=== $__sm_title ===" 1>&2
 | 
						||
            __sm_i=1
 | 
						||
            for __sm_option in "${__sm_options[@]}"; do
 | 
						||
                if [ "$__sm_option" == "$__sm_default" ]; then
 | 
						||
                    __eecho "$__sm_i*- $__sm_option" 1>&2
 | 
						||
                else
 | 
						||
                    __eecho "$__sm_i - $__sm_option" 1>&2
 | 
						||
                fi
 | 
						||
                let __sm_i=$__sm_i+1
 | 
						||
            done
 | 
						||
        fi
 | 
						||
 | 
						||
        # Afficher les choix
 | 
						||
        if [ -n "$__sm_yourchoice" ]; then
 | 
						||
            __eecho_ "$__sm_yourchoice" 1>&2
 | 
						||
        else
 | 
						||
            OENC="$UTF8" __eecho_ "Entrez le numéro de l'option choisie" 1>&2
 | 
						||
        fi
 | 
						||
        OENC="$UTF8" tooenc_ ": " 1>&2
 | 
						||
        uread __sm_choice
 | 
						||
 | 
						||
        # Valeur par défaut
 | 
						||
        if [ -z "$__sm_choice" -a -n "$__sm_default" ]; then
 | 
						||
            __sm_option="$__sm_default"
 | 
						||
            break
 | 
						||
        fi
 | 
						||
        # Vérifier la saisie
 | 
						||
        if [ -n "$__sm_choice" -a -z "${__sm_choice//[0-9]/}" ]; then
 | 
						||
            if [ "$__sm_choice" -gt 0 -a "$__sm_choice" -le "${#__sm_options[*]}" ]; then
 | 
						||
                __sm_option="${__sm_options[$(($__sm_choice - 1))]}"
 | 
						||
                break
 | 
						||
            else
 | 
						||
                OENC="$UTF8" eerror "Numéro d'option incorrect"
 | 
						||
            fi
 | 
						||
        else
 | 
						||
            OENC="$UTF8" eerror "Vous devez saisir le numéro de l'option choisie"
 | 
						||
        fi
 | 
						||
 | 
						||
        let __sm_c=$__sm_c+1
 | 
						||
        if [ "$__sm_c" -eq 5 ]; then
 | 
						||
            # sauter une ligne toutes les 4 tentatives
 | 
						||
            OENC="$UTF8" tooenc "" 1>&2
 | 
						||
            __sm_c=0
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    _setv "$__sm_option_var" "$__sm_option"
 | 
						||
}
 | 
						||
 | 
						||
function actions_menu() {
 | 
						||
# Afficher un menu dont les éléments sont les valeurs du tableau $4(=options),
 | 
						||
# et une liste d'actions tirées du tableau $3(=actions). L'option choisie est
 | 
						||
# placée dans la variable $2(=option). L'action choisie est placée dans la
 | 
						||
# variable $1(=action)
 | 
						||
# Un choix est saisi sous la forme [action]num_option
 | 
						||
# -t TITLE: spécifier le titre du menu
 | 
						||
# -m OPT_YOUR_CHOICE: spécifier le message d'invite pour la sélection de
 | 
						||
#     l'action et de l'option
 | 
						||
# -M ACT_YOUR_CHOICE: spécifier le message d'invite dans le cas où aucune option
 | 
						||
#     n'est disponible. Dans ce cas, seules les actions vides sont possibles.
 | 
						||
# -e VOID_ACTION: spécifier qu'une action est vide, c'est à dire qu'elle ne
 | 
						||
#     requière pas d'être associée à une option. Par défaut, la dernière action
 | 
						||
#     est classée dans cette catégorie puisque c'est l'action "quitter"
 | 
						||
# -d DEFAULT_ACTION: choisir l'action par défaut. par défaut, c'est la première
 | 
						||
#     action.
 | 
						||
# -q QUIT_ACTION: choisir l'option "quitter" qui provoque la sortie du menu sans
 | 
						||
#     choix. par défaut, c'est la dernière action.
 | 
						||
# -o DEFAULT_OPTION: choisir l'option par défaut. par défaut, prendre la valeur
 | 
						||
#     actuelle de la variable $2(=option)
 | 
						||
    local -a __am_action_descs __am_options __am_void_actions
 | 
						||
    local __am_tmp __am_select_action __am_select_option __am_title __am_optyc __am_actyc
 | 
						||
    local __am_default_action=auto __am_quit_action=auto
 | 
						||
    local __am_default_option=
 | 
						||
    local -a __am_args
 | 
						||
    parse_opts \
 | 
						||
        -t: __am_title= \
 | 
						||
        -m: __am_optyc= \
 | 
						||
        -M: __am_actyc= \
 | 
						||
        -e: __am_void_actions \
 | 
						||
        -d: __am_default_action= \
 | 
						||
        -q: __am_quit_action= \
 | 
						||
        -o: __am_default_option= \
 | 
						||
        @ __am_args -- "$@" && set -- "${__am_args[@]}" || { eerror "$__am_args"; return 1; }
 | 
						||
 | 
						||
    __am_tmp="${1:-action}"; __am_select_action="${!__am_tmp}"
 | 
						||
    __am_tmp="${2:-option}"; __am_select_option="${!__am_tmp}"
 | 
						||
    [ -n "$__am_default_option" ] && __am_select_option="$__am_default_option"
 | 
						||
    array_copy __am_action_descs "${3:-actions}"
 | 
						||
    array_copy __am_options "${4:-options}"
 | 
						||
 | 
						||
    eerror_unless [ ${#__am_action_descs[*]} -gt 0 ] "Vous devez spécifier le tableau des actions" || return
 | 
						||
    __actions_menu || return 1
 | 
						||
    _setv "${1:-action}" "$__am_select_action"
 | 
						||
    _setv "${2:-option}" "$__am_select_option"
 | 
						||
}
 | 
						||
function __actions_menu() {
 | 
						||
    local title="$__am_title"
 | 
						||
    local optyc="$__am_optyc" actyc="$__am_actyc"
 | 
						||
    local default_action="$__am_default_action"
 | 
						||
    local quit_action="$__am_quit_action"
 | 
						||
    local select_action="$__am_select_action"
 | 
						||
    local select_option="$__am_select_option"
 | 
						||
    local -a action_descs options void_actions
 | 
						||
    array_copy action_descs __am_action_descs
 | 
						||
    array_copy options __am_options
 | 
						||
    array_copy void_actions __am_void_actions
 | 
						||
 | 
						||
    # Calculer la liste des actions valides
 | 
						||
    local no_options
 | 
						||
    array_isempty options && no_options=1
 | 
						||
 | 
						||
    local -a actions
 | 
						||
    local tmp action name
 | 
						||
    for tmp in "${action_descs[@]}"; do
 | 
						||
        splitfsep2 "$tmp" : action name
 | 
						||
        [ -n "$action" ] || action="${name:0:1}"
 | 
						||
        action="$(strlower "$action")"
 | 
						||
        array_addu actions "$action"
 | 
						||
    done
 | 
						||
 | 
						||
    # Calculer l'action par défaut
 | 
						||
    if [ "$default_action" == auto ]; then
 | 
						||
        # si action par défaut non spécifiée, alors prendre la première action
 | 
						||
        default_action="$select_action"
 | 
						||
        if [ -n "$default_action" ]; then
 | 
						||
            array_contains actions "$default_action" || default_action=
 | 
						||
        fi
 | 
						||
        [ -n "$default_action" ] || default_action="${actions[0]}"
 | 
						||
    fi
 | 
						||
    default_action="${default_action:0:1}"
 | 
						||
    default_action="$(strlower "$default_action")"
 | 
						||
 | 
						||
    # Calculer l'action quitter par défaut
 | 
						||
    if [ "$quit_action" == auto ]; then
 | 
						||
        # si action par défaut non spécifiée, alors prendre la dernière action,
 | 
						||
        # s'il y a au moins 2 actions
 | 
						||
        if [ ${#actions[*]} -gt 1 ]; then
 | 
						||
            quit_action="${actions[@]:$((-1)):1}"
 | 
						||
            array_addu void_actions "$quit_action"
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    quit_action="${quit_action:0:1}"
 | 
						||
    quit_action="$(strlower "$quit_action")"
 | 
						||
 | 
						||
    # Calculer la ligne des actions à afficher
 | 
						||
    local action_title
 | 
						||
    for tmp in "${action_descs[@]}"; do
 | 
						||
        splitfsep2 "$tmp" : action name
 | 
						||
        [ -n "$action" ] || action="${name:0:1}"
 | 
						||
        [ -n "$name" ] || name="$action"
 | 
						||
        action="$(strlower "$action")"
 | 
						||
        if [ -n "$no_options" ]; then
 | 
						||
            if ! array_contains void_actions "$action"; then
 | 
						||
                array_del actions "$action"
 | 
						||
                continue
 | 
						||
            fi
 | 
						||
        fi
 | 
						||
        [ "$action" == "$default_action" ] && name="$name*"
 | 
						||
        action_title="${action_title:+$action_title/}$name"
 | 
						||
    done
 | 
						||
    if [ -n "$default_action" ]; then
 | 
						||
        # si action par défaut invalide, alors pas d'action par défaut
 | 
						||
        array_contains actions "$default_action" || default_action=
 | 
						||
    fi
 | 
						||
    if [ -n "$quit_action" ]; then
 | 
						||
        # si action quitter invalide, alors pas d'action quitter
 | 
						||
        array_contains actions "$quit_action" || quit_action=
 | 
						||
    fi
 | 
						||
 | 
						||
    # Type de menu
 | 
						||
    if [ -n "$no_options" ]; then
 | 
						||
        if array_isempty void_actions; then
 | 
						||
            eerror "Aucune option n'est définie. Il faut définir le tableau des actions vides"
 | 
						||
            return 1
 | 
						||
        fi
 | 
						||
        __void_actions_menu
 | 
						||
    else
 | 
						||
        __options_actions_menu
 | 
						||
    fi
 | 
						||
}
 | 
						||
function __void_actions_menu() {
 | 
						||
    eflush
 | 
						||
    local c=0 choice
 | 
						||
    while true; do
 | 
						||
        if [ $c -eq 0 ]; then
 | 
						||
            [ -n "$title" ] && __etitle "$title" 1>&2
 | 
						||
            __eecho_ "=== Actions disponibles: " 1>&2
 | 
						||
            tooenc "$action_title" 1>&2
 | 
						||
        fi
 | 
						||
        if [ -n "$actyc" ]; then
 | 
						||
            __eecho_ "$actyc" 1>&2
 | 
						||
        elif [ -n "$optyc" ]; then
 | 
						||
            __eecho_ "$optyc" 1>&2
 | 
						||
        else
 | 
						||
            __eecho_ "Entrez l'action à effectuer" 1>&2
 | 
						||
        fi
 | 
						||
        tooenc_ ": " 1>&2
 | 
						||
        uread choice
 | 
						||
        if [ -z "$choice" -a -n "$default_action" ]; then
 | 
						||
            select_action="$default_action"
 | 
						||
            break
 | 
						||
        fi
 | 
						||
 | 
						||
        # vérifier la saisie
 | 
						||
        choice="${choice:0:1}"
 | 
						||
        choice="$(strlower "$choice")"
 | 
						||
        if array_contains actions "$choice"; then
 | 
						||
            select_action="$choice"
 | 
						||
            break
 | 
						||
        elif [ -n "$choice" ]; then
 | 
						||
            eerror "$choice: action incorrecte"
 | 
						||
        else
 | 
						||
            eerror "vous devez saisir l'action à effectuer"
 | 
						||
        fi
 | 
						||
        let c=$c+1
 | 
						||
        if [ $c -eq 5 ]; then
 | 
						||
            # sauter une ligne toutes les 4 tentatives
 | 
						||
            tooenc "" 1>&2
 | 
						||
            c=0
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    __am_select_action="$select_action"
 | 
						||
    __am_select_option=
 | 
						||
}
 | 
						||
function __options_actions_menu() {
 | 
						||
    eflush
 | 
						||
    local c=0 option choice action option
 | 
						||
    while true; do
 | 
						||
        if [ $c -eq 0 ]; then
 | 
						||
            [ -n "$title" ] && __etitle "$title" 1>&2
 | 
						||
            i=1
 | 
						||
            for option in "${options[@]}"; do
 | 
						||
                if [ "$option" == "$select_option" ]; then
 | 
						||
                    tooenc "$i*- $option" 1>&2
 | 
						||
                else
 | 
						||
                    tooenc "$i - $option" 1>&2
 | 
						||
                fi
 | 
						||
                let i=$i+1
 | 
						||
            done
 | 
						||
            __estepn_ "Actions disponibles: " 1>&2
 | 
						||
            tooenc "$action_title" 1>&2
 | 
						||
        fi
 | 
						||
        if [ -n "$optyc" ]; then
 | 
						||
            __eecho_ "$optyc" 1>&2
 | 
						||
        else
 | 
						||
            __eecho_ "Entrez l'action et le numéro de l'option choisie" 1>&2
 | 
						||
        fi
 | 
						||
        tooenc_ ": " 1>&2
 | 
						||
        uread choice
 | 
						||
 | 
						||
        # vérifier la saisie
 | 
						||
        if [ -z "$choice" -a -n "$default_action" ]; then
 | 
						||
            action="$default_action"
 | 
						||
            if array_contains void_actions "$action"; then
 | 
						||
                select_action="$action"
 | 
						||
                select_option=
 | 
						||
                break
 | 
						||
            elif [ -n "$select_option" ]; then
 | 
						||
                select_action="$action"
 | 
						||
                break
 | 
						||
            fi
 | 
						||
        fi
 | 
						||
        action="${choice:0:1}"
 | 
						||
        action="$(strlower "$action")"
 | 
						||
        if array_contains actions "$action"; then
 | 
						||
            # on commence par un code d'action valide. cool :-)
 | 
						||
            if array_contains void_actions "$action"; then
 | 
						||
                select_action="$action"
 | 
						||
                select_option=
 | 
						||
                break
 | 
						||
            else
 | 
						||
                option="${choice:1}"
 | 
						||
                option="${option// /}"
 | 
						||
                if [ -z "$option" -a -n "$select_option" ]; then
 | 
						||
                    select_action="$action"
 | 
						||
                    break
 | 
						||
                elif [ -z "$option" ]; then
 | 
						||
                    eerror "vous devez saisir le numéro de l'option"
 | 
						||
                elif isnum "$option"; then
 | 
						||
                    if [ $option -gt 0 -a $option -le ${#options[*]} ]; then
 | 
						||
                        select_action="$action"
 | 
						||
                        select_option="${options[$(($option - 1))]}"
 | 
						||
                        break
 | 
						||
                    fi
 | 
						||
                else
 | 
						||
                    eerror "$option: numéro d'option incorrecte"
 | 
						||
                fi
 | 
						||
            fi
 | 
						||
        elif isnum "$choice"; then
 | 
						||
            # on a simplement donné un numéro d'option
 | 
						||
            action="$default_action"
 | 
						||
            if [ -n "$action" ]; then
 | 
						||
                if array_contains void_actions "$action"; then
 | 
						||
                    select_action="$action"
 | 
						||
                    select_option=
 | 
						||
                    break
 | 
						||
                else
 | 
						||
                    option="${choice// /}"
 | 
						||
                    if [ -z "$option" ]; then
 | 
						||
                        eerror "vous devez saisir le numéro de l'option"
 | 
						||
                    elif isnum "$option"; then
 | 
						||
                        if [ $option -gt 0 -a $option -le ${#options[*]} ]; then
 | 
						||
                            select_action="$action"
 | 
						||
                            select_option="${options[$(($option - 1))]}"
 | 
						||
                            break
 | 
						||
                        fi
 | 
						||
                    else
 | 
						||
                        eerror "$option: numéro d'option incorrecte"
 | 
						||
                    fi
 | 
						||
                fi
 | 
						||
            else
 | 
						||
                eerror "Vous devez spécifier l'action à effectuer"
 | 
						||
            fi
 | 
						||
        elif [ -n "$choice" ]; then
 | 
						||
            eerror "$choice: action et/ou option incorrecte"
 | 
						||
        else
 | 
						||
            eerror "vous devez saisir l'action à effectuer"
 | 
						||
        fi
 | 
						||
        let c=$c+1
 | 
						||
        if [ $c -eq 5 ]; then
 | 
						||
            # sauter une ligne toutes les 4 tentatives
 | 
						||
            tooenc "" 1>&2
 | 
						||
            c=0
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    __am_select_action="$select_action"
 | 
						||
    __am_select_option="$select_option"
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## fichiers temporaires
 | 
						||
 | 
						||
# autoclean: gérer une liste de fichiers temporaires à supprimer en fin de
 | 
						||
# programme
 | 
						||
function __ac_forgetall() { __ac_files=(); }
 | 
						||
__ac_forgetall
 | 
						||
function __ac_trap() {
 | 
						||
    local file
 | 
						||
    for file in "${__ac_files[@]}"; do
 | 
						||
        [ -e "$file" ] && rm -rf "$file" 2>/dev/null
 | 
						||
    done
 | 
						||
    __ac_forgetall
 | 
						||
}
 | 
						||
trap __ac_trap 1 3 15 EXIT
 | 
						||
function autoclean() {
 | 
						||
# Ajouter $1..$n à la liste des fichiers à supprimer à la fin du programme
 | 
						||
    local file
 | 
						||
    for file in "$@"; do
 | 
						||
        [ -n "$file" ] && array_add __ac_files "$file"
 | 
						||
    done
 | 
						||
}
 | 
						||
function ac_cleanall() {
 | 
						||
# Supprimer *tous* les fichiers temporaires gérés par autoclean tout de suite.
 | 
						||
    __ac_trap
 | 
						||
}
 | 
						||
function ac_clean() {
 | 
						||
# Supprimer les fichier temporaires $1..$* si et seulement s'ils ont été générés
 | 
						||
# par ac_set_tmpfile ou ac_set_tmpdir
 | 
						||
    local file
 | 
						||
    for file in "$@"; do
 | 
						||
        if array_contains __ac_files "$file"; then
 | 
						||
            [ -e "$file" ] && rm -rf "$file" 2>/dev/null
 | 
						||
            array_del __ac_files "$file"
 | 
						||
        fi
 | 
						||
    done
 | 
						||
}
 | 
						||
function ac_set_tmpfile() {
 | 
						||
# Créer un fichier temporaire avec le motif $2, l'ajouter à la liste des
 | 
						||
# fichiers à supprimer en fin de programme, et mettre sa valeur dans la
 | 
						||
# variable $1
 | 
						||
# En mode debug, si ($5 est vide ou ${!5} est une valeur vraie), et si $3 n'est
 | 
						||
# pas vide, prendre ce fichier au lieu de générer un nouveau fichier
 | 
						||
# temporaire. Si $4==keep, ne pas écraser le fichier $3 s'il existe.
 | 
						||
    local __acst_d
 | 
						||
    if show_debug; then
 | 
						||
        if [ -n "$5" ]; then
 | 
						||
            is_yes "${!5}" && __acst_d=1
 | 
						||
        else
 | 
						||
            __acst_d=1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    if [ -n "$__acst_d" -a -n "$3" ]; then
 | 
						||
        _setv "$1" "$3"
 | 
						||
        [ -f "$3" -a "$4" == keep ] || >"$3"
 | 
						||
    else
 | 
						||
        local __acst_t="$(mktempf "$2")"
 | 
						||
        autoclean "$__acst_t"
 | 
						||
        _setv "$1" "$__acst_t"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function ac_set_tmpdir() {
 | 
						||
# Créer un répertoire temporaire avec le motif $2, l'ajouter à la liste des
 | 
						||
# fichiers à supprimer en fin de programme, et mettre sa valeur dans la
 | 
						||
# variable $1
 | 
						||
# En mode debug, si ($4 est vide ou ${!4} est une valeur vraie), et si $3 n'est
 | 
						||
# pas vide, prendre ce nom de répertoire au lieu de créer un nouveau répertoire
 | 
						||
# temporaire
 | 
						||
    local __acst_d
 | 
						||
    if show_debug; then
 | 
						||
        if [ -n "$4" ]; then
 | 
						||
            is_yes "${!4}" && __acst_d=1
 | 
						||
        else
 | 
						||
            __acst_d=1
 | 
						||
        fi
 | 
						||
    fi
 | 
						||
    if [ -n "$__acst_d" -a -n "$3" ]; then
 | 
						||
        _setv "$1" "$3"
 | 
						||
        mkdir -p "$3"
 | 
						||
    else
 | 
						||
        local __acst_t="$(mktempd "$2")"
 | 
						||
        autoclean "$__acst_t"
 | 
						||
        _setv "$1" "$__acst_t"
 | 
						||
    fi
 | 
						||
}
 | 
						||
function debug_tee() {
 | 
						||
# En mode debug, passer le flux à travers la commande 'tee "$@"'. Sinon, le flux
 | 
						||
# est passé inchangé.
 | 
						||
    if show_debug; then
 | 
						||
        tee "$@"
 | 
						||
    else
 | 
						||
        cat
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## environnement
 | 
						||
 | 
						||
function get_user_defaults_file() {
 | 
						||
# Afficher le chemin vers le fichier utilisateur à éditer pour qu'il soit chargé
 | 
						||
# par 'set_defaults $1'. Ce fichier n'existe pas forcément; il faut peut-être le
 | 
						||
# créer.
 | 
						||
    if [ -r "$HOME/etc/default.${HOSTNAME%%.*}/$1" ]; then
 | 
						||
        echo "$HOME/etc/default.${HOSTNAME%%.*}/$1"
 | 
						||
    elif [ -r "$HOME/etc/default/$1" ]; then
 | 
						||
        echo "$HOME/etc/default/$1"
 | 
						||
    elif [ -n "$UTOOLS_LOCAL_PROFILES" ]; then
 | 
						||
        echo "$HOME/etc/default.${HOSTNAME%%.*}/$1"
 | 
						||
    else
 | 
						||
        echo "$HOME/etc/default/$1"
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
function get_defaults_files() {
 | 
						||
# Initialiser le tableau $1(=defaults) avec la liste des fichiers qui seraient
 | 
						||
# chargés par la commande 'set_defaults $2..N'
 | 
						||
    local __gd_dest="${1:-defaults}"; shift
 | 
						||
    local -a __gd_fs
 | 
						||
    local __gd_f __gd_found
 | 
						||
    for __gd_f in "$@"; do
 | 
						||
        __gd_found=
 | 
						||
        if [ -r "/etc/default/$__gd_f" ]; then
 | 
						||
            __gd_fs=("${__gd_fs[@]}" "/etc/default/$__gd_f")
 | 
						||
            __gd_found=1
 | 
						||
        fi
 | 
						||
        if [ -r "$HOME/etc/default.${HOSTNAME%%.*}/$__gd_f" ]; then
 | 
						||
            __gd_fs=("${__gd_fs[@]}" "$HOME/etc/default.${HOSTNAME%%.*}/$__gd_f")
 | 
						||
            __gd_found=1
 | 
						||
        elif [ -r "$HOME/etc/default/$__gd_f" ]; then
 | 
						||
            __gd_fs=("${__gd_fs[@]}" "$HOME/etc/default/$__gd_f")
 | 
						||
            __gd_found=1
 | 
						||
        fi
 | 
						||
        if [ -z "$__gd_found" -a -r "$scriptdir/lib/default/$__gd_f" ]; then
 | 
						||
            # si pas de fichier dans /etc/default ni dans ~/etc/default,
 | 
						||
            # utiliser $scriptdir/lib/default/$arg en dernier lieu
 | 
						||
            __gd_fs=("${__gd_fs[@]}" "$scriptdir/lib/default/$__gd_f")
 | 
						||
        fi
 | 
						||
    done
 | 
						||
    array_copy "$__gd_dest" __gd_fs
 | 
						||
}
 | 
						||
 | 
						||
function set_defaults() {
 | 
						||
# Pour chaque argument, sourcer /etc/default/$arg *et* (en priorité
 | 
						||
# ~/etc/default.$HOSTNAME/$arg ou à défaut ~/etc/default/$arg) si ceux-ci
 | 
						||
# existent. *Sinon*, lire $scriptdir/lib/default/$arg si ce fichier existe
 | 
						||
    local -a __sd_fs
 | 
						||
    local __sd_f
 | 
						||
    get_defaults_files __sd_fs "$@"
 | 
						||
    for __sd_f in "${__sd_fs[@]}"; do
 | 
						||
        source "$__sd_f"
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
################################################################################
 | 
						||
## Informations sur le système local
 | 
						||
 | 
						||
# Nom d'hôte respectivement avec et sans domaine
 | 
						||
# contrairement à $HOSTNAME, cette valeur peut être spécifiée, comme par ruinst
 | 
						||
: "${MYHOST:=$HOSTNAME}"
 | 
						||
: "${MYHOSTNAME:=${MYHOST%%.*}}"
 | 
						||
export MYHOST MYHOSTNAME
 | 
						||
 | 
						||
function myhost() {
 | 
						||
    # Afficher le nom d'hôte pleinement qualifié, en faisant appel à la commande
 | 
						||
    # hostname. Par comparaison, $MYHOST est fourni par bash.
 | 
						||
    hostname -f 2>/dev/null || echo "$MYHOST"
 | 
						||
}
 | 
						||
function myhostname() {
 | 
						||
    # Afficher le nom d'hôte sans domaine, en faisant appel à la commande
 | 
						||
    # hostname. Par comparaison, $MYHOSTNAME est fourni par bash.
 | 
						||
    hostname -s 2>/dev/null || echo "$MYHOSTNAME"
 | 
						||
}
 |