4103 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			4103 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
 | |
| 
 | |
| # Emplacement du script courant
 | |
| if [ -n "$UTOOLS_HAVE_SCRIPTVARS" ]; then
 | |
|     # ne pas mettre à jour script, scriptname, scriptdir. Ils ont déjà été
 | |
|     # calculés
 | |
|     :
 | |
| elif [ "$0" == "-bash" ]; then
 | |
|     scriptname=
 | |
|     scriptdir=
 | |
|     script=
 | |
| else
 | |
|     scriptname="$(basename -- "$0")"
 | |
|     scriptdir="$(dirname -- "$0")"
 | |
|     scriptdir="$(cd "$scriptdir"; pwd)"
 | |
|     script="$scriptdir/$scriptname"
 | |
| fi
 | |
| : "${ULIBDIR:=$scriptdir}"
 | |
| 
 | |
| # Repertoire temporaire
 | |
| [ -z "$TMPDIR" -a -d "$HOME/tmp" ] && TMPDIR="$HOME/tmp"
 | |
| export TMPDIR="${TMPDIR:-${TMP:-${TEMP:-/tmp}}}"
 | |
| 
 | |
| # User
 | |
| [ -z "$USER" -a -n "$LOGNAME" ] && export USER="$LOGNAME"
 | |
| 
 | |
| # Le fichier nutoolsrc doit être chargé systématiquement
 | |
| [ -f /etc/nutoolsrc ] && . /etc/nutoolsrc
 | |
| [ -f ~/.nutoolsrc ] && . ~/.nutoolsrc
 | |
| 
 | |
| ################################################################################
 | |
| ## core
 | |
| function recho() {
 | |
| # afficher une valeur brute. contrairement à la commande echo, ne reconnaitre
 | |
| # aucune option (i.e. -e, -E, -n ne sont pas signifiants)
 | |
|     if [[ "${1:0:2}" == -[eEn] ]]; then
 | |
|         echo -n -
 | |
|         local first="${1:1}"; shift
 | |
|         echo "$first" "$@"
 | |
|     else
 | |
|         echo "$*"
 | |
|     fi
 | |
| }
 | |
| function recho_() {
 | |
| # afficher une valeur brute, sans passer à la ligne. contrairement à la commande
 | |
| # echo, ne reconnaitre aucune option (i.e. -e, -E, -n ne sont pas signifiants)
 | |
|     if [[ "${1:0:2}" == -[eEn] ]]; then
 | |
|         echo -n -
 | |
|         local first="${1:1}"; shift
 | |
|         echo -n "$first" "$@"
 | |
|     else
 | |
|         echo -n "$*"
 | |
|     fi
 | |
| }
 | |
| function qval() {
 | |
| # Dans la chaine $*, remplacer \ par \\, " par \", $ par \$, ` par \`
 | |
| # Cela permet de quoter une chaine à mettre entre guillements. note: la
 | |
| # protection de ! n'est pas effectuée, parce que le comportement du shell est
 | |
| # incohérent entre le shell interactif et les scripts. Pour une version plus
 | |
| # robuste, il est nécessaire d'utiliser un programme externe tel que sed ou awk
 | |
|     local s="$*"
 | |
|     s="${s//\\/\\\\}"
 | |
|     s="${s//\"/\\\"}"
 | |
|     s="${s//\$/\\\$}"
 | |
|     s="${s//\`/\\\`}"
 | |
|     recho_ "$s"
 | |
| }
 | |
| function should_quote() {
 | |
| # Tester si la chaine $* doit être mise entre quotes
 | |
|     [ -z "$1" ] && return 0
 | |
|     local s="${*//[a-zA-Z0-9]/}"
 | |
|     s="${s//,/}"
 | |
|     s="${s//./}"
 | |
|     s="${s//+/}"
 | |
|     s="${s//\//}"
 | |
|     s="${s//-/}"
 | |
|     s="${s//_/}"
 | |
|     s="${s//=/}"
 | |
|     [ -n "$s" ]
 | |
| }
 | |
| function qvals() {
 | |
| # Afficher chaque argument de cette fonction quotée le cas échéant avec "
 | |
| # Chaque valeur est séparée par un espace.
 | |
|     local arg first=1
 | |
|     for arg in "$@"; do
 | |
|         [ -z "$first" ] && echo -n " "
 | |
|         if should_quote "$arg"; then
 | |
|             echo -n \"
 | |
|             qv "$arg"
 | |
|             echo -n \"
 | |
|         else
 | |
|             recho_ "$arg"
 | |
|         fi
 | |
|         first=
 | |
|     done
 | |
| }
 | |
| function qlines() {
 | |
| # Traiter chaque ligne de l'entrée standard pour en faire des chaines quotées
 | |
| # avec '
 | |
|     sed "s/'/'\\\\''/g; s/.*/'&'/g"
 | |
| }
 | |
| function setv() {
 | |
| # initialiser la variable $1 avec la valeur $2..*
 | |
|     local __s_var="$1"; shift
 | |
|     eval "$__s_var=\"$(qval "$*")\""
 | |
| }
 | |
| function setx() {
 | |
| # initialiser la variable $1 avec le résultat de la commande '$2..@'
 | |
|     local __s_var="$1"; shift
 | |
|     eval "$__s_var=\"\$(\"\$@\")\""
 | |
| }
 | |
| function seta() {
 | |
| # initialiser le tableau $1 avec le résultat de la commande '$2..@', chaque
 | |
| # ligne du résultat étant un élément du tableau
 | |
|     local __s_array="$1"; shift
 | |
|     eval "$__s_array=($("$@" | qlines))"
 | |
| }
 | |
| function e2of() {
 | |
| # lancer la commande $@ en redirigeant la sortie d'erreur sur la sortie standard
 | |
|     "$@" 2>&1
 | |
| }
 | |
| function nef() {
 | |
| # lancer la commande $@ et filtrer du résultat toutes les lignes vides
 | |
|     "$@" | sed '/^$/d'
 | |
| }
 | |
| 
 | |
| ## valeurs
 | |
| function isnum() {
 | |
| # retourner vrai si $1 est une valeur numérique entière (positive ou négative)
 | |
|     [ ${#1} -gt 0 ] || return 1
 | |
|     local v="$1"
 | |
|     v="${v#-}"
 | |
|     v="${v//[0-9]/}"
 | |
|     [ -z "$v" ]
 | |
| }
 | |
| function ispnum() {
 | |
| # retourner vrai si $1 est une valeur numérique entière positive
 | |
|     [ ${#1} -gt 0  ] || return 1
 | |
|     local v="$1"
 | |
|     v="${v//[0-9]/}"
 | |
|     [ -z "$v" ]
 | |
| }
 | |
| function isrnum() {
 | |
| # retourner vrai si $1 est une valeur numérique réelle (positive ou négative)
 | |
| # le séparateur décimal peut être . ou ,
 | |
|     [ ${#1} -gt 0 ] || return 1
 | |
|     local v="$1"
 | |
|     v="${v#-}"
 | |
|     v="${v//./}"
 | |
|     v="${v//,/}"
 | |
|     v="${v//[0-9]/}"
 | |
|     [ -z "$v" ]
 | |
| }
 | |
| function is_yes() {
 | |
| # retourner vrai si $1 est une valeur "oui"
 | |
|     case "${1,,}" in
 | |
|     o|oui|y|yes|v|vrai|t|true|on) return 0;;
 | |
|     esac
 | |
|     isnum "$1" && [ "$1" -ne 0 ] && return 0
 | |
|     return 1
 | |
| }
 | |
| function yesval() {
 | |
| # normaliser une valeur vraie: si $1 est une valeur "oui", afficher 1, sinon
 | |
| # afficher une chaine vide
 | |
|     is_yes "$1" && echo 1
 | |
| }
 | |
| function setyesval() {
 | |
| # mettre la valeur normalisée de la valeur "oui" de $2 dans la variable $1
 | |
|     is_yes "$2" && set_var "$1" 1 || set_var "$1" ""
 | |
| }
 | |
| function normyesval() {
 | |
| # remplacer la valeur de la variable $1 par la valeur normalisée de sa valeur "oui"
 | |
| # Si $2 est non vide, prendre cette valeur plutôt que la valeur de la variable $1
 | |
|     is_yes "${2:-"${!1}"}" && set_var "$1" 1 || set_var "$1" ""
 | |
| }
 | |
| function normyesvals() {
 | |
| # remplacer les valeur des variables $1..* par les valeurs normalisées
 | |
| # respectives de leur valeur "oui"
 | |
|     local __nyv_yesvar
 | |
|     for __nyv_yesvar in "$@"; do
 | |
|         is_yes "${!__nyv_yesvar}" && set_var "$__nyv_yesvar" 1 || set_var "$__nyv_yesvar" ""
 | |
|     done
 | |
| }
 | |
| function is_no() {
 | |
| # retourner vrai si $1 est une valeur "non"
 | |
|     case "${1,,}" in
 | |
|     n|non|no|f|faux|false|off) return 0;;
 | |
|     esac
 | |
|     isnum "$1" && [ "$1" -eq 0 ] && return 0
 | |
|     return 1
 | |
| }
 | |
| function rawecho() {
 | |
| # afficher une valeur brute. contrairement à echo, ne pas reconnaitre les
 | |
| # options -e, -E, -n.
 | |
| # cette fonction est nécessaire pour pouvoir splitter et afficher des options de
 | |
| # ligne de commande.
 | |
|     local first
 | |
|     while [ "${1:0:1}" == "-" ]; do
 | |
|         echo_ -
 | |
|         first="${1:1}"; shift
 | |
|         set -- "$first" "$@"
 | |
|     done
 | |
|     echo "$*"
 | |
| }
 | |
| function rawecho_() {
 | |
|     local first
 | |
|     while [ "${1:0:1}" == "-" ]; do
 | |
|         echo_ -
 | |
|         first="${1:1}"; shift
 | |
|         set -- "$first" "$@"
 | |
|     done
 | |
|     echo_ "$*"
 | |
| }
 | |
| function quote_arg() {
 | |
| # Dans la chaine $1, remplacer \ par \\, " par \", $ par \$, ` par \`
 | |
| # Cela permet de quoter une chaine à mettre entre guillements
 | |
| # note: la protection de ! n'est pas effectuée, parce que le comportement du
 | |
| # shell est incohérent entre le shell interactif et les scripts. Pour une
 | |
| # version plus robuste, utiliser plutôt quote_sarg(), qui est malheureusement
 | |
| # plus lent, parce qu'il utilise un programme externe
 | |
|     local s="$1"
 | |
|     s="${s//\\/\\\\}"
 | |
|     s="${s//\"/\\\"}"
 | |
|     s="${s//\$/\\\$}"
 | |
|     s="${s//\`/\\\`}"
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quoted_arg() {
 | |
| # Dans la chaine $1, remplacer \ par \\, " par \" et $ par \$, et afficher la
 | |
| # chaine entourée de guillemets, si nécessaire
 | |
|     should_quote "$1" && echo "\"$(quote_arg "$1")\"" || quote_arg "$1"
 | |
| }
 | |
| function quote_in() {
 | |
| # Comme quote_arg pour une chaine lue sur stdin
 | |
|     sed 's/\\/\\\\/g
 | |
| s/"/\\"/g
 | |
| s/\$/\\$/g
 | |
| s/`/\\`/g'
 | |
| }
 | |
| function quote_sin() {
 | |
| # Pour la chaine lue sur stdin, remplacer ' par '\''. Cela permet de protéger une
 | |
| # chaine à mettre entre quotes
 | |
|     sed "s/'/'\\\\''/g"
 | |
| }
 | |
| function quote_sarg() {
 | |
| # Dans la chaine $1, remplacer ' par '\''. Cette fonction utilise quote_sin,
 | |
| # puisque le shell a des difficultés à faire le rechercher/remplacer approprié
 | |
|     quote_sin <<<"$1"
 | |
| }
 | |
| function quoted_sarg() {
 | |
| # Dans la chaine $1, remplacer ' par '\'', et afficher la chaine entourée de
 | |
| # quotes
 | |
|     echo "'$(quote_sarg "$1")'"
 | |
| }
 | |
| function quoted_args() {
 | |
| # Comme quoted_arg, mais tous les arguments sont quotés et affichés entourés de
 | |
| # guillemets, ce qui permet de construire des arguments d'une ligne de commande
 | |
|     local a s
 | |
|     for a in "$@"; do
 | |
|         s="${s:+$s }$(quoted_arg "$a")"
 | |
|     done
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quoted_sargs() {
 | |
| # Comme quoted_sarg, mais tous les arguments sont quotés et affichés entourés de
 | |
| # quotes, ce qui permet de construire des arguments d'une ligne de commande
 | |
|     local a s
 | |
|     for a in "$@"; do
 | |
|         s="${s:+$s }$(quoted_sarg "$a")"
 | |
|     done
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quote_awk() {
 | |
| # dans la chaine $1, remplacer \ par \\ et " par \". ceci est utile pour quoter
 | |
| # des valeur à insérer dans un script awk
 | |
|     local s="$1"
 | |
|     s="${s//\\/\\\\}"
 | |
|     s="${s//\"/\\\"}"
 | |
|     s="${s//
 | |
| /\\n}"
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quoted_awk() {
 | |
| # dans la chaine $1, remplacer \ par \\ et " par \" et afficher la
 | |
| # chaine entourée de guillemets. ceci est utile pour quoter
 | |
| # des valeur à insérer dans un script awk
 | |
|     rawecho "\"$(quote_awk "$1")\""
 | |
| }
 | |
| function quote_seds() {
 | |
| # Quoter la chaine $1, qui doit être utilisée comme chaine de recherche ou de
 | |
| # remplacement de grep, sed ou awk
 | |
|     local s="$1"
 | |
|     s="${s//\\/\\\\}"
 | |
|     s="${s//\//\\/}"
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quote_form() {
 | |
| # Dans la chaine $1, remplacer '%' par '%25', '+' par '%2B', '&' par '%26', '='
 | |
| # par '%3D', ' ' par '+'
 | |
|     local s="$1"
 | |
|     s="${s//\%/%25}"
 | |
|     s="${s//+/%2B}"
 | |
|     s="${s//&/%26}"
 | |
|     s="${s//=/%3D}"
 | |
|     s="${s// /+}"
 | |
|     rawecho "$s"
 | |
| }
 | |
| function quoted_form() {
 | |
| # Dans la chaine $1 qui est de la forme "name=value", remplacer dans name et
 | |
| # dans value '%' par '%25', '+' par '%2B', '&' par '%26', '=' par '%3D', ' ' par
 | |
| # '+'
 | |
|     local n="${1%%=*}"
 | |
|     if [ "$n" != "$1" ]; then
 | |
|         local v="${1#*=}"
 | |
|         rawecho "$(quote_form "$n")=$(quote_form "$v")"
 | |
|     else
 | |
|         quote_form "$1"
 | |
|     fi
 | |
| }
 | |
| function first_char() {
 | |
| # retourner le premier caractère de la chaine $1
 | |
|     rawecho "${1:0:1}"
 | |
| }
 | |
| function last_char() {
 | |
| # retourner le dernier caractère de la chaine $1
 | |
|     rawecho "${1:$((-1)):1}"
 | |
| }
 | |
| function first_chars() {
 | |
| # retourner tous les caractères de la chaine $1, excepté le dernier
 | |
|     rawecho "${1:0:$((${#1}-1))}"
 | |
| }
 | |
| function last_chars() {
 | |
| # retourner tous les caractères de la chaine $1, excepté le premier
 | |
|     rawecho "${1:1}"
 | |
| }
 | |
| function first_char_is() {
 | |
| # Tester si le premier caractère de la chaine $1 est $2
 | |
|     [ "${1:0:1}" == "$2" ]
 | |
| }
 | |
| function last_char_is() {
 | |
| # Tester si le dernier caractère de la chaine $1 est $2
 | |
|     [ "${1:$((-1)):1}" == "$2" ]
 | |
| }
 | |
| function beginswith() {
 | |
| # Tester si la chaine $1 commence par le wildcard $2
 | |
|     eval '[ "${1#'"$(quote_arg "$2")"'}" != "$1" ]'
 | |
| }
 | |
| function endswith() {
 | |
| # Tester si la chaine $1 se termine par le wildcard $2
 | |
|     eval '[ "${1%'"$(quote_arg "$2")"'}" != "$1" ]'
 | |
| }
 | |
| function splitfsep() {
 | |
| # Découper $1 de la forme "first[SEPsecond]" entre first, qui est placé dans la
 | |
| # variable $3(=first) et second, qui est placée dans la variable $4(=second). $2
 | |
| # est la valeur SEP. Le découpage est faite sur la *première* occurence de SEP.
 | |
|     if [[ "$1" == *"$2"* ]]; then
 | |
|         set_var "${3:-first}" "${1%%$2*}"
 | |
|         set_var "${4:-second}" "${1#*$2}"
 | |
|     else
 | |
|         set_var "${3:-first}" "$1"
 | |
|         set_var "${4:-second}"
 | |
|     fi        
 | |
| }
 | |
| function splitfsep2() {
 | |
| # Découper $1 de la forme "[firstSEP]second" entre first, qui est placé dans la
 | |
| # variable $3(=first) et second, qui est placée dans la variable $4(=second). $2
 | |
| # est la valeur SEP. Le découpage est faite sur la *première* occurence de SEP.
 | |
|     if [[ "$1" == *"$2"* ]]; then
 | |
|         set_var "${3:-first}" "${1%%$2*}"
 | |
|         set_var "${4:-second}" "${1#*$2}"
 | |
|     else
 | |
|         set_var "${3:-first}"
 | |
|         set_var "${4:-second}" "$1"
 | |
|     fi        
 | |
| }
 | |
| function splitlsep() {
 | |
| # Découper $1 de la forme "first[SEPsecond]" entre first, qui est placé dans la
 | |
| # variable $3(=first) et second, qui est placée dans la variable $4(=second). $2
 | |
| # est la valeur SEP. Le découpage est faite sur la *dernière* occurence de SEP.
 | |
|     if [[ "$1" == *"$2"* ]]; then
 | |
|         set_var "${3:-first}" "${1%$2*}"
 | |
|         set_var "${4:-second}" "${1##*$2}"
 | |
|     else
 | |
|         set_var "${3:-first}" "$1"
 | |
|         set_var "${4:-second}"
 | |
|     fi        
 | |
| }
 | |
| function splitlsep2() {
 | |
| # Découper $1 de la forme "[firstSEP]second" entre first, qui est placé dans la
 | |
| # variable $3(=first) et second, qui est placée dans la variable $4(=second). $2
 | |
| # est la valeur SEP. Le découpage est faite sur la *dernière* occurence de SEP.
 | |
|     if [[ "$1" == *"$2"* ]]; then
 | |
|         set_var "${3:-first}" "${1%$2*}"
 | |
|         set_var "${4:-second}" "${1##*$2}"
 | |
|     else
 | |
|         set_var "${3:-first}"
 | |
|         set_var "${4:-second}" "$1"
 | |
|     fi        
 | |
| }
 | |
| function splitvar() {
 | |
| # Découper $1 de la forme name[=value] entre le nom, qui est placé dans la
 | |
| # variable $2(=name) et la valeur, qui est placée dans la variable $3(=value)
 | |
|     splitfsep "$1" = "${2:-name}" "${3:-value}"
 | |
| }
 | |
| function splitname() {
 | |
| # Découper $1 de la forme basename[.ext] entre le nom de base du fichier, qui
 | |
| # est placé dans la variable $2(=basename) et l'extension, qui est placée dans
 | |
| # la variable $3(=ext)
 | |
| # Attention, si $1 est un chemin, le résultat risque d'être faussé. Par exemple,
 | |
| # 'splitname a.b/c' ne donne pas le résultat escompté.
 | |
|     splitlsep "$1" . "${2:-basename}" "${3:-ext}"
 | |
| }
 | |
| function splithost() {
 | |
| # Découper $1 de la forme hostname[.domain] entre le nom d'hôte, qui est placé
 | |
| # dans la variable $2(=hostname) et le domaine, qui est placée dans la variable
 | |
| # $3(=domain)
 | |
|     splitfsep "$1" . "${2:-hostname}" "${3:-domain}"
 | |
| }
 | |
| function splituserhost() {
 | |
| # Découper $1 de la forme [user@]host entre le nom de l'utilisateur, qui est placé
 | |
| # dans la variable $2(=user) et le nom d'hôte, qui est placée dans la variable
 | |
| # $3(=host)
 | |
|     splitfsep2 "$1" @ "${2:-user}" "${3:-host}"
 | |
| }
 | |
| function splitpair() {
 | |
| # Découper $1 de la forme first[:second] entre la première valeur, qui est placé
 | |
| # dans la variable $2(=src) et la deuxième valeur, qui est placée dans la variable
 | |
| # $3(=dest)
 | |
|     splitfsep "$1" : "${2:-src}" "${3:-dest}"
 | |
| }
 | |
| function splitproxy() {
 | |
| # Découper $1 de la forme http://[user:password@]host[:port]/ entre les valeurs
 | |
| # $2(=host), $3(=port), $4(=user), $5(=password)
 | |
|     local __sp_tmp __sp_host __sp_port __sp_creds __sp_user __sp_password
 | |
| 
 | |
|     __sp_tmp="${1#http://}"
 | |
|     if [[ "$__sp_tmp" == *@* ]]; then
 | |
|         __sp_creds="${__sp_tmp%%@*}"
 | |
|         __sp_tmp="${__sp_tmp#${__sp_creds}@}"
 | |
|         splitpair "$__sp_creds" __sp_user __sp_password
 | |
|     fi
 | |
|     __sp_tmp="${__sp_tmp%%/*}"
 | |
|     splitpair "$__sp_tmp" __sp_host __sp_port
 | |
|     [ -n "$__sp_port" ] || __sp_port=3128
 | |
| 
 | |
|     set_var "${2:-host}" "$__sp_host"
 | |
|     set_var "${3:-port}" "$__sp_port"
 | |
|     set_var "${4:-user}" "$__sp_user"
 | |
|     set_var "${5:-password}" "$__sp_password"
 | |
| }
 | |
| function spliturl() {
 | |
| # Découper $1 de la forme scheme://[user:password@]host[:port]/path entre les
 | |
| # valeurs $2(=scheme), $3(=user), $4(=password), $5(=host), $6(=port), $7(=path)
 | |
|     local __su_tmp __su_scheme __su_creds __su_user __su_password __su_host __su_port __su_path
 | |
| 
 | |
|     __su_scheme="${1%%:*}"
 | |
|     __su_tmp="${1#${__su_scheme}://}"
 | |
|     if [[ "$__su_tmp" == */* ]]; then
 | |
|         __su_path="${__su_tmp#*/}"
 | |
|         __su_tmp="${__su_tmp%%/*}"
 | |
|     fi
 | |
|     if [[ "$__su_tmp" == *@* ]]; then
 | |
|         __su_creds="${__su_tmp%%@*}"
 | |
|         __su_tmp="${__su_tmp#${__su_creds}@}"
 | |
|         splitpair "$__su_creds" __su_user __su_password
 | |
|     fi
 | |
|     splitpair "$__su_tmp" __su_host __su_port
 | |
|     if [ -z "$__su_port" ]; then
 | |
|         [ "$__su_scheme" == "http" ] && __su_port=80
 | |
|         [ "$__su_scheme" == "https" ] && __su_port=443
 | |
|         [ "$__su_scheme" == "ftp" ] && __su_port=21
 | |
|     fi
 | |
| 
 | |
|     set_var "${2:-scheme}" "$__su_scheme"
 | |
|     set_var "${3:-user}" "$__su_user"
 | |
|     set_var "${4:-password}" "$__su_password"
 | |
|     set_var "${5:-host}" "$__su_host"
 | |
|     set_var "${6:-port}" "$__su_port"
 | |
|     set_var "${7:-path}" "$__su_path"
 | |
| }
 | |
| 
 | |
| ## variables scalaires
 | |
| function set_var_cmd() {
 | |
|     echo "$1=$(quoted_arg "$2")"
 | |
| }
 | |
| function set_var() {
 | |
|     eval "$(set_var_cmd "$@")"
 | |
| }
 | |
| function set_var_literal() {
 | |
|     eval "$1=$2"
 | |
| }
 | |
| 
 | |
| ## variables tableaux
 | |
| function set_array_cmd() {
 | |
| # Afficher la commande permettant d'initialiser le tableau $1 avec les valeurs:
 | |
| # soit du tableau $2, soit de $3..$n si $2=="@"
 | |
| # S'il n'y a que l'argument $1, alors afficher la commande permettant de
 | |
| # recréer le tableau $1
 | |
|     [ $# -eq 1 ] && set -- "$1" "$1"
 | |
|     local __sac_s __sac_v __sac_f
 | |
|     __sac_s="$1=("; shift
 | |
|     if [ "$1" == "@" ]; then
 | |
|         shift
 | |
|     else
 | |
|         eval "set -- \"\${$1[@]}\""
 | |
|     fi
 | |
|     __sac_f=1
 | |
|     for __sac_v in "$@"; do
 | |
|         [ -n "$__sac_f" ] && __sac_f= || __sac_s="$__sac_s "
 | |
|         __sac_s="$__sac_s$(quoted_arg "$__sac_v")"
 | |
|     done
 | |
|     __sac_s="$__sac_s)"
 | |
|     echo "$__sac_s"
 | |
| }
 | |
| function set_array() {
 | |
| # Soit $1 un tableau à créer. Si $2=="@", créer le tableau $1 avec les valeurs
 | |
| # $3..$n. Sinon, créer le tableau $1 avec les valeurs du tableau $2.
 | |
| # Cette fonction n'existe que comme un pendant de set_var(), mais le véritable
 | |
| # intérêt est la fonction set_array_cmd(). cf array_copy() pour une version plus
 | |
| # efficace de la copie de tableaux
 | |
|     eval "$(set_array_cmd "$@")"
 | |
| }
 | |
| 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 la valeur $2 au tableau dont le nom est $1
 | |
|     eval "$1=(\"\${$1[@]}\" \"$(quote_arg "$2")\")"
 | |
| }
 | |
| function array_ins() {
 | |
| # insérer la valeur $2 au début du tableau dont le nom est $1
 | |
|     eval "$1=(\"$(quote_arg "$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" != '"$(quoted_arg "$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" == '"$(quoted_arg "$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" == '"$(quoted_arg "$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" == '"$(quoted_arg "$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" == '"$(quoted_arg "$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 "rawecho \"\${$1[@]:0:1}\""
 | |
| }
 | |
| function last_value() {
 | |
| # retourner la dernière valeur du tableau $1
 | |
|     eval "rawecho \"\${$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=($(<<<"$2" stripnl | 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=($(<<<"$2" stripnl | 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=($(<<<"$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=($(<<<"$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
 | |
|         rawecho "$__aj_j"
 | |
|     elif [ "$__aj_an" != "\$@" -a -n "$3" ]; then
 | |
|         rawecho "$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="$(($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"
 | |
|         ;;
 | |
|     *)
 | |
|         rawecho "$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
 | |
|     rawecho "$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="${path/#$HOME/~}"
 | |
| 
 | |
|     rawecho "$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
 | |
|         rawecho "${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 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"
 | |
|     set_var "$__sw_dd" "$__sw_d"
 | |
|     set_var "$__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 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
 | |
|                 rawecho "$reldir$file"
 | |
|             else
 | |
|                 rawecho "$basedir/$file"
 | |
|             fi
 | |
|             break
 | |
|         fi
 | |
|         shift
 | |
|     done
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| ## 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() {
 | |
| # test si $2 n'existe pas ou si $1 est différent de $2
 | |
|     if [ -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
 | |
|     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)
 | |
| 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 gitexclude=/.git/
 | |
|         if [ "${src%/}" == "$src" ]; then
 | |
|             gitexclude="/$(basename -- "$src")$gitexclude"
 | |
|         fi
 | |
|         rsync -a ${__CPNOVCS_RSYNC_SLOW:+-c} --exclude CVS/ --exclude .svn/ --exclude "$gitexclude" "${__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 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 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
 | |
|         rawecho "$f"
 | |
|     done
 | |
|     cd "$curdir"
 | |
| }
 | |
| function __la_cmd() {
 | |
|     [ -n "$*" ] || set '*'
 | |
|     local arg
 | |
|     local cmd="/bin/ls -1d"
 | |
|     for arg in "$@"; do
 | |
|         cmd="$cmd $(quote_arg "$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" ] && rawecho "$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" ] && rawecho "$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_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
 | |
|     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 {
 | |
|     print $0
 | |
|   }
 | |
| }'
 | |
| }
 | |
| function get_archive_version() {
 | |
| # Obtenir la valeur probable de la version de l'application ou du framework
 | |
| # contenu dans l'archive $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-z]?)$/, 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=$(quoted_arg "$__estack")
 | |
| __tlevel=$(quoted_args "$__tlevel")
 | |
| export __estack __tlevel
 | |
| exec ${BASH:-/bin/sh} $(quoted_args "$@")"
 | |
| 
 | |
|     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..*
 | |
|     local userhost user host path
 | |
|     for userhost in "$@"; do
 | |
|         splitfsep "$userhost" : userhost path
 | |
|         splituserhost "$userhost" user host
 | |
|         [ "$MYHOSTNAME" == "${host%%.*}" ] && 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
 | |
| 
 | |
|         enote "Vous pouvez tenter de relancer le script sur ${userhosts[0]}, mais 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"
 | |
|         if ask_yesno "Voulez-vous tenter de relancer le script sur l'hôte distant?" N; 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="$(quoted_args cd "$path"); "
 | |
|             cmd="$cmd$(quoted_args "$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 quote_html(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 quote_value(s) {'"
 | |
|   gsub(/'/, \"'\\\\''\", s)
 | |
|   return \"'\" s \"'\"
 | |
| "'}
 | |
| function quoted_values(             i, line) {
 | |
|   line = ""
 | |
|   for (i = 1; i <= NF; i++) {
 | |
|     if (i > 1) line = line " "
 | |
|     line = line quote_value($i)
 | |
|   }
 | |
|   return line
 | |
| }
 | |
| function quote_subrepl(s) {
 | |
|   gsub(/\\/, "\\\\", s)
 | |
|   gsub(/&/, "\\\\&", s)
 | |
|   return s
 | |
| }
 | |
| function quote_grep(s) {
 | |
|   gsub(/[[\\.^$*]/, "\\\\&", s)
 | |
|   return s
 | |
| }
 | |
| function quote_egrep(s) {
 | |
|   gsub(/[[\\.^$*+?()|{]/, "\\\\&", s)
 | |
|   return s
 | |
| }
 | |
| function quote_sql(s) {'"
 | |
|   gsub(/'/, \"''\", s)
 | |
|   return \"'\" s \"'\"
 | |
| "'}
 | |
| function unquote_mysqlcsv(s) {
 | |
|   gsub(/\\n/, "\n", s)
 | |
|   gsub(/\\t/, "\t", s)
 | |
|   gsub(/\\0/, "\0", s)
 | |
|   gsub(/\\\\/, "\\", s)
 | |
|   return s
 | |
| }
 | |
| 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
 | |
|   for (i = 1; i <= size; i++) {
 | |
|     dest[i] = ""
 | |
|   }
 | |
| }
 | |
| function mkindices(values, indices,        i, j) {
 | |
|   array_new(indices)
 | |
|   j = 1
 | |
|   for (i in values) {
 | |
|     indices[j++] = int(i)
 | |
|   }
 | |
|   return asort(indices)
 | |
| }
 | |
| 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) {
 | |
|   if (i == 0) return
 | |
|   l = length(dest)
 | |
|   while (i < l) {
 | |
|     dest[i] = dest[i + 1]
 | |
|     i = i + 1
 | |
|   }
 | |
|   delete dest[l]
 | |
| }
 | |
| function array_del(dest, value,              i) {
 | |
|   do {
 | |
|     i = key_index(value, dest)
 | |
|     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 i
 | |
|     }
 | |
|   } else {
 | |
|     for (i in values) {
 | |
|       if (values[i] == value) return 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 = 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) {
 | |
|     while (length(fields) < nbfields) {
 | |
|       fields[length(fields) + 1] = ""
 | |
|     }
 | |
|   }
 | |
|   return length(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) {
 | |
|   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) {
 | |
|       while (length(fields) < nbfields) {
 | |
|         fields[length(fields) + 1] = ""
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   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=var.
 | |
| # 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 ulib/awk.
 | |
| 
 | |
|     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($(quoted_awk "$__ad_value") + 0)"
 | |
|                 elif [ -n "$__ad_str" ]; then
 | |
|                     # valeur chaine
 | |
|                     echo "$__ad_name = $(quoted_awk "$__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 = $(quoted_awk "$__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]=$(quoted_awk "$__ad_arg")"
 | |
|                     __ad_i=$(($__ad_i + 1))
 | |
|                 done
 | |
|                 eval "echo \"\${__ad_name}_count = \${#$__ad_value}\""
 | |
|             fi
 | |
|             shift
 | |
|         done
 | |
|         echo "}"
 | |
|         for __ad_arg in "$@"; do
 | |
|             rawecho "$__ad_arg"
 | |
|         done
 | |
|     fi
 | |
| }
 | |
| 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[@]}")"
 | |
|     #edebug "Script awkrun: $__ar_script"
 | |
|     awk "$__ar_script" "${__ar_args[@]}"
 | |
| }
 | |
| function cawkrun() { LANG=C lawkrun "$@"; }
 | |
| function awkrun() { LANG=C lawkrun "$@"; }
 | |
| function parse_opts() {
 | |
| # Analyser des arguments. Cette fonction doit être appelée avec une description
 | |
| # des options à analyser, suivie des arguments proprement dits. En fonction des
 | |
| # options rencontrées, certaines variables sont mises à jour.
 | |
| # Les arguments de cette fonction sont donc de la forme 'optdescs -- args'
 | |
| 
 | |
| # optdescs est une suite d'arguments d'une des formes suivantes: 'opt,opt... var',
 | |
| # 'opt,opt... $cmd', '@ var', '+', '-'
 | |
| # - Dans la forme 'opt var[=value]', opt est une description d'option, var un
 | |
| # nom de variable à mettre à jour, et value une valeur éventuelle pour les
 | |
| # options sans argument. Si plusieurs options sont mentionnées, séparées par des
 | |
| # virgules, alors tous les options partagent les mêmes paramètres.
 | |
| # opt peut être de la forme '-o' ou '--longopt' pour des options sans
 | |
| # arguments. Dans ce cas, var obtient le nombre de fois que l'option est
 | |
| # mentionnée ('' pour aucune mention, '1' pour une seule mention, etc.), sauf si
 | |
| # on utilise la forme var=value, auquel cas la variable obtient la valeur value,
 | |
| # et le nombre d'occurences de l'option n'est pas compté.
 | |
| # Avec la forme '-o:' ou '--longopt:', l'option prend un argument obligatoire.
 | |
| # Avec la forme '-o::' ou '--longopt::', l'option prend un argument facultatif
 | |
| # (dans ce cas, la valeur de l'option sur la ligne de commande doit
 | |
| # obligatoirement être collée à l'option.)
 | |
| # Si ces options sont mentionnées plusieurs fois sur la ligne de commande, alors
 | |
| # la variable de destination est un tableau qui contient toutes les valeurs.
 | |
| # Le traitement de la valeur d'une variable dépend de la forme utilisée.
 | |
| # - Avec une option sans argument, le comportement est celui décrit ci-dessus.
 | |
| # - Avec une option qui prend des arguments, la forme '-o: var' provoque
 | |
| # l'effacement de la variable de destination, puisqu'elle doit devenir un
 | |
| # tableau qui contiendra toutes les valeurs mentionnées dans les options. La
 | |
| # forme '-o: var=' empêche l'effacement de la variable de destination, qui garde
 | |
| # sa valeur par défaut. De plus, elle ne sera pas un tableau, et l'on ne
 | |
| # supporte pas les valeurs multiples.
 | |
| # - Dans la forme 'opt $cmd', la commande cmd est executée avec eval *dès* que
 | |
| # l'option est rencontrée. La variable option_ contient l'option, e.g. '-o' ou
 | |
| # '--longopt'. Le cas échéant, la variable value_ contient la valeur de
 | |
| # l'option. La fonction 'set@ NAME' met à jour la variable NAME, soit en lui
 | |
| # donnant la valeur $value_, soit en l'incrémentant, suivant le type d'option.
 | |
| # La fonction 'inc@ NAME' incrémente la variable NAME, 'res@ NAME [VALUE]'
 | |
| # initialise la variable à la valeur VALUE, 'add@ NAME [VALUE]' met à jour le
 | |
| # tableau NAME, en lui ajoutant la valeur VALUE. Par défaut, VALUE vaut $value_
 | |
| # - Dans la forme '@ var', var est un nom de tableau qui est initialisé avec le
 | |
| # reste des arguments.
 | |
| # - Avec les caractères '-' ou '+', l'on influe sur la méthode d'analyse. Par
 | |
| # défaut, les options sont valides n'importe ou sur la ligne de commande. Avec
 | |
| # '+', l'analyse s'arrête au premier argument qui n'est pas une option. Avec
 | |
| # '-', les options sont valides n'importe ou sur la ligne de commande, mais les
 | |
| # arguments ne sont pas réordonnés, et apparaissent dans l'ordre de leur
 | |
| # mention.
 | |
| # Si opt est définie plusieurs fois, la dernière définition est celle qui est
 | |
| # retenue, e.g. dans l'exemple suivant, l'option -o prend une valeur et met à
 | |
| # jour la variable second:
 | |
| #     parse_opts -o,--longo first=1 -o: second= ....
 | |
| 
 | |
| # Si une erreur se produit pendant l'analyse, retourner 1. Si '@ var' est
 | |
| # spécifié, insérer le texte de l'erreur comme unique élément du tableau var.
 | |
| # Une suggestion d'utilisation est donc celle-ci:
 | |
| #     parse_opts ... @ args -- "$@" && set -- "${args[@]}" || die "$args"
 | |
| 
 | |
|     # D'abord, parser et normaliser les options
 | |
|     # options_ contient la liste des options (-o --longo...)
 | |
|     # names_ contient la liste des variables qu'il faut mettre à jour (name name...)
 | |
|     # flags_ contient la liste des flags pour les options: '' pour une option
 | |
|     # simple, ':' pour option avec argument obligatoire, '::' pour option avec
 | |
|     # argument facultatif
 | |
|     local -a options_ names_ flags_ destargs_
 | |
|     local opts_ longopts_
 | |
|     __po_parse_optdescs "$@" || shift $?
 | |
|     local args_
 | |
|     if args_="$(__po_check_options "$@")"; then
 | |
|         eval "set -- $args_"
 | |
|         __po_process_options "$@"
 | |
|     else
 | |
|         [ -n "$destargs_" ] && set_var "$destargs_" "$args_"
 | |
|         return 1
 | |
|     fi
 | |
| }
 | |
| function __po_parse_optdescs() {
 | |
|     # Parser et normaliser la description des options valides. A l'issue de
 | |
|     # l'appel de cette méthode, 3 tableaux sont initialisés, dont chaque
 | |
|     # position correspond à une option:
 | |
|     # - options_ contient la liste des options valides e.g. (-o --longo...)
 | |
|     # - names_ contient la liste des variables qu'il faut mettre à jour pour ces
 | |
|     # options e.g. (name name...)
 | |
|     # - flags_ contient la liste des flags pour les options: '' pour une option
 | |
|     # simple, ':' pour option avec argument obligatoire, '::' pour option avec
 | |
|     # argument facultatif, '$' pour une commande à lancer au lieu d'une variable
 | |
|     # à mettre à jour.
 | |
|     # De plus, les variables suivantes sont initialisées:
 | |
|     # - destargs_ obtient la variable qui doit être initialisée avec le reste
 | |
|     # des arguments.
 | |
|     # - opts_ et longopts_ sont les arguments à utiliser pour la commande getopt
 | |
|     # du package util-linux
 | |
|     # Retourner le nombre d'arguments à shifter
 | |
|     local -a optdescs_
 | |
|     local optdesc_ option_ name_ flag_ value_
 | |
|     local shift_
 | |
|     local i_ count_
 | |
| 
 | |
|     let shift_=0
 | |
|     while [ -n "$1" ]; do
 | |
|         if [ "$1" == -- ]; then
 | |
|             shift
 | |
|             let shift_=$shift_+1
 | |
|             break
 | |
|         elif [ "$1" == "-" -o "$1" == "+" ]; then
 | |
|             # annuler les précédentes options + ou -
 | |
|             if [ "${opts_#+}" != "$opts_" ]; then
 | |
|                 opts_="${opts_#+}"
 | |
|             elif  [ "${opts_#-}" != "$opts_" ]; then
 | |
|                 opts_="${opts_#-}"
 | |
|             fi
 | |
|             # puis rajouter l'option
 | |
|             opts_="$1$opts_"
 | |
|             shift
 | |
|             let shift_=$shift_+1
 | |
|         elif [ "$1" == "@" ]; then
 | |
|             destargs_="$2"
 | |
|             shift; shift
 | |
|             let shift_=$shift_+2
 | |
|         elif [[ "$1" == --* ]] || [[ "$1" == -* ]]; then
 | |
|             array_split optdescs_ "$1" ","
 | |
|             for optdesc_ in "${optdescs_[@]}"; do
 | |
|                 if [[ "$2" == \$* ]]; then
 | |
|                     name_="$2"
 | |
|                     if [[ "$optdesc_" == *:: ]]; then
 | |
|                         option_="${optdesc_%::}"
 | |
|                         flag_='::$'
 | |
|                     elif [[ "$optdesc_" == *: ]]; then
 | |
|                         option_="${optdesc_%:}"
 | |
|                         flag_=':$'
 | |
|                     else
 | |
|                         option_="$optdesc_"
 | |
|                         flag_='$'
 | |
|                     fi
 | |
|                 elif [[ "$optdesc_" == *:: ]]; then
 | |
|                     option_="${optdesc_%::}"
 | |
|                     if [[ "$2" == *=* ]]; then
 | |
|                         # la valeur mentionnée est toujours ignorée, mais la
 | |
|                         # valeur de la variable n'est pas écrasée
 | |
|                         name_="${2%%=*}="
 | |
|                     else
 | |
|                         name_="$2"
 | |
|                         array_new "$name_"
 | |
|                     fi
 | |
|                     flag_=::
 | |
|                 elif [[ "$optdesc_" == *: ]]; then
 | |
|                     option_="${optdesc_%:}"
 | |
|                     if [[ "$2" == *=* ]]; then
 | |
|                         # la valeur mentionnée est toujours ignorée, mais la
 | |
|                         # valeur de la variable n'est pas écrasée
 | |
|                         name_="${2%%=*}="
 | |
|                     else
 | |
|                         name_="$2"
 | |
|                         array_new "$name_"
 | |
|                     fi
 | |
|                     flag_=:
 | |
|                 else
 | |
|                     option_="$optdesc_"
 | |
|                     name_="$2"
 | |
|                     flag_=
 | |
|                 fi
 | |
| 
 | |
|                 if i_="$(array_find options_ "$option_")"; then
 | |
|                     # supprimer l'ancienne occurence
 | |
|                     options_=("${options_[@]:0:$i_}" "${options_[@]:$(($i_ + 1))}")
 | |
|                     names_=("${names_[@]:0:$i_}" "${names_[@]:$(($i_ + 1))}")
 | |
|                     flags_=("${flags_[@]:0:$i_}" "${flags_[@]:$(($i_ + 1))}")
 | |
|                 fi
 | |
|                 options_=("${options_[@]}" "$option_")
 | |
|                 names_=("${names_[@]}" "$name_")
 | |
|                 flags_=("${flags_[@]}" "$flag_")
 | |
|             done
 | |
|             shift; shift
 | |
|             let shift_=$shift_+2
 | |
|         else
 | |
|             break
 | |
|         fi
 | |
|     done
 | |
| 
 | |
|     i_=0
 | |
|     count_=${#options_[*]}
 | |
|     while [ $i_ -lt $count_ ]; do
 | |
|         option_="${options_[$i_]}"
 | |
|         flag_="${flags_[$i_]}"
 | |
|         i_=$(($i_ + 1))
 | |
| 
 | |
|         # pour construire longopts_ et opts_, enlever $ de flag
 | |
|         flag_="${flag_%$}"
 | |
|         if [[ "$option_" == --* ]]; then
 | |
|             longopts_="${longopts_:+$longopts_,}${option_#--}$flag_"
 | |
|         elif [[ "$option_" == -* ]]; then
 | |
|             opts_="$opts_${option_#-}$flag_"
 | |
|         fi
 | |
|     done
 | |
| 
 | |
|     return $shift_
 | |
| }
 | |
| function __po_check_options() {
 | |
|     # vérifier la validité des options mentionnées dans les arguments
 | |
|     # Si les options sont valides, retourner 0 et afficher la chaine des
 | |
|     # arguments traitées.
 | |
|     # Sinon, retourner 1 et initialiaser la variable $destargs_ avec le message
 | |
|     # d'erreur.
 | |
|     local -a getopt_args_
 | |
|     getopt_args_=(-o "$opts_" ${longopts_:+-l "$longopts_"} -- "$@")
 | |
|     local args_
 | |
|     if args_="$(getopt -q "${getopt_args_[@]}")"; then
 | |
|         rawecho "$args_"
 | |
|         return 0
 | |
|     else
 | |
|         # relancer la commande pour avoir le message d'erreur
 | |
|         LANG=C getopt "${getopt_args_[@]}" 2>&1 1>/dev/null
 | |
|         return 1
 | |
|     fi
 | |
| }
 | |
| function __po_process_options() {
 | |
|     # Traiter les options
 | |
|     while [ -n "$1" ]; do
 | |
|         if [ "$1" == -- ]; then
 | |
|             shift
 | |
|             break
 | |
|         elif [[ "$1" == -* ]]; then
 | |
|             local i_
 | |
|             let i_=0
 | |
|             for option_ in "${options_[@]}"; do
 | |
|                 [ "$1" == "${options_[$i_]}" ] && break
 | |
|                 let i_=$i_+1
 | |
|             done
 | |
|             name_="names_[$i_]"; name_="${!name_}"
 | |
|             flag_="flags_[$i_]"; flag_="${!flag_}"
 | |
|             function inc@ { eval "let $1=\$$1+1"; }
 | |
|             function res@ { set_var "$1" "${value_:-$2}"; }
 | |
|             function add@ { array_add "$1" "${value_:-$2}"; }
 | |
|             if [ -z "$name_" ]; then
 | |
|                 # option non reconnue. ce cas aurait dû être traité par
 | |
|                 # __po_check_options.
 | |
|                 ewarn "$1: option non reconnue, elle sera ignorée"
 | |
|             elif [[ "$flag_" == *\$ ]]; then
 | |
|                 if [[ "$flag_" == :* ]]; then
 | |
|                     value_="$2"; shift
 | |
|                     function set@ { res@ "$@"; }
 | |
|                 else
 | |
|                     value_=
 | |
|                     function set@ { inc@ "$@"; }
 | |
|                 fi
 | |
|                 eval "${name_#\$}"
 | |
|             elif [ "$flag_" == "" ]; then
 | |
|                 if [[ "$name_" == *=* ]]; then
 | |
|                     set_var "${name_%%=*}" "${name_#*=}"
 | |
|                 else
 | |
|                     inc@ "$name_"
 | |
|                 fi
 | |
|             elif [ "$flag_" == ":" -o "$flag_" == "::" ]; then
 | |
|                 value_="$2"; shift
 | |
|                 if [ "${name_%=}" != "$name_" ]; then
 | |
|                     set_var "${name_%=}" "$value_"
 | |
|                 elif [[ "$name_" == *=* ]]; then
 | |
|                     set_var "${name_%%=*}" "${name_#*=}"
 | |
|                 else
 | |
|                     array_add "$name_" "$value_"
 | |
|                 fi
 | |
|             fi
 | |
|         else
 | |
|             break
 | |
|         fi
 | |
|         shift
 | |
|     done
 | |
|     unset -f inc@ res@ add@ set@
 | |
|     [ -n "$destargs_" ] &&
 | |
|     set_array "$destargs_" @ "$@"
 | |
|     return 0
 | |
| }
 | |
| function __genparse_shortopt() {
 | |
|     local LC_COLLATE=C
 | |
|     local shortopt="${1//[^A-Z]}"
 | |
|     shortopt="${shortopt:0:1}"
 | |
|     shortopt="${shortopt,,}"
 | |
|     [ -n "$shortopt" ] && echo "$shortopt"
 | |
| }
 | |
| HELP_DESC=
 | |
| HELP_USAGE=
 | |
| HELP_OPTIONS=
 | |
| function genparse() {
 | |
| # Afficher une ligne de commande à évaluer pour simplifier l'utilisation de
 | |
| # parse_opts(). Une fonction display_help() par défaut est définie et les
 | |
| # options appropriées de parse_opts sont utilisées pour reconnaître les options
 | |
| # spécifiées par les arguments.
 | |
| # Cette fonction peut être utilisée de cette manière:
 | |
| #     HELP_DESC=...
 | |
| #     HELP_ARG_DESC=... # pour chaque arg
 | |
| #     eval "$(genparse [args...])"
 | |
| # D'autres variables peuvent être définies: HELP_USAGE, HELP_OPTIONS,
 | |
| # HELP_ARG_OPTION. Consulter le source pour connaitre leur utilisation
 | |
| # Les arguments de cette fonction sont de la forme 'sansarg' pour une option
 | |
| # simple qui ne prend pas d'argument ou 'avecarg=[default-value]' pour une
 | |
| # option qui prend un argument. Les options générées sont des options
 | |
| # longues. En l'occurence, les options générées sont respectivement '--sansarg'
 | |
| # et '--avecarg:'
 | |
| # Les variables et les options sont toujours en minuscule. Pour les variables,
 | |
| # le caractère '-' est remplacé par '_'. Si une option contient une lettre en
 | |
| # majuscule, l'option courte correspondante à cette lettre sera aussi reconnue.
 | |
| # Par exemple, la commande suivante:
 | |
| #     genparse Force enCoding=utf-8 input= long-Option=
 | |
| # affichera ceci:
 | |
| #     function display_help() {
 | |
| #         [ -n "$HELP_USAGE" ] || HELP_USAGE="USAGE
 | |
| #         $scriptname [options]"
 | |
| #         [ -n "$HELP_OPTIONS" ] || HELP_OPTIONS="OPTIONS
 | |
| #     ${HELP_FORCE_OPTION:-    -f, --force${HELP_FORCE_DESC:+
 | |
| #             $HELP_FORCE_DESC}}
 | |
| #     ${HELP_ENCODING_OPTION:-    -c, --encoding VALUE${HELP_ENCODING_DESC:+
 | |
| #             ${HELP_ENCODING_DESC}}}
 | |
| #     ${HELP_INPUT_OPTION:-    --input VALUE${HELP_INPUT_DESC:+
 | |
| #             ${HELP_INPUT_DESC}}}
 | |
| #     ${HELP_LONG_OPTION_OPTION:-    -o, --long-option VALUE${HELP_LONG_OPTION_DESC:+
 | |
| #             ${HELP_LONG_OPTION_DESC}}}"
 | |
| #         uecho "${HELP_DESC:+$HELP_DESC
 | |
| #
 | |
| #     }$HELP_USAGE${HELP_OPTIONS:+
 | |
| #
 | |
| #     $HELP_OPTIONS}"
 | |
| #     }
 | |
| #
 | |
| #     force=
 | |
| #     encoding=utf-8
 | |
| #     input=""
 | |
| #     long_option=""
 | |
| #     parse_opts "${PRETTYOPTS[@]}" \
 | |
| #         --help '$exit_with display_help' \
 | |
| #         -f,--force force=1 \
 | |
| #         -c:,--encoding: encoding= \
 | |
| #         --input: input= \
 | |
| #         -o:,--long-option: long_option= \
 | |
| #         @ args -- "$@" && set -- "${args[@]}" || die "$args"
 | |
| 
 | |
|     local -a names descs vars options
 | |
|     local i desc var option name value shortopt
 | |
| 
 | |
|     # analyser les arguments
 | |
|     for var in "$@"; do
 | |
|         if [[ "$var" == *=* ]]; then
 | |
|             splitvar "$var" name value
 | |
|             shortopt="$(__genparse_shortopt "$name")"
 | |
|             option="${name,,}"
 | |
|             name="${option//-/_}"
 | |
|             array_add names "$name"
 | |
|             array_add descs "${shortopt:+-$shortopt, }--$option VALUE"
 | |
|             array_add vars "$(set_var_cmd "$name" "$value")"
 | |
|             array_add options "${shortopt:+-$shortopt:,}--$option: $name="
 | |
|         else
 | |
|             name="$var"
 | |
|             shortopt="$(__genparse_shortopt "$name")"
 | |
|             option="${name,,}"
 | |
|             name="${option//-/_}"
 | |
|             array_add names "$name"
 | |
|             array_add descs "${shortopt:+-$shortopt, }--$option"
 | |
|             array_add vars "$name="
 | |
|             array_add options "${shortopt:+-$shortopt,}--$option $name=1"
 | |
|         fi
 | |
|     done
 | |
| 
 | |
|     # afficher la commande parse_opts
 | |
|     echo -n 'function display_help() {
 | |
|     [ -n "$HELP_USAGE" ] || HELP_USAGE="USAGE
 | |
|     $scriptname'
 | |
|     [ -n "$descs" ] && echo -n ' [options]'
 | |
|     echo '"'
 | |
|     if [ -n "$descs" ]; then
 | |
|         echo -n '    [ -n "$HELP_OPTIONS" ] || HELP_OPTIONS="OPTIONS'
 | |
|         i=0
 | |
|         while [ $i -lt ${#descs[*]} ]; do
 | |
|             name="${names[$i]}"
 | |
|             desc="${descs[$i]}"
 | |
|             echo -n "
 | |
| \${HELP_${name^^}_OPTION:-    $desc\${HELP_${name^^}_DESC:+
 | |
|         \${HELP_${name^^}_DESC//
 | |
| /
 | |
|         }}}"
 | |
|             i=$(($i + 1))
 | |
|         done
 | |
|         echo '"'
 | |
|     fi
 | |
|     echo '    uecho "${HELP_DESC:+$HELP_DESC
 | |
| 
 | |
| }$HELP_USAGE${HELP_OPTIONS:+
 | |
| 
 | |
| $HELP_OPTIONS}"
 | |
| }
 | |
| '
 | |
|     for var in "${vars[@]}"; do
 | |
|         echo "$var"
 | |
|     done
 | |
|     echo 'parse_opts "${PRETTYOPTS[@]}" \'
 | |
|     echo '    --help '\''$exit_with display_help'\'' \'
 | |
|     for option in "${options[@]}"; do
 | |
|         echo "    $option \\"
 | |
|     done
 | |
|     echo '    @ args -- "$@" && set -- "${args[@]}" || die "$args"'
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| ## 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
 | |
|     [ -n "$*" ] || 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 echo_() {
 | |
|     echo -n "$*"
 | |
| }
 | |
| 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() {
 | |
|     [ -n "$*" ] && eerror "$@"; exit 1
 | |
| }
 | |
| function exit_with {
 | |
|     [ -n "$*" ] && "$@"; exit $?
 | |
| }
 | |
| function die_with {
 | |
|     [ -n "$1" ] && eerror "$1"; shift; [ -n "$*" ] && "$@"; exit 1
 | |
| }
 | |
| 
 | |
| 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
 | |
|         rawecho "$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
 | |
|         rawecho_ "$src"
 | |
|     else
 | |
|         rawecho_ "$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
 | |
|         set_var "$__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
 | |
|     [ -n "$*" ] || 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
 | |
|         rawecho "$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() { __estep "$@"; }
 | |
| function __estepw() { __estep "$@"; }
 | |
| function __estepn() { __estep "$@"; }
 | |
| function __estepi() { __estep "$@"; }
 | |
| function __estep_() { tooenc_ "$(__edate)${__tlevel}* $(__indent "$1")"; }
 | |
| function __estepe_() { __estep_ "$@"; }
 | |
| function __estepw_() { __estep_ "$@"; }
 | |
| function __estepn_() { __estep_ "$@"; }
 | |
| function __estepi_() { __estep_ "$@"; }
 | |
| 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 rawecho "$__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 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 [ -n "$*" ]; 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 [ -n "$*" ]; 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 && [ -n "$*" ] && __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 && [ -n "$*" ] && __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 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
 | |
|             tooenc_ "$message" 1>&2
 | |
|         else
 | |
|             tooenc_ "Voulez-vous continuer?" "$UTF8" 1>&2
 | |
|         fi
 | |
|         tooenc_ " $prompt " "$UTF8" 1>&2
 | |
|         uread r
 | |
|         is_yes "${r:-$default}"
 | |
|     else
 | |
|         is_yes "$default"
 | |
|     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_opts=()
 | |
|     __rv_read "$@"
 | |
| }
 | |
| function read_password() {
 | |
|     local -a __rv_opts
 | |
|     __rv_opts=(-s)
 | |
|     __rv_read "$@"
 | |
|     echo ""
 | |
| }
 | |
| 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
 | |
|         set_var "$__rv_v" "$__rv_d"
 | |
|         return 0
 | |
|     fi
 | |
| 
 | |
|     eflush
 | |
|     local __rv_r
 | |
|     while true; do
 | |
|         if [ -n "$__rv_msg" ]; then
 | |
|             tooenc_ "$__rv_msg" 1>&2
 | |
|         else
 | |
|             tooenc_ "Entrez la valeur" "$UTF8" 1>&2
 | |
|         fi
 | |
|         [ -n "$__rv_d" ] && tooenc_ " [$__rv_d]" 1>&2
 | |
|         tooenc_ ": " "$UTF8" 1>&2
 | |
|         uread "${__rv_opts[@]}" __rv_r
 | |
|         __rv_r="${__rv_r:-$__rv_d}"
 | |
|         if [ -n "$__rv_r" ] || ! is_yes "$__rv_re"; then
 | |
|             set_var "$__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" ] && tooenc "=== $__sm_title ===" 1>&2
 | |
|             __sm_i=1
 | |
|             for __sm_option in "${__sm_options[@]}"; do
 | |
|                 if [ "$__sm_option" == "$__sm_default" ]; then
 | |
|                     tooenc "$__sm_i*- $__sm_option" 1>&2
 | |
|                 else
 | |
|                     tooenc "$__sm_i - $__sm_option" 1>&2
 | |
|                 fi
 | |
|                 let __sm_i=$__sm_i+1
 | |
|             done
 | |
|         fi
 | |
| 
 | |
|         # Afficher les choix
 | |
|         if [ -n "$__sm_yourchoice" ]; then
 | |
|             tooenc_ "$__sm_yourchoice" 1>&2
 | |
|         else
 | |
|             tooenc_ "Entrez le numéro de l'option choisie" "$UTF8" 1>&2
 | |
|         fi
 | |
|         tooenc_ ": " "$UTF8" 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
 | |
|             tooenc "" "$UTF8" 1>&2
 | |
|             __sm_c=0
 | |
|         fi
 | |
|     done
 | |
|     set_var "$__sm_option_var" "$__sm_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
 | |
|         set_var "$1" "$3"
 | |
|         [ -f "$3" -a "$4" == keep ] || >"$3"
 | |
|     else
 | |
|         local __acst_t="$(mktempf "$2")"
 | |
|         autoclean "$__acst_t"
 | |
|         set_var "$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
 | |
|         set_var "$1" "$3"
 | |
|         mkdir -p "$3"
 | |
|     else
 | |
|         local __acst_t="$(mktempd "$2")"
 | |
|         autoclean "$__acst_t"
 | |
|         set_var "$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_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/$__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* ~/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"
 | |
| }
 | |
| 
 | |
| # Type de système
 | |
| UNAME_SYSTEM=`uname -s`
 | |
| [ "${UNAME_SYSTEM#CYGWIN}" != "$UNAME_SYSTEM" ] && UNAME_SYSTEM=Cygwin
 | |
| [ "${UNAME_SYSTEM#MINGW32}" != "$UNAME_SYSTEM" ] && UNAME_SYSTEM=Mingw
 | |
| UNAME_MACHINE=`uname -m`
 | |
| if [ -n "$UTOOLS_CHROOT" ]; then
 | |
|     # Dans un chroot, il est possible de forcer les valeurs
 | |
|     [ -n "$UTOOLS_UNAME_SYSTEM" ] && eval "UNAME_SYSTEM=$UTOOLS_UNAME_SYSTEM"
 | |
|     [ -n "$UTOOLS_UNAME_MACHINE" ] && eval "UNAME_MACHINE=$UTOOLS_UNAME_MACHINE"
 | |
| fi
 | |
| 
 | |
| ################################################################################
 | |
| ## support de ulib dans le cas où cette librairie n'est pas encore chargée
 | |
| 
 | |
| if [ -z "$ULIBDIR" -o "$ULIBDIR" != "$ULIBINIT" ]; then
 | |
|     ULIBPROVIDED=()
 | |
|     function uprovided() {
 | |
|         array_contains ULIBPROVIDED "$1"
 | |
|     }
 | |
|     function uprovide() {
 | |
|         uprovided "$1" && return 1
 | |
|         array_add ULIBPROVIDED "$1"
 | |
|     }
 | |
|     function urequire() {
 | |
|         # si ulib n'est pas disponible pour charger la librairie, on compte sur
 | |
|         # l'utilisateur pour charger manuellement les librairies nécessaires.
 | |
|         local ulib_
 | |
|         for ulib_ in "$@"; do
 | |
|             uprovided "$ulib_" || ewarn "$ulib_: this ulib is required"
 | |
|         done
 | |
|     }
 | |
|     uprovide base
 | |
| fi
 |