diff --git a/bash/src/base.args.sh b/bash/src/base.args.sh index 92381ce..1d19723 100644 --- a/bash/src/base.args.sh +++ b/bash/src/base.args.sh @@ -1,7 +1,6 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments module: base.args "Fonctions de base: analyse d'arguments" -require: base.arr function: local_args "Afficher des commandes pour rendre locales des variables utilisées par parse_args() @@ -9,6 +8,8 @@ Cela permet d'utiliser parse_args() à l'intérieur d'une fonction." function local_args() { echo "local args" echo "local NUCORE_ARGS_ONERROR_RETURN=1" + echo "local NUCORE_VERBOSITY=\"\$NUCORE_VERBOSITY\"" + echo "local NUCORE_INTERACTION=\"\$NUCORE_INTERACTION\"" } function: parse_args "Analyser les arguments de la ligne de commande à partir des définitions du tableau args diff --git a/bash/src/base.arr.sh b/bash/src/base.array.sh similarity index 98% rename from bash/src/base.arr.sh rename to bash/src/base.array.sh index 0130dd3..9c47e71 100644 --- a/bash/src/base.arr.sh +++ b/bash/src/base.array.sh @@ -1,7 +1,6 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments -module: base.arr "Fonctions de base: gestion des variables tableaux" -require: base.core base.str +module: base.array "Fonctions de base: gestion des variables tableaux" function: array_count "retourner le nombre d'éléments du tableau \$1" function array_count() { @@ -269,7 +268,7 @@ function array_splitc() { function: array_xsplitl "créer le tableau \$1 avec chaque ligne de \$2" function array_xsplitl() { - eval "$1=($(recho_ "$2" | strnl2lf | lawk ' + eval "$1=($(recho_ "$2" | nl2lf | lawk ' { gsub(/'\''/, "'\'\\\\\'\''") print "'\''" $0 "'\''" @@ -280,7 +279,7 @@ function: array_splitl "créer le tableau \$1 avec chaque ligne de \$2 Les lignes vides sont ignorés." function array_splitl() { - eval "$1=($(recho_ "$2" | strnl2lf | lawk ' + eval "$1=($(recho_ "$2" | nl2lf | lawk ' /^$/ { next } { gsub(/'\''/, "'\'\\\\\'\''") diff --git a/bash/src/base.eval.sh b/bash/src/base.eval.sh deleted file mode 100644 index fff1268..0000000 --- a/bash/src/base.eval.sh +++ /dev/null @@ -1,468 +0,0 @@ -# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 -##@cooked nocomments -module: base.eval "Fonctions de base: évaluation d'expressions" -require: base.str base.arr - -################################################################################ -# Chaines - -function: evals "Appliquer à une chaine de caractères une suite de traitements, e.g: -~~~ -evals var deref +suffix -~~~ -est équivalent à -~~~ -echo \"\${var}suffix\" -~~~ - -En commençant avec la valeur initiale \$1, les arguments \$2..* sont des -opérations à appliquer dans l'ordre. - -Les opérations suivantes considèrent que la valeur courante est un nom de -variable: -~~~ -:- := :? :+ deref dcount -~~~ - -Toutes les autres opérations travaillent directement avec la valeur -courante. Les opérations suivantes appliquent une transformation: -~~~ -# % / : ^ , +# -# +% -% + - mid repl -~~~ -IMPORTANT: aucune de ces fonctions ne met en échappement les valeur des -patterns. Ainsi, si un pattern contient des caractères interdits comme \\ ou \$, -il faut d'abord le traiter avec _qval() - -Les opérations suivantes font un test sur la valeur et retournent immédiatement: -~~~ -= == != < > -eq -ne -lt -le -gt -ge -n -z -~~~ - -La syntaxe des opérateurs standards de bash est reprise autant que possible, i.e -si on a l'habitude d'écrire ${varOP} en bash, alors la syntaxe à utiliser à -priori est 'evals var OP' ou 'evals var deref OP' suivant les -opérateurs. - -Autres opérateurs: -~~~ -deref indirection -dcount nombre d'éléments du tableau -+#STR ajouter un préfixe --#STR supprimer un préfixe -+%STR ou +STR ajouter un suffixe --%STR ou -STR supprimer un suffixe -mid RANGE traiter la chaine avec strmid() -repl FROM TO traiter la chaine avec strrepl() -~~~ - -Tout autre opérateur est traité comme un appel à une fonction qui prend un seul -argument, la valeur courante, et qui affiche le résultat." -function evals() { - local -a es__tmp - local es__value="$1"; shift - while [ $# -gt 0 ]; do - case "$1" in - # l'argument est le nom de la variable - :-*|:=*|:\?*|:+*) eval 'es__value="${'"${es__value}$1"'}"';; - d|deref) es__value="${!es__value}";; - dc|dcount|ds|dsize) - es__value="${es__value}[@]" - es__tmp=("${!es__value}") - es__value="${#es__tmp[@]}" - ;; - # l'argument est la valeur de la variable - \#*|%*|/*|:*|^*|,*) eval 'es__value="${es__value'"$1"'}"';; - l|length) es__value="${#es__value}";; - =|==|!=|\<|\>|-eq|-ne|-lt|-le|-gt|-ge) - es__tmp=(\[ "$es__value" "$@" ]); "${es__tmp[@]}"; return $?;; - -n|-z) es__tmp=(\[ "$1" "$es__value" ]); "${es__tmp[@]}"; return $?;; - +#*) eval 'es__value="'"${1#+#}"'$es__value"';; - -#*) eval 'es__value="${es__value'"${1#-}"'}"';; - +%*) eval 'es__value="$es__value"'"${1#+%}";; - +*) eval 'es__value="$es__value"'"${1#+}";; - -%*) eval 'es__value="${es__value'"${1#-}"'}"';; - -*) eval 'es__value="${es__value%'"${1#-}"'}"';; - mid|strmid) eval 'es__value="$(strmid "$2" "$es__value")"'; shift;; - repl|strrepl) eval 'es__value="$(strrepl "$2" "$3" "$es__value")"'; shift; shift;; - *) es__value="$("$1" "$es__value")";; - esac - shift - done - echo "$es__value" -} - -function: setxs "équivalent à setx \$1 evals \$2..@" -function setxs() { - local -a ss__args - if [ "$1" == -a ]; then ss__args=(-a); shift; fi - local ss__var="$1"; shift - if [[ "$ss__var" == *=* ]]; then - set -- "${ss__var#*=}" "$@" - ss__var="${ss__var%%=*}" - fi - ss__args=("${ss__args[@]}" "$ss__var") - setx "${ss__args[@]}" evals "$@" -} - -function: cmds "lancer une commande avec comme argument le résultat de evals - -Par exemple, les deux commandes suivantes sont équivalentes: -~~~ -cmds CMD ARGS... // EVALARGS -CMD ARGS... \"\$(evals EVALARGS)\" -~~~" -function cmds() { - local cs__arg - local -a cs__cmd - while [ $# -gt 0 ]; do - cs__arg="$1"; shift - [ "$cs__arg" == // ] && break - cs__cmd=("${cs__cmd[@]}" "$cs__arg") - done - "${cs__cmd[@]}" "$(evals "$@")" -} - -function: evalm "construire une chaine en mixant chaines statiques et évaluations de commandes - -Par exemple, les deux commandes suivantes sont équivalentes: -~~~ -evalm //\"string\" cmd args // cmd args //\"string\" -echo \"string\$(cmd args)\$(cmd args)string\" -~~~" -function evalm() { - local em__val em__arg - local -a em__cmd - while [ $# -gt 0 ]; do - em__arg="$1" - if [ "${em__arg#//}" != "$em__arg" ]; then - em__val="$em__val${em__arg#//}" - shift - continue - fi - em__cmd=() - while [ $# -gt 0 ]; do - em__arg="$1" - [ "${em__arg#//}" != "$em__arg" ] && break - shift - if [ "${em__arg%//}" != "$em__arg" ]; then - local em__tmp="${em__arg%//}" - if [ -z "${em__tmp//\\/}" ]; then - em__arg="${em__arg#\\}" - em__cmd=("${em__cmd[@]}" "$em__arg") - continue - fi - fi - em__cmd=("${em__cmd[@]}" "$em__arg") - done - [ ${#em__cmd[*]} -gt 0 ] && em__val="$em__val$("${em__cmd[@]}")" - done - echo "$em__val" -} - -function: setxm "équivalent à setx \$1 evalm \$2..@" -function setxm() { - local -a sm__args - if [ "$1" == -a ]; then sm__args=(-a); shift; fi - local sm__var="$1"; shift - if [[ "$sm__var" == *=* ]]; then - set -- "${sm__var#*=}" "$@" - sm__var="${sm__var%%=*}" - fi - sm__args=("${sm__args[@]}" "$sm__var") - setx "${sm__args[@]}" evalm "$@" -} - -function: cmdm "lancer une commande avec comme argument le résultat de evalm - -Par exemple, les deux commandes suivantes sont équivalentes: -~~~ -cmdm CMD ARGS... // EVALARGS -CMD ARGS... \"\$(evalm EVALARGS)\" -~~~" -function cmdm() { - local cm__arg - local -a cm__cmd - while [ $# -gt 0 ]; do - cm__arg="$1"; shift - [ "$cm__arg" == // ] && break - cm__cmd=("${cm__cmd[@]}" "$cm__arg") - done - "${cm__cmd[@]}" "$(evalm "$@")" -} - -################################################################################ -# Nombres - -function: evali "Evaluer une expression numérique" -function evali() { - echo "$(($*))" -} - -################################################################################ -# Tableaux - -################################################################################ -# Composition - -function: evalc "Implémenter une syntaxe lisible et naturelle permettant d'enchainer des traitements sur une valeur. - -Par exemple, la commande -~~~ -evalc cmd1... // cmd2... // cmd3... -~~~ -est équivalente à la commande -~~~ -cmd3... \"\$(cmd2... \"\$(cmd1...)\")\" -~~~" -function evalc() { - local ec__arg ec__cmd ec__finalcmd - - while [ $# -gt 0 ]; do - ec__arg="$1"; shift - if [ "$ec__arg" == // ]; then - if [ ${#ec__cmd} -gt 0 ]; then - if [ ${#ec__finalcmd} -eq 0 ]; then ec__finalcmd="$ec__cmd" - else ec__finalcmd="$ec__cmd \$($ec__finalcmd)" - fi - fi - ec__cmd= - continue - elif [ "${ec__arg%//}" != "$ec__arg" ]; then - local tmp="${ec__arg%//}" - [ -z "${tmp//\\/}" ] && ec__arg="${ec__arg#\\}" - fi - ec__cmd="$ec__cmd \"$(_qval "$ec__arg")\"" - done - if [ ${#ec__cmd} -gt 0 ]; then - if [ ${#ec__finalcmd} -eq 0 ]; then ec__finalcmd="$ec__cmd" - else ec__finalcmd="$ec__cmd \$($ec__finalcmd)" - fi - fi - eval "$ec__finalcmd" -} - -function: setxc "équivalent à setx \$1 evalc \$2..@" -function setxc() { - local -a sx__args - if [ "$1" == -a ]; then sx__args=(-a); shift; fi - local sx__var="$1"; shift - if [[ "$sx__var" == *=* ]]; then - set -- "${sx__var#*=}" "$@" - sx__var="${sx__var%%=*}" - fi - sx__args=("${sx__args[@]}" "$sx__var") - setx "${sx__args[@]}" evalc "$@" -} - -################################################################################ -# Chainage - -function: evalp "Implémenter une syntaxe alternative permettant d'enchainer des traitements sur un flux de données. - -Par exemple, la commande -~~~ -evalp cmd1... // cmd2... // cmd3... -~~~ -affiche le résultat de la commande -~~~ -cmd1... | cmd2... | cmd3... -~~~ - -Typiquement, cette fonction permet de faciliter la *construction* d'un -enchainement de commandes par programme, ou de faciliter l'utilisation de la -fonction setx() pour récupérer le résultat d'un enchainement. Dans les autres -cas, il est plus simple et naturel d'écrire les enchainements avec la syntaxe de -bash." -function evalp() { - local ep__arg ep__cmd - - while [ $# -gt 0 ]; do - ep__arg="$1"; shift - if [ "$ep__arg" == // ]; then - ep__cmd="$ep__cmd |" - continue - elif [ "${ep__arg%//}" != "$ep__arg" ]; then - local ep__tmp="${ep__arg%//}" - if [ -z "${ep__tmp//\\/}" ]; then - ep__arg="${ep__arg#\\}" - fi - fi - ep__cmd="${ep__cmd:+$ep__cmd }\"$(_qval "$ep__arg")\"" - done - eval "$ep__cmd" -} - -function: setxp "équivalent à setx \$1 evalp \$2..@" -function setxp() { - local -a sp__args - if [ "$1" == -a ]; then sp__args=(-a); shift; fi - local sp__var="$1"; shift - if [[ "$sp__var" == *=* ]]; then - set -- "${sp__var#*=}" "$@" - sp__var="${sp__var%%=*}" - fi - sp__args=("${sp__args[@]}" "$sp__var") - setx "${sp__args[@]}" evalp "$@" -} - -function: cmdp "lancer une commande avec comme argument le résultat de evalp - -Par exemple, les deux commandes suivantes sont équivalentes: -~~~ -cmdp CMD ARGS... // EVALARGS -CMD ARGS... \"\$(evalp EVALARGS)\" -~~~" -function cmdp() { - local cp__arg - local -a cp__cmd - while [ $# -gt 0 ]; do - cp__arg="$1"; shift - [ "$cp__arg" == // ] && break - cp__cmd=("${cp__cmd[@]}" "$cp__arg") - done - "${cp__cmd[@]}" "$(evalp "$@")" -} - -################################################################################ -# Générique - -function: evalx "" -function evalx() { - : -} - -function: setxx "équivalent à setx \$1 evalx \$2..@" -function setxx() { - local -a sx__args - if [ "$1" == -a ]; then sx__args=(-a); shift; fi - local sx__var="$1"; shift - if [[ "$sx__var" == *=* ]]; then - set -- "${sx__var#*=}" "$@" - sx__var="${sx__var%%=*}" - fi - sx__args=("${sx__args[@]}" "$sx__var") - setx "${sx__args[@]}" evalx "$@" -} - -function: cmdx "lancer une commande avec comme argument le résultat de evalx - -Par exemple, les deux commandes suivantes sont équivalentes: -~~~ -cmdx CMD ARGS... // EVALARGS -CMD ARGS... \"\$(evalx EVALARGS)\" -~~~" -function cmdx() { - local cx__arg - local -a cx__cmd - while [ $# -gt 0 ]; do - cx__arg="$1"; shift - [ "$cx__arg" == // ] && break - cx__cmd=("${cx__cmd[@]}" "$cx__arg") - done - "${cx__cmd[@]}" "$(evalx "$@")" -} - -function: cmdsplitf "\ -Cette fonction doit être appelée avec N arguments (avec N>1). Elle analyse et -découpe l'argument \$N comme avec une ligne de commande du shell. Ensuite, elle -appelle la fonction \$1 avec les arguments de \$2 à \${N-1}, suivi des arguments -obtenus lors de l'analyse de l'argument \$N. Par exemple, la commande suivante: -~~~ -strsplitf cmd arg1 \"long arg2\" \"arg3 'long arg4'\" -~~~ -est équivalente à: -~~~ -cmd arg1 \"long arg2\" arg3 \"long arg4\" -~~~ - -Retourner le code 127 si la fonction à appeler n'est pas spécifiée. Retourner le -code 126 si une erreur s'est produite lors de l'analyse de l'argument \$N" -function cmdsplitf() { - [ $# -gt 0 ] || return 127 - local func count - func="$1"; shift - count=$# - if [ $count -gt 0 ]; then - eval 'set -- "${@:1:$(($count-1))}" '"${!count}" || return 126 - fi - "$func" "$@" -} - -################################################################################ -# Tests - -function: testx "Faire un test unaire avec la commande [ sur une valeur calculée avec evalx. - -Utiliser la syntaxe 'testx op cmds...' e.g. -~~~ -testx -z cmd1 // cmd2 -~~~" -function testx() { - local t__op="$1"; shift - local t__val="$(evalx "$@")" - [ $t__op "$t__val" ] -} - -function: test2x "Faire une test binaire avec la commande [ entre une valeur spécifiée et une valeur calculée avec evalx. - -Utiliser la syntaxe 'test2x value op cmds...' e.g. -~~~ -test2x value == cmd1 // cmd2 -~~~" -function test2x() { - local t__val1="$1"; shift - local t__op="$1"; shift - local t__val2="$(evalx "$@")" - [ "$t__val1" $t__op "$t__val2" ] -} - -function: testrx "Faire une test binaire avec la commande [[ entre une valeur spécifiée et une valeur calculée avec evalx. - -Utiliser la syntaxe 'testrx value op cmds...' e.g. -~~~ -testrx value == cmd1 // cmd2 -~~~" -function testrx() { - local t__val1="$1"; shift - local t__op="$1"; shift - local t__val2="$(evalx "$@")" - eval '[[ "$t__val1" '"$t__op"' "$t__val2" ]]' -} - -function: testp "Faire un test unaire avec la commande [ sur une valeur calculée avec evalp. - -Utiliser la syntaxe 'testp op cmds...' e.g. -~~~ -testp -z cmd1 // cmd2 -~~~" -function testp() { - local t__op="$1"; shift - local t__val="$(evalp "$@")" - [ $t__op "$t__val" ] -} - -function: test2p "Faire une test binaire avec la commande [ entre une valeur spécifiée et une valeur calculée avec evalp. - -Utiliser la syntaxe 'test2p value op cmds...' e.g. -~~~ -test2p value == cmd1 // cmd2 -~~~" -function test2p() { - local t__val1="$1"; shift - local t__op="$1"; shift - local t__val2="$(evalp "$@")" - [ "$t__val1" $t__op "$t__val2" ] -} - -function: testrp "Faire une test binaire avec la commande [[ entre une valeur spécifiée et une valeur calculée avec evalp. - -Utiliser la syntaxe 'testrp value op cmds...' e.g. -~~~ -testrp value == cmd1 // cmd2 -~~~" -function testrp() { - local t__val1="$1"; shift - local t__op="$1"; shift - local t__val2="$(evalp "$@")" - eval '[[ "$t__val1" '"$t__op"' "$t__val2" ]]' -} diff --git a/bash/src/base.input.sh b/bash/src/base.input.sh new file mode 100644 index 0000000..e826296 --- /dev/null +++ b/bash/src/base.input.sh @@ -0,0 +1,586 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +##@cooked nocomments +module: base.input "Fonctions de base: saisie" + +function toienc() { +# $1 étant une variable contenant une chaine encodée dans l'encoding d'entrée $2 +# (qui vaut par défaut $NUCORE_INPUT_ENCODING), transformer cette chaine en +# utf-8 + local __var="$1" __from="${2:-$NUCORE_INPUT_ENCODING}" + if [ "$__from" != "NUCORE__UTF8" ]; then + _setv "$__var" "$(iconv -f "$__from" -t utf-8 <<<"${!__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 $NUCORE_INPUT_ENCODING + [ $# -gt 0 ] || set -- REPLY + local __var + read "$@" + for __var in "$@"; do + [ -z "$__var" -o "${__var:0:1}" == "-" ] && continue # ignorer les options + toienc "$__var" + done +} + +function is_interaction() { + return 1 +} + +function check_interaction() { + return 0 +} +function get_interaction_option() { :;} + +function ask_yesno() { +# Afficher le message $1 suivi de [oN] ou [On] suivant que $2 vaut O ou N, puis +# lire la réponse. Retourner 0 si la réponse est vrai, 1 sinon. +# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si +# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées +# ($2=message, $3=default) +# Si $2 vaut C, la valeur par défaut est N si on est interactif, O sinon +# Si $2 vaut X, la valeur par défaut est O si on est interactif, N sinon + local interactive=1 + if [[ "$1" == -* ]]; then + if [ "$1" != -- ]; then + check_interaction "$1" || interactive= + fi + shift + else + check_interaction -c || interactive= + fi + local default="${2:-N}" + if [ "$default" == "C" ]; then + [ -n "$interactive" ] && default=N || default=O + elif [ "$default" == "X" ]; then + [ -n "$interactive" ] && default=O || default=N + fi + if [ -n "$interactive" ]; then + eflush + local message="$1" + local prompt="[oN]" + local r + is_yes "$default" && prompt="[On]" + if [ -n "$message" ]; then + __eecho_ "$message" 1>&2 + else + __eecho_ "Voulez-vous continuer?" 1>&2 + fi + tooenc_ " $prompt " 1>&2 + uread r + is_yes "${r:-$default}" + else + is_yes "$default" + fi +} + +function ask_any() { +# Afficher le message $1 suivi du texte "[$2]" (qui vaut par défaut +Oq), puis +# lire la réponse. Les lettres de la chaine de format $2 sont numérotées de 0 à +# $((${#2} - 1)). Le code de retour est le numéro de la lettre qui a été +# sélectionnée. Cette fonction est une généralisation de ask_yesno() pour +# n'importe quel ensemble de lettres. +# La première lettre en majuscule est la lettre sélectionnée par défaut. +# La lettre O matche toutes les lettres qui signifient oui: o, y, 1, v, t +# La lettre N matche toutes les lettres qui signifient non: n, f, 0 +# Il y a des raccourcis: +# +O --> On +# +N --> oN +# +C --> oN si on est en mode interactif, On sinon +# +X --> On si on est en mode interactifn oN sinon +# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si +# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées +# ($2=message, $3=format) + local interactive=1 + if [[ "$1" == -* ]]; then + if [ "$1" != -- ]; then + check_interaction "$1" || interactive= + fi + shift + else + check_interaction -c || interactive= + fi + local format="${2:-+Oq}" + format="${format/+O/On}" + format="${format/+N/oN}" + if [ -n "$interactive" ]; then + format="${format/+C/oN}" + format="${format/+X/On}" + else + format="${format/+C/On}" + format="${format/+X/oN}" + fi + local i count="${#format}" + + if [ -n "$interactive" ]; then + eflush + local message="${1:-Voulez-vous continuer?}" + local prompt="[$format]" + local r f lf defi + while true; do + __eecho_ "$message $prompt " 1>&2 + uread r + r="$(strlower "${r:0:1}")" + i=0; defi= + while [ $i -lt $count ]; do + f="${format:$i:1}" + lf="$(strlower "$f")" + [ "$r" == "$lf" ] && return $i + if [ -z "$defi" ]; then + [ -z "${f/[A-Z]/}" ] && defi="$i" + fi + if [ "$lf" == o ]; then + case "$r" in o|y|1|v|t) return $i;; esac + elif [ "$lf" == n ]; then + case "$r" in n|f|0) return $i;; esac + fi + i=$(($i + 1)) + done + [ -z "$r" ] && return ${defi:-0} + done + else + i=0 + while [ $i -lt $count ]; do + f="${format:$i:1}" + [ -z "${f/[A-Z]/}" ] && return $i + i=$(($i + 1)) + done + return 0 + fi +} + +function read_value() { +# Afficher le message $1 suivi de la valeur par défaut [$3] si elle est non +# vide, puis lire la valeur donnée par l'utilisateur. Cette valeur doit être non +# vide si $4(=O) est vrai. La valeur saisie est placée dans la variable +# $2(=value) +# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si +# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées +# ($2=message, $3=variable, $4=default, $5=required) +# En mode non interactif, c'est la valeur par défaut qui est sélectionnée. Si +# l'utilisateur requière que la valeur soit non vide et que la valeur par défaut +# est vide, afficher un message d'erreur et retourner faux +# read_password() est comme read_value(), mais la valeur saisie n'est pas +# affichée, ce qui la rend appropriée pour la lecture d'un mot de passe. + local -a __rv_opts; local __rv_readline=1 __rv_showdef=1 __rv_nl= + __rv_opts=() + [ -n "$NUCORE_NO_READLINE" ] && __rv_readline= + __rv_read "$@" +} + +function read_password() { + local -a __rv_opts __rv_readline= __rv_showdef= __rv_nl=1 + __rv_opts=(-s) + __rv_read "$@" +} + +function __rv_read() { + local __rv_int=1 + if [[ "$1" == -* ]]; then + if [ "$1" != -- ]; then + check_interaction "$1" || __rv_int= + fi + shift + else + check_interaction -c || __rv_int= + fi + local __rv_msg="$1" __rv_v="${2:-value}" __rv_d="$3" __rv_re="${4:-O}" + if [ -z "$__rv_int" ]; then + # En mode non interactif, retourner la valeur par défaut + if is_yes "$__rv_re" && [ -z "$__rv_d" ]; then + eerror "La valeur par défaut de $__rv_v doit être non vide" + return 1 + fi + _setv "$__rv_v" "$__rv_d" + return 0 + fi + + eflush + local __rv_r + while true; do + if [ -n "$__rv_msg" ]; then + __eecho_ "$__rv_msg" 1>&2 + else + __eecho_ "Entrez la valeur" 1>&2 + fi + if [ -n "$__rv_readline" ]; then + tooenc_ ": " 1>&2 + uread -e ${__rv_d:+-i"$__rv_d"} "${__rv_opts[@]}" __rv_r + else + if [ -n "$__rv_d" ]; then + if [ -n "$__rv_showdef" ]; then + tooenc_ " [$__rv_d]" 1>&2 + else + tooenc_ " [****]" 1>&2 + fi + fi + tooenc_ ": " 1>&2 + uread "${__rv_opts[@]}" __rv_r + [ -n "$__rv_nl" ] && echo + fi + __rv_r="${__rv_r:-$__rv_d}" + if [ -n "$__rv_r" ] || ! is_yes "$__rv_re"; then + _setv "$__rv_v" "$__rv_r" + return 0 + fi + done +} + +function simple_menu() { +# Afficher un menu simple dont les éléments sont les valeurs du tableau +# $2(=options). L'option choisie est placée dans la variable $1(=option) +# -t TITLE: spécifier le titre du menu +# -m YOUR_CHOICE: spécifier le message d'invite pour la sélection de l'option +# -d DEFAULT: spécifier l'option par défaut. Par défaut, prendre la valeur +# actuelle de la variable $1(=option) + local __sm_title= __sm_yourchoice= __sm_default= + local -a __sm_args + parse_opts -t: __sm_title= -m: __sm_yourchoice= -d: __sm_default= @ __sm_args -- "$@" && + set -- "${__sm_args[@]}" || ewarn "$__sm_args" + + local __sm_option_var="${1:-option}" __sm_options_var="${2:-options}" + local __sm_option __sm_options + __sm_options="$__sm_options_var[*]" + if [ -z "${!__sm_options}" ]; then + eerror "Le tableau $__sm_options_var doit être non vide" + return 1 + fi + [ -z "$__sm_default" ] && __sm_default="${!__sm_option_var}" + + eflush + array_copy __sm_options "$__sm_options_var" + local __sm_c=0 __sm_i __sm_choice + while true; do + if [ "$__sm_c" == "0" ]; then + # Afficher le menu + [ -n "$__sm_title" ] && __eecho "=== $__sm_title ===" 1>&2 + __sm_i=1 + for __sm_option in "${__sm_options[@]}"; do + if [ "$__sm_option" == "$__sm_default" ]; then + __eecho "$__sm_i*- $__sm_option" 1>&2 + else + __eecho "$__sm_i - $__sm_option" 1>&2 + fi + let __sm_i=$__sm_i+1 + done + fi + + # Afficher les choix + if [ -n "$__sm_yourchoice" ]; then + __eecho_ "$__sm_yourchoice" 1>&2 + else + __eecho_ "Entrez le numéro de l'option choisie" 1>&2 + fi + tooenc_ ": " 1>&2 + uread __sm_choice + + # Valeur par défaut + if [ -z "$__sm_choice" -a -n "$__sm_default" ]; then + __sm_option="$__sm_default" + break + fi + # Vérifier la saisie + if [ -n "$__sm_choice" -a -z "${__sm_choice//[0-9]/}" ]; then + if [ "$__sm_choice" -gt 0 -a "$__sm_choice" -le "${#__sm_options[*]}" ]; then + __sm_option="${__sm_options[$(($__sm_choice - 1))]}" + break + else + eerror "Numéro d'option incorrect" + fi + else + 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 "" 1>&2 + __sm_c=0 + fi + done + _setv "$__sm_option_var" "$__sm_option" +} + +function actions_menu() { +# Afficher un menu dont les éléments sont les valeurs du tableau $4(=options), +# et une liste d'actions tirées du tableau $3(=actions). L'option choisie est +# placée dans la variable $2(=option). L'action choisie est placée dans la +# variable $1(=action) +# Un choix est saisi sous la forme [action]num_option +# -t TITLE: spécifier le titre du menu +# -m OPT_YOUR_CHOICE: spécifier le message d'invite pour la sélection de +# l'action et de l'option +# -M ACT_YOUR_CHOICE: spécifier le message d'invite dans le cas où aucune option +# n'est disponible. Dans ce cas, seules les actions vides sont possibles. +# -e VOID_ACTION: spécifier qu'une action est vide, c'est à dire qu'elle ne +# requière pas d'être associée à une option. Par défaut, la dernière action +# est classée dans cette catégorie puisque c'est l'action "quitter" +# -d DEFAULT_ACTION: choisir l'action par défaut. par défaut, c'est la première +# action. +# -q QUIT_ACTION: choisir l'option "quitter" qui provoque la sortie du menu sans +# choix. par défaut, c'est la dernière action. +# -o DEFAULT_OPTION: choisir l'option par défaut. par défaut, prendre la valeur +# actuelle de la variable $2(=option) + local -a __am_action_descs __am_options __am_void_actions + local __am_tmp __am_select_action __am_select_option __am_title __am_optyc __am_actyc + local __am_default_action=auto __am_quit_action=auto + local __am_default_option= + local -a __am_args + parse_opts \ + -t: __am_title= \ + -m: __am_optyc= \ + -M: __am_actyc= \ + -e: __am_void_actions \ + -d: __am_default_action= \ + -q: __am_quit_action= \ + -o: __am_default_option= \ + @ __am_args -- "$@" && set -- "${__am_args[@]}" || { eerror "$__am_args"; return 1; } + + __am_tmp="${1:-action}"; __am_select_action="${!__am_tmp}" + __am_tmp="${2:-option}"; __am_select_option="${!__am_tmp}" + [ -n "$__am_default_option" ] && __am_select_option="$__am_default_option" + array_copy __am_action_descs "${3:-actions}" + array_copy __am_options "${4:-options}" + + eerror_unless [ ${#__am_action_descs[*]} -gt 0 ] "Vous devez spécifier le tableau des actions" || return + __actions_menu || return 1 + _setv "${1:-action}" "$__am_select_action" + _setv "${2:-option}" "$__am_select_option" +} + +function __actions_menu() { + local title="$__am_title" + local optyc="$__am_optyc" actyc="$__am_actyc" + local default_action="$__am_default_action" + local quit_action="$__am_quit_action" + local select_action="$__am_select_action" + local select_option="$__am_select_option" + local -a action_descs options void_actions + array_copy action_descs __am_action_descs + array_copy options __am_options + array_copy void_actions __am_void_actions + + # Calculer la liste des actions valides + local no_options + array_isempty options && no_options=1 + + local -a actions + local tmp action name + for tmp in "${action_descs[@]}"; do + splitfsep2 "$tmp" : action name + [ -n "$action" ] || action="${name:0:1}" + action="$(strlower "$action")" + array_addu actions "$action" + done + + # Calculer l'action par défaut + if [ "$default_action" == auto ]; then + # si action par défaut non spécifiée, alors prendre la première action + default_action="$select_action" + if [ -n "$default_action" ]; then + array_contains actions "$default_action" || default_action= + fi + [ -n "$default_action" ] || default_action="${actions[0]}" + fi + default_action="${default_action:0:1}" + default_action="$(strlower "$default_action")" + + # Calculer l'action quitter par défaut + if [ "$quit_action" == auto ]; then + # si action par défaut non spécifiée, alors prendre la dernière action, + # s'il y a au moins 2 actions + if [ ${#actions[*]} -gt 1 ]; then + quit_action="${actions[@]:$((-1)):1}" + array_addu void_actions "$quit_action" + fi + fi + quit_action="${quit_action:0:1}" + quit_action="$(strlower "$quit_action")" + + # Calculer la ligne des actions à afficher + local action_title + for tmp in "${action_descs[@]}"; do + splitfsep2 "$tmp" : action name + [ -n "$action" ] || action="${name:0:1}" + [ -n "$name" ] || name="$action" + action="$(strlower "$action")" + if [ -n "$no_options" ]; then + if ! array_contains void_actions "$action"; then + array_del actions "$action" + continue + fi + fi + [ "$action" == "$default_action" ] && name="$name*" + action_title="${action_title:+$action_title/}$name" + done + if [ -n "$default_action" ]; then + # si action par défaut invalide, alors pas d'action par défaut + array_contains actions "$default_action" || default_action= + fi + if [ -n "$quit_action" ]; then + # si action quitter invalide, alors pas d'action quitter + array_contains actions "$quit_action" || quit_action= + fi + + # Type de menu + if [ -n "$no_options" ]; then + if array_isempty void_actions; then + eerror "Aucune option n'est définie. Il faut définir le tableau des actions vides" + return 1 + fi + __void_actions_menu + else + __options_actions_menu + fi +} + +function __void_actions_menu() { + eflush + local c=0 choice + while true; do + if [ $c -eq 0 ]; then + [ -n "$title" ] && __etitle "$title" 1>&2 + __eecho_ "=== Actions disponibles: " 1>&2 + tooenc "$action_title" 1>&2 + fi + if [ -n "$actyc" ]; then + __eecho_ "$actyc" 1>&2 + elif [ -n "$optyc" ]; then + __eecho_ "$optyc" 1>&2 + else + __eecho_ "Entrez l'action à effectuer" 1>&2 + fi + tooenc_ ": " 1>&2 + uread choice + if [ -z "$choice" -a -n "$default_action" ]; then + select_action="$default_action" + break + fi + + # vérifier la saisie + choice="${choice:0:1}" + choice="$(strlower "$choice")" + if array_contains actions "$choice"; then + select_action="$choice" + break + elif [ -n "$choice" ]; then + eerror "$choice: action incorrecte" + else + eerror "vous devez saisir l'action à effectuer" + fi + let c=$c+1 + if [ $c -eq 5 ]; then + # sauter une ligne toutes les 4 tentatives + tooenc "" 1>&2 + c=0 + fi + done + __am_select_action="$select_action" + __am_select_option= +} + +function __options_actions_menu() { + eflush + local c=0 option choice action option + while true; do + if [ $c -eq 0 ]; then + [ -n "$title" ] && __etitle "$title" 1>&2 + i=1 + for option in "${options[@]}"; do + if [ "$option" == "$select_option" ]; then + tooenc "$i*- $option" 1>&2 + else + tooenc "$i - $option" 1>&2 + fi + let i=$i+1 + done + __estepn_ "Actions disponibles: " 1>&2 + tooenc "$action_title" 1>&2 + fi + if [ -n "$optyc" ]; then + __eecho_ "$optyc" 1>&2 + else + __eecho_ "Entrez l'action et le numéro de l'option choisie" 1>&2 + fi + tooenc_ ": " 1>&2 + uread choice + + # vérifier la saisie + if [ -z "$choice" -a -n "$default_action" ]; then + action="$default_action" + if array_contains void_actions "$action"; then + select_action="$action" + select_option= + break + elif [ -n "$select_option" ]; then + select_action="$action" + break + fi + fi + action="${choice:0:1}" + action="$(strlower "$action")" + if array_contains actions "$action"; then + # on commence par un code d'action valide. cool :-) + if array_contains void_actions "$action"; then + select_action="$action" + select_option= + break + else + option="${choice:1}" + option="${option// /}" + if [ -z "$option" -a -n "$select_option" ]; then + select_action="$action" + break + elif [ -z "$option" ]; then + eerror "vous devez saisir le numéro de l'option" + elif isnum "$option"; then + if [ $option -gt 0 -a $option -le ${#options[*]} ]; then + select_action="$action" + select_option="${options[$(($option - 1))]}" + break + fi + else + eerror "$option: numéro d'option incorrecte" + fi + fi + elif isnum "$choice"; then + # on a simplement donné un numéro d'option + action="$default_action" + if [ -n "$action" ]; then + if array_contains void_actions "$action"; then + select_action="$action" + select_option= + break + else + option="${choice// /}" + if [ -z "$option" ]; then + eerror "vous devez saisir le numéro de l'option" + elif isnum "$option"; then + if [ $option -gt 0 -a $option -le ${#options[*]} ]; then + select_action="$action" + select_option="${options[$(($option - 1))]}" + break + fi + else + eerror "$option: numéro d'option incorrecte" + fi + fi + else + eerror "Vous devez spécifier l'action à effectuer" + fi + elif [ -n "$choice" ]; then + eerror "$choice: action et/ou option incorrecte" + else + eerror "vous devez saisir l'action à effectuer" + fi + let c=$c+1 + if [ $c -eq 5 ]; then + # sauter une ligne toutes les 4 tentatives + tooenc "" 1>&2 + c=0 + fi + done + __am_select_action="$select_action" + __am_select_option="$select_option" +} diff --git a/bash/src/base.io.sh b/bash/src/base.io.sh deleted file mode 100644 index d7f46e9..0000000 --- a/bash/src/base.io.sh +++ /dev/null @@ -1,1291 +0,0 @@ -# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 -##@cooked nocomments -module: base.io "Fonctions de base: affichage et saisie" -require: base.arr - -NUCORE__TAB=$'\t' -NUCORE__LATIN1=iso-8859-1 -NUCORE__LATIN9=iso-8859-15 -NUCORE__UTF8=utf-8 -NUCORE_IENC="$NUCORE__UTF8" -NUCORE_OENC="$NUCORE__UTF8" - -if [ ! -x "$(which iconv 2>/dev/null)" ]; then - function iconv() { cat; } -fi - -function nucore__lang_encoding() { - case "${LANG,,}" in - *@euro) echo "iso-8859-15";; - *.utf-8|*.utf8) echo "utf-8";; - *) echo "iso-8859-1";; - esac -} - -function nucore__norm_encoding() { - local enc="${1,,}" - enc="${enc//[-_]/}" - case "$enc" in - latin|latin1|iso8859|iso88591|8859|88591) echo "iso-8859-1";; - latin9|iso885915|885915) echo "iso-8859-15";; - utf|utf8) echo "utf-8";; - *) echo "$1";; - esac -} - -function nucore__init_encoding() { - local DEFAULT_ENCODING="$(nucore__lang_encoding)" - [ -n "$DEFAULT_ENCODING" ] || DEFAULT_ENCODING=utf-8 - [ -n "$NUCORE_OUTPUT_ENCODING" ] || NUCORE_OUTPUT_ENCODING="$DEFAULT_ENCODING" - NUCORE_OUTPUT_ENCODING="$(nucore__norm_encoding "$NUCORE_OUTPUT_ENCODING")" - [ -n "$NUCORE_INPUT_ENCODING" ] || NUCORE_INPUT_ENCODING="$NUCORE_OUTPUT_ENCODING" - NUCORE_INPUT_ENCODING="$(nucore__norm_encoding "$NUCORE_INPUT_ENCODING")" - [ -n "$NUCORE_EDITOR_ENCODING" ] || NUCORE_EDITOR_ENCODING="$NUCORE_INPUT_ENCODING" - NUCORE_EDITOR_ENCODING="$(nucore__norm_encoding "$NUCORE_EDITOR_ENCODING")" - - NUCORE_IENC="$NUCORE_INPUT_ENCODING" - NUCORE_OENC="$NUCORE_OUTPUT_ENCODING" -} -[ -n "$NUCORE_LANG" -a -z "$LANG" ] && export NUCORE_LANG LANG="$NUCORE_LANG" -nucore__init_encoding - -function nucore_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 NUCORE_VERBOSITY et -# NUCORE_INTERACTION. Ceci est utile pour pouvoir appeler sans risque de -# pollution de l'environnement une fonction qui utilise parse_opts() avec les -# définitions de PRETTYOPTS. -# Si aucun arguments n'est fourni, toutes les définitions sont affichées. - local arg - [ $# -gt 0 ] || set -- opts verbosity interaction - for arg in "$@"; do - case "$arg" in - parse_opts|opts|o|args) echo "local -a args";; - verbosity|v) echo "local NUCORE_VERBOSITY='$NUCORE_VERBOSITY'";; - interaction|i) echo "local NUCORE_INTERACTION='$NUCORE_INTERACTION'";; - esac - done -} - -function noerror() { -# lancer la commande "$@" et masquer son code de retour - [ $# -gt 0 ] || set : - "$@" || return 0 -} - -function noout() { -# lancer la commande "$@" en supprimant sa sortie standard - [ $# -gt 0 ] || return 0 - "$@" >/dev/null -} - -function noerr() { -# lancer la commande "$@" en supprimant sa sortie d'erreur - [ $# -gt 0 ] || return 0 - "$@" 2>/dev/null -} - -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. identique à isatty() - tty -s <&1 -} - -function err_isatty() { -# tester si STDERR n'est pas une redirection - tty -s <&2 -} - -################################################################################ -# affichage - -function tooenc() { -# Transformer la valeur $1 de l'encoding $2(=$NUCORE_OENC) vers l'encoding de sortie -# $3=($NUCORE_OUTPUT_ENCODING) - local src="$1" from="${2:-$NUCORE_OENC}" to="${3:-$NUCORE_OUTPUT_ENCODING}" - if [ "$from" == "$to" ]; then - recho "$src" - else - iconv -f "$from" -t "$to" <<<"$src" - fi -} - -function uecho() { - tooenc "$*" -} - -function tooenc_() { -# Transformer la valeur $1 de l'encoding $2(=$NUCORE_OENC) vers l'encoding de sortie -# $3=($NUCORE_OUTPUT_ENCODING) - local src="$1" from="${2:-$NUCORE_OENC}" to="${3:-$NUCORE_OUTPUT_ENCODING}" - if [ "$from" == "$to" ]; then - recho_ "$src" - else - recho_ "$src" | iconv -f "$from" -t "$to" - fi -} - -function uecho_() { - tooenc_ "$*" -} - -function stooenc() { ### XXX -# Transformer la valeur lue sur stdin de $NUCORE_OENC vers l'encoding de sortie par -# défaut ($NUCORE_OUTPUT_ENCODING) - local from="${1:-$NUCORE_OENC}" to="${2:-$NUCORE_OUTPUT_ENCODING}" - if [ "$from" == "$to" ]; then - cat - else - iconv -f "$from" -t "$to" - fi -} - -# faut-il dater les messages de etitle, estep, ebegin? -# Faire NUCORE_EDATE=1 en début de script pour activer cette fonctionnalité -export NUCORE_EDATE -function __edate() { [ -n "$NUCORE_EDATE" ] && date +"[%d/%m/%Y-%H:%M:%S] "; } - -export NUCORE_ELOG_OVERWRITE -function __set_no_colors() { :; } -function elogto() { -# Activer NUCORE_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 NUCORE_ELOG_OVERWRITE=1, alors le fichier en sortie est écrasé. Sinon, les -# lignes en sortie lui sont ajoutées - NUCORE_EDATE=1 - if [ -n "$1" -a -n "$2" ]; then - LANG=fr_FR.UTF8 - NUCORE_OUTPUT_ENCODING="$NUCORE__UTF8" - __set_no_colors 1 - if [ -n "$NUCORE_ELOG_OVERWRITE" ]; then - exec >"$1" 2>"$2" - else - exec >>"$1" 2>>"$2" - fi - elif [ -n "$1" ]; then - LANG=fr_FR.UTF8 - NUCORE_OUTPUT_ENCODING="$NUCORE__UTF8" - __set_no_colors 1 - if [ -n "$NUCORE_ELOG_OVERWRITE" ]; then - exec >"$1" 2>&1 - else - exec >>"$1" 2>&1 - fi - fi -} - -# variables utilisées pour l'affichage indenté des messages et des titres -# __estack est la liste des invocations de 'ebegin' et 'etitle' en cours -# __tlevel est l'indentation à appliquer avant d'afficher le message -export __estack __tlevel -function __indent() { -# indenter les lignes de $1, sauf la première - if [ "${1/ -/}" != "$1" ]; then - sed "2,\$s/^/${__tlevel}/g" <<<"$1" - else - recho "$1" - fi -} -# fonctions à surcharger pour modifier la façon dont les messages sont affichés -function __eerror() { tooenc "$(__edate)${__tlevel}ERROR $(__indent "$1")"; } -function __ewarn() { tooenc "$(__edate)${__tlevel}WARNING $(__indent "$1")"; } -function __enote() { tooenc "$(__edate)${__tlevel}NOTE $(__indent "$1")"; } -function __ebanner() { - local maxi="${COLUMNS:-80}" - local -a lines - local psfix line - - psfix="$(__edate)${__tlevel}" - while [ ${#psfix} -lt $maxi ]; do psfix="$psfix="; done - - tooenc "$psfix" - maxi=$(($maxi - 1)) - array_xsplitl lines "$1" - for line in "" "${lines[@]}" ""; do - line="$(__edate)${__tlevel}= $line" - if [ ${#line} -le $maxi ]; then - while [ ${#line} -lt $maxi ]; do line="$line "; done - line="$line=" - fi - tooenc "$line" - done - tooenc "$psfix" -} -function __eimportant() { tooenc "$(__edate)${__tlevel}IMPORTANT $(__indent "$1")"; } -function __eattention() { tooenc "$(__edate)${__tlevel}ATTENTION $(__indent "$1")"; } -function __einfo() { tooenc "$(__edate)${__tlevel}INFO $(__indent "$1")"; } -function __eecho() { tooenc "$(__edate)${__tlevel}$(__indent "$1")"; } -function __eecho_() { tooenc_ "$(__edate)${__tlevel}$(__indent "$1")"; } -function __edebug() { tooenc "$(__edate)${__tlevel}DEBUG $(__indent "$1")"; } -function __estep() { tooenc "$(__edate)${__tlevel}. $(__indent "$1")"; } -function __estepe() { tooenc "$(__edate)${__tlevel}.E $(__indent "$1")"; } -function __estepw() { tooenc "$(__edate)${__tlevel}.W $(__indent "$1")"; } -function __estepn() { tooenc "$(__edate)${__tlevel}.N $(__indent "$1")"; } -function __estepi() { tooenc "$(__edate)${__tlevel}.I $(__indent "$1")"; } -function __estep_() { tooenc_ "$(__edate)${__tlevel}. $(__indent "$1")"; } -function __estepe_() { tooenc_ "$(__edate)${__tlevel}.E $(__indent "$1")"; } -function __estepw_() { tooenc_ "$(__edate)${__tlevel}.W $(__indent "$1")"; } -function __estepn_() { tooenc_ "$(__edate)${__tlevel}.N $(__indent "$1")"; } -function __estepi_() { tooenc_ "$(__edate)${__tlevel}.I $(__indent "$1")"; } -function __etitle() { tooenc "$(__edate)${__tlevel}=== $(__indent "$1")"; } -function __ebegin() { tooenc_ "$(__edate)${__tlevel}. $(__indent "$1"): "; } -function __edoto() { echo_ "."; } -function __edotw() { echo_ "w"; } -function __edotx() { echo_ "x"; } -function __edotp() { echo_ "+"; } -function __edotd() { tooenc "($1)"; } -function __eendo() { echo "[ok]"; } -function __eendx() { echo "[error]"; } -PRETTYOPTS=() -function set_verbosity() { :;} -function set_interaction() { :;} -function show_error() { -# tester respectivement si on doit afficher les messages d'erreur, -# d'avertissement, d'information, de debug - return 0 -} -function show_warn() { - return 0 -} -function show_info() { - return 0 -} -function show_verbose() { - return 0 -} -function show_debug() { - [ -n "$DEBUG" ] -} -function check_verbosity() { - return 0 -} -function get_verbosity_option() { :;} -function check_interaction() { - return 0 -} - -# note: toutes les fonctions d'affichage e* écrivent sur stderr -__epending= -function eflush() { -# Afficher les messages en attente - if [ -n "$__epending" ]; then recho "$__epending" 1>&2; __epending=; fi -} -function eclearp() { -# Supprimer les message en attente - __epending= -} -function eerror() { -# Afficher un message d'erreur - show_error || return; eflush; __eerror "$*" 1>&2 -} - -function die() { - [ $# -gt 0 ] && eerror "$@" - exit 1 -} - -function exit_with { - if [ $# -gt 0 ]; then "$@"; fi - exit $? -} - -function die_with { - [ $# -gt 0 ] && eerror "$1" - shift - [ $# -gt 0 ] && "$@" - exit 1 -} - -function die_unless() { - # Afficher $1 et quitter le script avec die() si la commande $2..@ retourne FAUX - local du__r - local du__msg="$1"; shift - if [ $# -eq 0 ]; then - [ -n "$du__msg" ] && _eerror "$du__msg" - exit 1 - elif "$@"; then - : - else - du__r=$? - [ -n "$du__msg" ] && _eerror "$du__msg" - exit $du__r - fi - return 0 -} - -function eerror_unless() { - # Afficher $1 avec eerror() si la commande $2..@ retourne FAUX. dans tous les cas, retourner le code de retour de la commande. - local eu__r - local eu__msg="$1"; shift - if [ $# -eq 0 ]; then - [ -n "$eu__msg" ] && _eerror "$eu__msg" - return 1 - elif "$@"; then - : - else - eu__r=$? - [ -n "$eu__msg" ] && _eerror "$eu__msg" - return $eu__r - fi - return 0 -} - -function die_if() { - # Afficher $1 et quitter le script avec die() si la commande $2..@ retourne VRAI. sinon, retourner le code de retour de la commande - local di__r=0 - local di__msg="$1"; shift - [ $# -eq 0 ] && return 0 - if "$@"; then - [ -n "$di__msg" ] && _eerror "$di__msg" - exit 0 - else - di__r=$? - fi - return $di__r -} - -function eerror_if() { - # Afficher $1 avec eerror() si la commande $2..@ retourne VRAI. dans tous les cas, retourner le code de retour de la commande. - local ei__r=0 - local ei__msg="$1"; shift - [ $# -eq 0 ] && return 0 - if "$@"; then - [ -n "$ei__msg" ] && _eerror "$ei__msg" - else - ei__r=$? - fi - return $ei__r -} - -function ewarn() { -# Afficher un message d'avertissement - show_warn || return; eflush; __ewarn "$*" 1>&2 -} -function enote() { -# Afficher un message d'information de même niveau qu'un avertissement - show_info || return; eflush; __enote "$*" 1>&2 -} -function ebanner() { -# Afficher un message très important encadré, puis attendre 5 secondes - show_error || return; eflush; __ebanner "$*" 1>&2; sleep 5 -} -function eimportant() { -# Afficher un message très important - show_error || return; eflush; __eimportant "$*" 1>&2 -} -function eattention() { -# Afficher un message important - show_warn || return; eflush; __eattention "$*" 1>&2 -} -function einfo() { -# Afficher un message d'information - show_info || return; eflush; __einfo "$*" 1>&2 -} -function eecho() { -# Afficher un message d'information sans préfixe - show_info || return; eflush; __eecho "$*" 1>&2 -} -function eecho_() { - show_info || return; eflush; __eecho_ "$*" 1>&2 -} -function edebug() { -# Afficher un message de debug - show_debug || return; eflush; __edebug "$*" 1>&2 -} -function trace() { -# Afficher la commande $1..@, la lancer, puis afficher son code d'erreur si une -# erreur se produit - local r cmd="$(qvals "$@")" - show_info && { eflush; __eecho "\$ $cmd" 1>&2; } - "$@"; r=$? - if [ $r -ne 0 ]; then - if show_info; then - eflush; __eecho "^ [EC #$r]" 1>&2 - elif show_error; then - eflush; __eecho "^ $cmd [EC #$r]" 1>&2; - fi - fi - return $r -} -function trace_error() { -# Lancer la commande $1..@, puis afficher son code d'erreur si une erreur se -# produit. La différence avec trace() est que la commande n'est affichée que si -# une erreur se produit. - local r - "$@"; r=$? - if [ $r -ne 0 ]; then - local cmd="$(qvals "$@")" - show_error && { eflush; __eecho "^ $cmd [EC #$r]" 1>&2; } - fi - return $r -} - -function etitle() { -# Afficher le titre $1, qui est le début éventuel d'une section. Les section -# imbriquées sont affichées indentées. La section n'est pas terminée, et il faut -# la terminer explicitement avec eend, sauf dans certains cas précis: -# - Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le -# contexte de la section. Puis, la section est automatiquement terminée sauf si -# l'option -s est spécifiée, auquel cas la section reste ouverte. Si l'option -p -# est spécifiée, eclearp() est appelé pour purger les messages en attente -# - Dans le cas contraire, l'option -s est ignorée: la section doit toujours -# être terminée explicitement. -# La fonction etitled() est comme etitle(), mais le titre n'est pas affiché -# immédiatement. L'affichage effectif est effectué dès qu'une fonction e* est -# utilisée. Ceci permet, avec la fonction eclearp(), de ne pas afficher de titre -# pour une section vide. - local __t_deferred= - __t_etitle "$@" -} -function etitled() { - local __t_deferred=1 - __t_etitle "$@" -} -function __t_etitle() { - local __t_eend=default - local __t_clearp= - while [ -n "$1" ]; do - if [ "$1" == "--" ]; then - shift - break - elif [ "$1" == "-s" ]; then - __t_eend= - shift - elif [ "$1" == "--eend" ]; then - __t_eend=1 - shift - elif [ "$1" == "-p" ]; then - __t_clearp=1 - shift - else - break - fi - done - local __t_title="$1"; shift - local __t_s=0 - # etitle - [ -n "$__estack" ] && __tlevel="${__tlevel} " - __estack="$__estack:t" - if show_info; then - if [ -n "$__t_deferred" ]; then - __epending="${__epending:+$__epending -}$(__etitle "$__t_title")" - else - eflush - __etitle "$__t_title" 1>&2 - fi - fi - # commande - if [ $# -gt 0 ]; then - "$@" - __t_s=$? - [ "$__t_eend" == "default" ] && __t_eend=1 - fi - # eend - [ "$__t_eend" == "default" ] && __t_eend= - if [ -n "$__t_eend" ]; then - eend $__t_s - [ -n "$__t_clearp" ] && eclearp - fi - return $__t_s -} -function estep() { -# Afficher la description d'une opération. Cette fonction est particulièrement -# appropriée dans le contexte d'un etitle. -# Les variantes e (error), w (warning), n (note), i (info) permettent d'afficher -# des couleurs différentes, mais toutes sont du niveau info. - show_info || return; eflush; __estep "$*" 1>&2 -} -function estepe() { - show_info || return; eflush; __estepe "$*" 1>&2 -} -function estepw() { - show_info || return; eflush; __estepw "$*" 1>&2 -} -function estepn() { - show_info || return; eflush; __estepn "$*" 1>&2 -} -function estepi() { - show_info || return; eflush; __estepi "$*" 1>&2 -} -function estep_() { - show_info || return; eflush; __estep_ "$*" 1>&2 -} -function estepe_() { - show_info || return; eflush; __estepe_ "$*" 1>&2 -} -function estepw_() { - show_info || return; eflush; __estepw_ "$*" 1>&2 -} -function estepn_() { - show_info || return; eflush; __estepn_ "$*" 1>&2 -} -function estepi_() { - show_info || return; eflush; __estepi_ "$*" 1>&2 -} -function ebegin() { -# Afficher le message $1, qui décrit le début d'une opération. Cette fonction -# débute une section, qu'il faut terminer avec eend. -# Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le -# contexte de la section. Puis, la section est terminée automatiquement, sauf si -# l'option -s est spécifiée, auquel cas la section reste ouverte. - local __b_eend=default - while [ -n "$1" ]; do - if [ "$1" == "--" ]; then - shift - break - elif [ "$1" == "-s" ]; then - __b_eend= - shift - elif [ "$1" == "--eend" ]; then - __b_eend=1 - shift - else - break - fi - done - local __b_msg="$1"; shift - local __b_s=0 - # ebegin - __estack="$__estack:b" - if show_info; then - eflush - __ebegin "$__b_msg" 1>&2 - fi - # commande - if [ $# -gt 0 ]; then - "$@" - __b_s=$? - [ "$__b_eend" == "default" ] && __b_eend=1 - fi - # eend - [ "$__b_eend" == "default" ] && __b_eend= - [ -n "$__b_eend" ] && eend $__b_s - return $__b_s -} -function edot() { -# Afficher une étape d'une opération, matérialisée par un point '.' ou une -# croix 'x' en cas de succès ou d'erreur. Cette fonction est particulièrement -# appropriée dans le contexte d'un ebegin. - local s=$? - show_info || return - eflush - [ -n "$1" ] && s="$1" - shift - if [ "$s" == "0" ]; then - __edoto 1>&2 - else - __edotx 1>&2 - fi - show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2 - return $s -} -function edotw() { -# Afficher un avertissement comme étape d'une opération, matérialisée par une -# lettre 'w' (typiquement de couleur jaune). Cette fonction est particulièrement -# appropriée dans le contexte d'un ebegin. - local s=$? - show_info || return - eflush - [ -n "$1" ] && s="$1" - shift - __edotw 1>&2 - show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2 - return $s -} -function ewait() { -# Afficher les étapes d'une opération qui dure, matérialisées par des '+' toutes -# les secondes tant que le processus $1 tourne. -# A utiliser de cette manière: -# ebegin "msg" -# cmd & -# ewait $! -# eend - [ -n "$1" ] || return 1 - if show_info; then - local count=2 - eflush - little_sleep # certains processus retournent tout de suite - while is_running "$1"; do - sleep 1 - if [ $count -gt 0 ]; then - # attendre 2 secondes avant de commencer à afficher des '+' - count=$(($count - 1)) - else - __edotp 1>&2 - fi - done - # terminer par un '.' - __edoto 1>&2 - else - # ne rien afficher, mais attendre quand même la fin de l'opération - wait "$1" - fi -} -function eend() { -# Terminer une section. -# Avec l'option -c, remettre à zéro toutes les informations de section -# Si la section en cours est un ebegin, afficher la fin de l'opération: [ok] ou -# [error] en fonction du code de retour de la dernière commande (ou de $1 si -# cette valeur est donnée) -# Si la section en cours est un etitle, marquer la fin de la section concernée -# par le titre. - local s=$? - if [ "$1" == "-c" ]; then - __estack= - __tlevel= - elif [ "${__estack%:b}" != "$__estack" ]; then - # terminer ebegin - __estack="${__estack%:b}" - show_info || return - eflush - [ -n "$1" ] && s="$1" - if [ "$s" == "0" ]; then - __eendo 1>&2 - else - __eendx 1>&2 - fi - elif [ "${__estack%:t}" != "$__estack" ]; then - # terminer etitle -s - __estack="${__estack%:t}" - __tlevel="${__tlevel% }" - fi -} -function __elinedots() { - ebegin "$1" - local line - if show_debug; then - while read line; do - __edoto 1>&2 - __edotd "$line" 1>&2 - done - else - while read line; do - __edoto 1>&2 - done - fi - eend -} -function elinedots() { -# Afficher un message comme avec ebegin "$1", puis afficher un point '.' pour -# chaque ligne lue sur stdin. Cela permet de suivre une opération. En mode -# DEBUG, afficher la ligne affichée plutôt qu'un point. -# Si $2..$* sont spécifiés, lancer la commande et suivre sa sortie. Ainsi, -# 'elinedots msg cmd args' est un raccourci pour 'cmd args | elinedots msg' - local msg="$1"; shift - if [ $# -gt 0 ]; then - "$@" | __elinedots "$msg" - else - __elinedots "$msg" - fi -} - -################################################################################ -# saisie - -function toienc() { -# Transformer la valeur de la variable $1 de l'encoding d'entrée -# $3(=$NUCORE_INPUT_ENCODING) vers l'encoding $2(=$NUCORE_IENC) - local __tie_var="$1" __tie_to="${2:-$NUCORE_IENC}" __tie_from="${3:-$NUCORE_INPUT_ENCODING}" - if [ "$__tie_from" != "$__tie_to" ]; then - _setv "$__tie_var" "$(iconv -f "$__tie_from" -t "$__tie_to" <<<"${!__tie_var}")" - fi -} - -function uread() { -# Lire une valeur sur stdin et la placer dans la variable $1. On assume que la -# valeur en entrée est encodée dans l'encoding d'entrée par défaut - [ $# -gt 0 ] || set -- REPLY - local __r_var - read "$@" - for __r_var in "$@"; do - [ -z "$__r_var" -o "${__r_var:0:1}" == "-" ] && continue # ignorer les options - toienc "$__r_var" - done -} - -function stoienc() { ### XXX -# Transformer la valeur lue sur stdin de $NUCORE_IENC vers l'encoding d'entrée par -# défaut ($NUCORE_INPUT_ENCODING) - local to="${1:-$NUCORE_IENC}" from="${2:-$NUCORE_INPUT_ENCODING}" - if [ "$from" == "$to" ]; then - cat - else - iconv -f "$from" -t "$to" - fi -} - - - -function is_interaction() { - return 1 -} - -function get_interaction_option() { :;} - -function ask_yesno() { -# Afficher le message $1 suivi de [oN] ou [On] suivant que $2 vaut O ou N, puis -# lire la réponse. Retourner 0 si la réponse est vrai, 1 sinon. -# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si -# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées -# ($2=message, $3=default) -# Si $2 vaut C, la valeur par défaut est N si on est interactif, O sinon -# Si $2 vaut X, la valeur par défaut est O si on est interactif, N sinon - local interactive=1 - if [[ "$1" == -* ]]; then - if [ "$1" != -- ]; then - check_interaction "$1" || interactive= - fi - shift - else - check_interaction -c || interactive= - fi - local default="${2:-N}" - if [ "$default" == "C" ]; then - [ -n "$interactive" ] && default=N || default=O - elif [ "$default" == "X" ]; then - [ -n "$interactive" ] && default=O || default=N - fi - if [ -n "$interactive" ]; then - eflush - local message="$1" - local prompt="[oN]" - local r - is_yes "$default" && prompt="[On]" - if [ -n "$message" ]; then - __eecho_ "$message" 1>&2 - else - NUCORE_OENC="$NUCORE__UTF8" __eecho_ "Voulez-vous continuer?" 1>&2 - fi - NUCORE_OENC="$NUCORE__UTF8" tooenc_ " $prompt " 1>&2 - uread r - is_yes "${r:-$default}" - else - is_yes "$default" - fi -} - -function ask_any() { -# Afficher le message $1 suivi du texte "[$2]" (qui vaut par défaut +Oq), puis -# lire la réponse. Les lettres de la chaine de format $2 sont numérotées de 0 à -# $((${#2} - 1)). Le code de retour est le numéro de la lettre qui a été -# sélectionnée. Cette fonction est une généralisation de ask_yesno() pour -# n'importe quel ensemble de lettres. -# La première lettre en majuscule est la lettre sélectionnée par défaut. -# La lettre O matche toutes les lettres qui signifient oui: o, y, 1, v, t -# La lettre N matche toutes les lettres qui signifient non: n, f, 0 -# Il y a des raccourcis: -# +O --> On -# +N --> oN -# +C --> oN si on est en mode interactif, On sinon -# +X --> On si on est en mode interactifn oN sinon -# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si -# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées -# ($2=message, $3=format) - local interactive=1 - if [[ "$1" == -* ]]; then - if [ "$1" != -- ]; then - check_interaction "$1" || interactive= - fi - shift - else - check_interaction -c || interactive= - fi - local format="${2:-+Oq}" - format="${format/+O/On}" - format="${format/+N/oN}" - if [ -n "$interactive" ]; then - format="${format/+C/oN}" - format="${format/+X/On}" - else - format="${format/+C/On}" - format="${format/+X/oN}" - fi - local i count="${#format}" - - if [ -n "$interactive" ]; then - eflush - local message="${1:-Voulez-vous continuer?}" - local prompt="[$format]" - local r f lf defi - while true; do - __eecho_ "$message $prompt " 1>&2 - uread r - r="$(strlower "${r:0:1}")" - i=0; defi= - while [ $i -lt $count ]; do - f="${format:$i:1}" - lf="$(strlower "$f")" - [ "$r" == "$lf" ] && return $i - if [ -z "$defi" ]; then - [ -z "${f/[A-Z]/}" ] && defi="$i" - fi - if [ "$lf" == o ]; then - case "$r" in o|y|1|v|t) return $i;; esac - elif [ "$lf" == n ]; then - case "$r" in n|f|0) return $i;; esac - fi - i=$(($i + 1)) - done - [ -z "$r" ] && return ${defi:-0} - done - else - i=0 - while [ $i -lt $count ]; do - f="${format:$i:1}" - [ -z "${f/[A-Z]/}" ] && return $i - i=$(($i + 1)) - done - return 0 - fi -} - -function read_value() { -# Afficher le message $1 suivi de la valeur par défaut [$3] si elle est non -# vide, puis lire la valeur donnée par l'utilisateur. Cette valeur doit être non -# vide si $4(=O) est vrai. La valeur saisie est placée dans la variable -# $2(=value) -# Si $1 est une option, elle est utilisée avec check_interaction pour savoir si -# on est en mode interactif ou non. A ce moment-là, les valeurs sont décalées -# ($2=message, $3=variable, $4=default, $5=required) -# En mode non interactif, c'est la valeur par défaut qui est sélectionnée. Si -# l'utilisateur requière que la valeur soit non vide et que la valeur par défaut -# est vide, afficher un message d'erreur et retourner faux -# read_password() est comme read_value(), mais la valeur saisie n'est pas -# affichée, ce qui la rend appropriée pour la lecture d'un mot de passe. - local -a __rv_opts __rv_readline=1 __rv_showdef=1 __rv_nl= - __rv_opts=() - [ -n "$NUCORE_NO_READLINE" ] && __rv_readline= - __rv_read "$@" -} - -function read_password() { - local -a __rv_opts __rv_readline= __rv_showdef= __rv_nl=1 - __rv_opts=(-s) - __rv_read "$@" -} - -function __rv_read() { - local __rv_int=1 - if [[ "$1" == -* ]]; then - if [ "$1" != -- ]; then - check_interaction "$1" || __rv_int= - fi - shift - else - check_interaction -c || __rv_int= - fi - local __rv_msg="$1" __rv_v="${2:-value}" __rv_d="$3" __rv_re="${4:-O}" - if [ -z "$__rv_int" ]; then - # En mode non interactif, retourner la valeur par défaut - if is_yes "$__rv_re" && [ -z "$__rv_d" ]; then - NUCORE_OENC="$NUCORE__UTF8" eerror "La valeur par défaut de $__rv_v doit être non vide" - return 1 - fi - _setv "$__rv_v" "$__rv_d" - return 0 - fi - - eflush - local __rv_r - while true; do - if [ -n "$__rv_msg" ]; then - __eecho_ "$__rv_msg" 1>&2 - else - NUCORE_OENC="$NUCORE__UTF8" __eecho_ "Entrez la valeur" 1>&2 - fi - if [ -n "$__rv_readline" ]; then - NUCORE_OENC="$NUCORE__UTF8" tooenc_ ": " 1>&2 - uread -e ${__rv_d:+-i"$__rv_d"} "${__rv_opts[@]}" __rv_r - else - if [ -n "$__rv_d" ]; then - if [ -n "$__rv_showdef" ]; then - tooenc_ " [$__rv_d]" 1>&2 - else - tooenc_ " [****]" 1>&2 - fi - fi - NUCORE_OENC="$NUCORE__UTF8" tooenc_ ": " 1>&2 - uread "${__rv_opts[@]}" __rv_r - [ -n "$__rv_nl" ] && echo - fi - __rv_r="${__rv_r:-$__rv_d}" - if [ -n "$__rv_r" ] || ! is_yes "$__rv_re"; then - _setv "$__rv_v" "$__rv_r" - return 0 - fi - done -} - -function simple_menu() { -# Afficher un menu simple dont les éléments sont les valeurs du tableau -# $2(=options). L'option choisie est placée dans la variable $1(=option) -# -t TITLE: spécifier le titre du menu -# -m YOUR_CHOICE: spécifier le message d'invite pour la sélection de l'option -# -d DEFAULT: spécifier l'option par défaut. Par défaut, prendre la valeur -# actuelle de la variable $1(=option) - local __sm_title= __sm_yourchoice= __sm_default= - local -a __sm_args - parse_opts -t: __sm_title= -m: __sm_yourchoice= -d: __sm_default= @ __sm_args -- "$@" && - set -- "${__sm_args[@]}" || ewarn "$__sm_args" - - local __sm_option_var="${1:-option}" __sm_options_var="${2:-options}" - local __sm_option __sm_options - __sm_options="$__sm_options_var[*]" - if [ -z "${!__sm_options}" ]; then - NUCORE_OENC="$NUCORE__UTF8" eerror "Le tableau $__sm_options_var doit être non vide" - return 1 - fi - [ -z "$__sm_default" ] && __sm_default="${!__sm_option_var}" - - eflush - array_copy __sm_options "$__sm_options_var" - local __sm_c=0 __sm_i __sm_choice - while true; do - if [ "$__sm_c" == "0" ]; then - # Afficher le menu - [ -n "$__sm_title" ] && __eecho "=== $__sm_title ===" 1>&2 - __sm_i=1 - for __sm_option in "${__sm_options[@]}"; do - if [ "$__sm_option" == "$__sm_default" ]; then - __eecho "$__sm_i*- $__sm_option" 1>&2 - else - __eecho "$__sm_i - $__sm_option" 1>&2 - fi - let __sm_i=$__sm_i+1 - done - fi - - # Afficher les choix - if [ -n "$__sm_yourchoice" ]; then - __eecho_ "$__sm_yourchoice" 1>&2 - else - NUCORE_OENC="$NUCORE__UTF8" __eecho_ "Entrez le numéro de l'option choisie" 1>&2 - fi - NUCORE_OENC="$NUCORE__UTF8" tooenc_ ": " 1>&2 - uread __sm_choice - - # Valeur par défaut - if [ -z "$__sm_choice" -a -n "$__sm_default" ]; then - __sm_option="$__sm_default" - break - fi - # Vérifier la saisie - if [ -n "$__sm_choice" -a -z "${__sm_choice//[0-9]/}" ]; then - if [ "$__sm_choice" -gt 0 -a "$__sm_choice" -le "${#__sm_options[*]}" ]; then - __sm_option="${__sm_options[$(($__sm_choice - 1))]}" - break - else - NUCORE_OENC="$NUCORE__UTF8" eerror "Numéro d'option incorrect" - fi - else - NUCORE_OENC="$NUCORE__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 - NUCORE_OENC="$NUCORE__UTF8" tooenc "" 1>&2 - __sm_c=0 - fi - done - _setv "$__sm_option_var" "$__sm_option" -} - -function actions_menu() { -# Afficher un menu dont les éléments sont les valeurs du tableau $4(=options), -# et une liste d'actions tirées du tableau $3(=actions). L'option choisie est -# placée dans la variable $2(=option). L'action choisie est placée dans la -# variable $1(=action) -# Un choix est saisi sous la forme [action]num_option -# -t TITLE: spécifier le titre du menu -# -m OPT_YOUR_CHOICE: spécifier le message d'invite pour la sélection de -# l'action et de l'option -# -M ACT_YOUR_CHOICE: spécifier le message d'invite dans le cas où aucune option -# n'est disponible. Dans ce cas, seules les actions vides sont possibles. -# -e VOID_ACTION: spécifier qu'une action est vide, c'est à dire qu'elle ne -# requière pas d'être associée à une option. Par défaut, la dernière action -# est classée dans cette catégorie puisque c'est l'action "quitter" -# -d DEFAULT_ACTION: choisir l'action par défaut. par défaut, c'est la première -# action. -# -q QUIT_ACTION: choisir l'option "quitter" qui provoque la sortie du menu sans -# choix. par défaut, c'est la dernière action. -# -o DEFAULT_OPTION: choisir l'option par défaut. par défaut, prendre la valeur -# actuelle de la variable $2(=option) - local -a __am_action_descs __am_options __am_void_actions - local __am_tmp __am_select_action __am_select_option __am_title __am_optyc __am_actyc - local __am_default_action=auto __am_quit_action=auto - local __am_default_option= - local -a __am_args - parse_opts \ - -t: __am_title= \ - -m: __am_optyc= \ - -M: __am_actyc= \ - -e: __am_void_actions \ - -d: __am_default_action= \ - -q: __am_quit_action= \ - -o: __am_default_option= \ - @ __am_args -- "$@" && set -- "${__am_args[@]}" || { eerror "$__am_args"; return 1; } - - __am_tmp="${1:-action}"; __am_select_action="${!__am_tmp}" - __am_tmp="${2:-option}"; __am_select_option="${!__am_tmp}" - [ -n "$__am_default_option" ] && __am_select_option="$__am_default_option" - array_copy __am_action_descs "${3:-actions}" - array_copy __am_options "${4:-options}" - - eerror_unless [ ${#__am_action_descs[*]} -gt 0 ] "Vous devez spécifier le tableau des actions" || return - __actions_menu || return 1 - _setv "${1:-action}" "$__am_select_action" - _setv "${2:-option}" "$__am_select_option" -} - -function __actions_menu() { - local title="$__am_title" - local optyc="$__am_optyc" actyc="$__am_actyc" - local default_action="$__am_default_action" - local quit_action="$__am_quit_action" - local select_action="$__am_select_action" - local select_option="$__am_select_option" - local -a action_descs options void_actions - array_copy action_descs __am_action_descs - array_copy options __am_options - array_copy void_actions __am_void_actions - - # Calculer la liste des actions valides - local no_options - array_isempty options && no_options=1 - - local -a actions - local tmp action name - for tmp in "${action_descs[@]}"; do - splitfsep2 "$tmp" : action name - [ -n "$action" ] || action="${name:0:1}" - action="$(strlower "$action")" - array_addu actions "$action" - done - - # Calculer l'action par défaut - if [ "$default_action" == auto ]; then - # si action par défaut non spécifiée, alors prendre la première action - default_action="$select_action" - if [ -n "$default_action" ]; then - array_contains actions "$default_action" || default_action= - fi - [ -n "$default_action" ] || default_action="${actions[0]}" - fi - default_action="${default_action:0:1}" - default_action="$(strlower "$default_action")" - - # Calculer l'action quitter par défaut - if [ "$quit_action" == auto ]; then - # si action par défaut non spécifiée, alors prendre la dernière action, - # s'il y a au moins 2 actions - if [ ${#actions[*]} -gt 1 ]; then - quit_action="${actions[@]:$((-1)):1}" - array_addu void_actions "$quit_action" - fi - fi - quit_action="${quit_action:0:1}" - quit_action="$(strlower "$quit_action")" - - # Calculer la ligne des actions à afficher - local action_title - for tmp in "${action_descs[@]}"; do - splitfsep2 "$tmp" : action name - [ -n "$action" ] || action="${name:0:1}" - [ -n "$name" ] || name="$action" - action="$(strlower "$action")" - if [ -n "$no_options" ]; then - if ! array_contains void_actions "$action"; then - array_del actions "$action" - continue - fi - fi - [ "$action" == "$default_action" ] && name="$name*" - action_title="${action_title:+$action_title/}$name" - done - if [ -n "$default_action" ]; then - # si action par défaut invalide, alors pas d'action par défaut - array_contains actions "$default_action" || default_action= - fi - if [ -n "$quit_action" ]; then - # si action quitter invalide, alors pas d'action quitter - array_contains actions "$quit_action" || quit_action= - fi - - # Type de menu - if [ -n "$no_options" ]; then - if array_isempty void_actions; then - eerror "Aucune option n'est définie. Il faut définir le tableau des actions vides" - return 1 - fi - __void_actions_menu - else - __options_actions_menu - fi -} - -function __void_actions_menu() { - eflush - local c=0 choice - while true; do - if [ $c -eq 0 ]; then - [ -n "$title" ] && __etitle "$title" 1>&2 - __eecho_ "=== Actions disponibles: " 1>&2 - tooenc "$action_title" 1>&2 - fi - if [ -n "$actyc" ]; then - __eecho_ "$actyc" 1>&2 - elif [ -n "$optyc" ]; then - __eecho_ "$optyc" 1>&2 - else - __eecho_ "Entrez l'action à effectuer" 1>&2 - fi - tooenc_ ": " 1>&2 - uread choice - if [ -z "$choice" -a -n "$default_action" ]; then - select_action="$default_action" - break - fi - - # vérifier la saisie - choice="${choice:0:1}" - choice="$(strlower "$choice")" - if array_contains actions "$choice"; then - select_action="$choice" - break - elif [ -n "$choice" ]; then - eerror "$choice: action incorrecte" - else - eerror "vous devez saisir l'action à effectuer" - fi - let c=$c+1 - if [ $c -eq 5 ]; then - # sauter une ligne toutes les 4 tentatives - tooenc "" 1>&2 - c=0 - fi - done - __am_select_action="$select_action" - __am_select_option= -} - -function __options_actions_menu() { - eflush - local c=0 option choice action option - while true; do - if [ $c -eq 0 ]; then - [ -n "$title" ] && __etitle "$title" 1>&2 - i=1 - for option in "${options[@]}"; do - if [ "$option" == "$select_option" ]; then - tooenc "$i*- $option" 1>&2 - else - tooenc "$i - $option" 1>&2 - fi - let i=$i+1 - done - __estepn_ "Actions disponibles: " 1>&2 - tooenc "$action_title" 1>&2 - fi - if [ -n "$optyc" ]; then - __eecho_ "$optyc" 1>&2 - else - __eecho_ "Entrez l'action et le numéro de l'option choisie" 1>&2 - fi - tooenc_ ": " 1>&2 - uread choice - - # vérifier la saisie - if [ -z "$choice" -a -n "$default_action" ]; then - action="$default_action" - if array_contains void_actions "$action"; then - select_action="$action" - select_option= - break - elif [ -n "$select_option" ]; then - select_action="$action" - break - fi - fi - action="${choice:0:1}" - action="$(strlower "$action")" - if array_contains actions "$action"; then - # on commence par un code d'action valide. cool :-) - if array_contains void_actions "$action"; then - select_action="$action" - select_option= - break - else - option="${choice:1}" - option="${option// /}" - if [ -z "$option" -a -n "$select_option" ]; then - select_action="$action" - break - elif [ -z "$option" ]; then - eerror "vous devez saisir le numéro de l'option" - elif isnum "$option"; then - if [ $option -gt 0 -a $option -le ${#options[*]} ]; then - select_action="$action" - select_option="${options[$(($option - 1))]}" - break - fi - else - eerror "$option: numéro d'option incorrecte" - fi - fi - elif isnum "$choice"; then - # on a simplement donné un numéro d'option - action="$default_action" - if [ -n "$action" ]; then - if array_contains void_actions "$action"; then - select_action="$action" - select_option= - break - else - option="${choice// /}" - if [ -z "$option" ]; then - eerror "vous devez saisir le numéro de l'option" - elif isnum "$option"; then - if [ $option -gt 0 -a $option -le ${#options[*]} ]; then - select_action="$action" - select_option="${options[$(($option - 1))]}" - break - fi - else - eerror "$option: numéro d'option incorrecte" - fi - fi - else - eerror "Vous devez spécifier l'action à effectuer" - fi - elif [ -n "$choice" ]; then - eerror "$choice: action et/ou option incorrecte" - else - eerror "vous devez saisir l'action à effectuer" - fi - let c=$c+1 - if [ $c -eq 5 ]; then - # sauter une ligne toutes les 4 tentatives - tooenc "" 1>&2 - c=0 - fi - done - __am_select_action="$select_action" - __am_select_option="$select_option" -} diff --git a/bash/src/base.output.sh b/bash/src/base.output.sh new file mode 100644 index 0000000..d8f1f64 --- /dev/null +++ b/bash/src/base.output.sh @@ -0,0 +1,646 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +##@cooked nocomments +module: base.output "Fonctions de base: affichage" + +NUCORE__TAB=$'\t' +NUCORE__LATIN1=iso-8859-1 +NUCORE__LATIN9=iso-8859-15 +NUCORE__UTF8=utf-8 + +if [ ! -x "$(which iconv 2>/dev/null)" ]; then + function iconv() { cat; } +fi + +function nucore__lang_encoding() { + case "${LANG,,}" in + *@euro) echo "iso-8859-15";; + *.utf-8|*.utf8) echo "utf-8";; + *) echo "iso-8859-1";; + esac +} + +function nucore__norm_encoding() { + local enc="${1,,}" + enc="${enc//[-_]/}" + case "$enc" in + latin|latin1|iso8859|iso88591|8859|88591) echo "iso-8859-1";; + latin9|iso885915|885915) echo "iso-8859-15";; + utf|utf8) echo "utf-8";; + *) echo "$1";; + esac +} + +function nucore__init_encoding() { + local default_encoding="$(nucore__lang_encoding)" + [ -n "$default_encoding" ] || default_encoding=utf-8 + [ -n "$NUCORE_OUTPUT_ENCODING" ] || NUCORE_OUTPUT_ENCODING="$default_encoding" + NUCORE_OUTPUT_ENCODING="$(nucore__norm_encoding "$NUCORE_OUTPUT_ENCODING")" + [ -n "$NUCORE_INPUT_ENCODING" ] || NUCORE_INPUT_ENCODING="$NUCORE_OUTPUT_ENCODING" + NUCORE_INPUT_ENCODING="$(nucore__norm_encoding "$NUCORE_INPUT_ENCODING")" +} +[ -n "$NUCORE_LANG" -a -z "$LANG" ] && export NUCORE_LANG LANG="$NUCORE_LANG" +nucore__init_encoding + +function noerror() { +# lancer la commande "$@" et masquer son code de retour + [ $# -gt 0 ] || set : + "$@" || return 0 +} + +function noout() { +# lancer la commande "$@" en supprimant sa sortie standard + [ $# -gt 0 ] || return 0 + "$@" >/dev/null +} + +function noerr() { +# lancer la commande "$@" en supprimant sa sortie d'erreur + [ $# -gt 0 ] || return 0 + "$@" 2>/dev/null +} + +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. identique à isatty() + tty -s <&1 +} + +function err_isatty() { +# tester si STDERR n'est pas une redirection + tty -s <&2 +} + +################################################################################ + +function tooenc() { +# $1 étant une chaine encodée en utf-8, l'afficher dans l'encoding de sortie $2 +# qui vaut par défaut $NUCORE_OUTPUT_ENCODING + local value="$1" to="${2:-$NUCORE_OUTPUT_ENCODING}" + if [ "$to" == "$NUCORE__UTF8" ]; then + recho "$value" + else + iconv -f -utf-8 -t "$to" <<<"$value" + fi +} +function uecho() { tooenc "$*"; } + +function tooenc_() { +# $1 étant une chaine encodée en utf-8, l'afficher sans passer à la ligne dans +# l'encoding de sortie $2 qui vaut par défaut $NUCORE_OUTPUT_ENCODING + local value="$1" to="${2:-$NUCORE_OUTPUT_ENCODING}" + if [ "$to" == "$NUCORE__UTF8" ]; then + recho_ "$value" + else + recho_ "$value" | iconv -f utf-8 -t "$to" + fi +} +function uecho_() { tooenc_ "$*"; } + +# faut-il dater les messages de etitle, estep, ebegin? +# Faire NUCORE_EDATE=1 en début de script pour activer cette fonctionnalité +export NUCORE_EDATE +function __edate() { [ -n "$NUCORE_EDATE" ] && date +"[%d/%m/%Y-%H:%M:%S] "; } + +export NUCORE_ELOG_OVERWRITE +function __set_no_colors() { :; } +function elogto() { +# Activer NUCORE_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 NUCORE_ELOG_OVERWRITE=1, alors le fichier en sortie est écrasé. Sinon, les +# lignes en sortie lui sont ajoutées + NUCORE_EDATE=1 + if [ -n "$1" -a -n "$2" ]; then + LANG=fr_FR.UTF8 + NUCORE_OUTPUT_ENCODING="$NUCORE__UTF8" + __set_no_colors 1 + if [ -n "$NUCORE_ELOG_OVERWRITE" ]; then + exec >"$1" 2>"$2" + else + exec >>"$1" 2>>"$2" + fi + elif [ -n "$1" ]; then + LANG=fr_FR.UTF8 + NUCORE_OUTPUT_ENCODING="$NUCORE__UTF8" + __set_no_colors 1 + if [ -n "$NUCORE_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 +# NUCORE__ESTACK est la liste des invocations de 'ebegin' et 'etitle' en cours +# NUCORE__TLEVEL est l'indentation à appliquer avant d'afficher le message +export NUCORE__ESTACK NUCORE__TLEVEL +function __indent() { +# indenter les lignes de $1, sauf la première + if [ "${1/ +/}" != "$1" ]; then + sed "2,\$s/^/${NUCORE__TLEVEL}/g" <<<"$1" + else + recho "$1" + fi +} +# fonctions à surcharger pour modifier la façon dont les messages sont affichés +function __eerror() { tooenc "$(__edate)${NUCORE__TLEVEL}ERROR $(__indent "$1")"; } +function __ewarn() { tooenc "$(__edate)${NUCORE__TLEVEL}WARNING $(__indent "$1")"; } +function __enote() { tooenc "$(__edate)${NUCORE__TLEVEL}NOTE $(__indent "$1")"; } +function __ebanner() { + local maxi="${COLUMNS:-80}" + local -a lines + local psfix line + + psfix="$(__edate)${NUCORE__TLEVEL}" + while [ ${#psfix} -lt $maxi ]; do psfix="$psfix="; done + + tooenc "$psfix" + maxi=$(($maxi - 1)) + array_xsplitl lines "$1" + for line in "" "${lines[@]}" ""; do + line="$(__edate)${NUCORE__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)${NUCORE__TLEVEL}IMPORTANT $(__indent "$1")"; } +function __eattention() { tooenc "$(__edate)${NUCORE__TLEVEL}ATTENTION $(__indent "$1")"; } +function __einfo() { tooenc "$(__edate)${NUCORE__TLEVEL}INFO $(__indent "$1")"; } +function __eecho() { tooenc "$(__edate)${NUCORE__TLEVEL}$(__indent "$1")"; } +function __eecho_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}$(__indent "$1")"; } +function __edebug() { tooenc "$(__edate)${NUCORE__TLEVEL}DEBUG $(__indent "$1")"; } +function __estep() { tooenc "$(__edate)${NUCORE__TLEVEL}. $(__indent "$1")"; } +function __estepe() { tooenc "$(__edate)${NUCORE__TLEVEL}.E $(__indent "$1")"; } +function __estepw() { tooenc "$(__edate)${NUCORE__TLEVEL}.W $(__indent "$1")"; } +function __estepn() { tooenc "$(__edate)${NUCORE__TLEVEL}.N $(__indent "$1")"; } +function __estepi() { tooenc "$(__edate)${NUCORE__TLEVEL}.I $(__indent "$1")"; } +function __estep_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}. $(__indent "$1")"; } +function __estepe_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}.E $(__indent "$1")"; } +function __estepw_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}.W $(__indent "$1")"; } +function __estepn_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}.N $(__indent "$1")"; } +function __estepi_() { tooenc_ "$(__edate)${NUCORE__TLEVEL}.I $(__indent "$1")"; } +function __etitle() { tooenc "$(__edate)${NUCORE__TLEVEL}=== $(__indent "$1")"; } +function __ebegin() { tooenc_ "$(__edate)${NUCORE__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() { :;} + +# note: toutes les fonctions d'affichage e* écrivent sur stderr +NUCORE__EPENDING= +function eflush() { +# Afficher les messages en attente + if [ -n "$NUCORE__EPENDING" ]; then recho "$NUCORE__EPENDING" 1>&2; NUCORE__EPENDING=; fi +} +function eclearp() { +# Supprimer les message en attente + NUCORE__EPENDING= +} +function eerror() { +# Afficher un message d'erreur + show_error || return; eflush; __eerror "$*" 1>&2 +} + +function die() { + [ $# -gt 0 ] && eerror "$@" + exit 1 +} + +function exit_with { + if [ $# -gt 0 ]; then "$@"; fi + exit $? +} + +function die_with { + [ $# -gt 0 ] && eerror "$1" + shift + [ $# -gt 0 ] && "$@" + exit 1 +} + +function die_unless() { + # Afficher $1 et quitter le script avec die() si la commande $2..@ retourne FAUX + local du__r + local du__msg="$1"; shift + if [ $# -eq 0 ]; then + [ -n "$du__msg" ] && _eerror "$du__msg" + exit 1 + elif "$@"; then + : + else + du__r=$? + [ -n "$du__msg" ] && _eerror "$du__msg" + exit $du__r + fi + return 0 +} + +function eerror_unless() { + # Afficher $1 avec eerror() si la commande $2..@ retourne FAUX. dans tous les cas, retourner le code de retour de la commande. + local eu__r + local eu__msg="$1"; shift + if [ $# -eq 0 ]; then + [ -n "$eu__msg" ] && _eerror "$eu__msg" + return 1 + elif "$@"; then + : + else + eu__r=$? + [ -n "$eu__msg" ] && _eerror "$eu__msg" + return $eu__r + fi + return 0 +} + +function die_if() { + # Afficher $1 et quitter le script avec die() si la commande $2..@ retourne VRAI. sinon, retourner le code de retour de la commande + local di__r=0 + local di__msg="$1"; shift + [ $# -eq 0 ] && return 0 + if "$@"; then + [ -n "$di__msg" ] && _eerror "$di__msg" + exit 0 + else + di__r=$? + fi + return $di__r +} + +function eerror_if() { + # Afficher $1 avec eerror() si la commande $2..@ retourne VRAI. dans tous les cas, retourner le code de retour de la commande. + local ei__r=0 + local ei__msg="$1"; shift + [ $# -eq 0 ] && return 0 + if "$@"; then + [ -n "$ei__msg" ] && _eerror "$ei__msg" + else + ei__r=$? + fi + return $ei__r +} + +function ewarn() { +# Afficher un message d'avertissement + show_warn || return; eflush; __ewarn "$*" 1>&2 +} +function enote() { +# Afficher un message d'information de même niveau qu'un avertissement + show_info || return; eflush; __enote "$*" 1>&2 +} +function ebanner() { +# Afficher un message très important encadré, puis attendre 5 secondes + show_error || return; eflush; __ebanner "$*" 1>&2; sleep 5 +} +function eimportant() { +# Afficher un message très important + show_error || return; eflush; __eimportant "$*" 1>&2 +} +function eattention() { +# Afficher un message important + show_warn || return; eflush; __eattention "$*" 1>&2 +} +function einfo() { +# Afficher un message d'information + show_info || return; eflush; __einfo "$*" 1>&2 +} +function eecho() { +# Afficher un message d'information sans préfixe + show_info || return; eflush; __eecho "$*" 1>&2 +} +function eecho_() { + show_info || return; eflush; __eecho_ "$*" 1>&2 +} +function edebug() { +# Afficher un message de debug + show_debug || return; eflush; __edebug "$*" 1>&2 +} +function trace() { +# Afficher la commande $1..@, la lancer, puis afficher son code d'erreur si une +# erreur se produit + local r cmd="$(qvals "$@")" + show_info && { eflush; __eecho "\$ $cmd" 1>&2; } + "$@"; r=$? + if [ $r -ne 0 ]; then + if show_info; then + eflush; __eecho "^ [EC #$r]" 1>&2 + elif show_error; then + eflush; __eecho "^ $cmd [EC #$r]" 1>&2; + fi + fi + return $r +} +function trace_error() { +# Lancer la commande $1..@, puis afficher son code d'erreur si une erreur se +# produit. La différence avec trace() est que la commande n'est affichée que si +# une erreur se produit. + local r + "$@"; r=$? + if [ $r -ne 0 ]; then + local cmd="$(qvals "$@")" + show_error && { eflush; __eecho "^ $cmd [EC #$r]" 1>&2; } + fi + return $r +} + +function etitle() { +# Afficher le titre $1, qui est le début éventuel d'une section. Les section +# imbriquées sont affichées indentées. La section n'est pas terminée, et il faut +# la terminer explicitement avec eend, sauf dans certains cas précis: +# - Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le +# contexte de la section. Puis, la section est automatiquement terminée sauf si +# l'option -s est spécifiée, auquel cas la section reste ouverte. Si l'option -p +# est spécifiée, eclearp() est appelé pour purger les messages en attente +# - Dans le cas contraire, l'option -s est ignorée: la section doit toujours +# être terminée explicitement. +# La fonction etitled() est comme etitle(), mais le titre n'est pas affiché +# immédiatement. L'affichage effectif est effectué dès qu'une fonction e* est +# utilisée. Ceci permet, avec la fonction eclearp(), de ne pas afficher de titre +# pour une section vide. + local __t_deferred= + __t_etitle "$@" +} +function etitled() { + local __t_deferred=1 + __t_etitle "$@" +} +function __t_etitle() { + local __t_eend=default + local __t_clearp= + while [ -n "$1" ]; do + if [ "$1" == "--" ]; then + shift + break + elif [ "$1" == "-s" ]; then + __t_eend= + shift + elif [ "$1" == "--eend" ]; then + __t_eend=1 + shift + elif [ "$1" == "-p" ]; then + __t_clearp=1 + shift + else + break + fi + done + local __t_title="$1"; shift + local __t_s=0 + # etitle + [ -n "$NUCORE__ESTACK" ] && NUCORE__TLEVEL="${NUCORE__TLEVEL} " + NUCORE__ESTACK="$NUCORE__ESTACK:t" + if show_info; then + if [ -n "$__t_deferred" ]; then + NUCORE__EPENDING="${NUCORE__EPENDING:+$NUCORE__EPENDING +}$(__etitle "$__t_title")" + else + eflush + __etitle "$__t_title" 1>&2 + fi + fi + # commande + if [ $# -gt 0 ]; then + "$@" + __t_s=$? + [ "$__t_eend" == "default" ] && __t_eend=1 + fi + # eend + [ "$__t_eend" == "default" ] && __t_eend= + if [ -n "$__t_eend" ]; then + eend $__t_s + [ -n "$__t_clearp" ] && eclearp + fi + return $__t_s +} +function estep() { +# Afficher la description d'une opération. Cette fonction est particulièrement +# appropriée dans le contexte d'un etitle. +# Les variantes e (error), w (warning), n (note), i (info) permettent d'afficher +# des couleurs différentes, mais toutes sont du niveau info. + show_info || return; eflush; __estep "$*" 1>&2 +} +function estepe() { + show_info || return; eflush; __estepe "$*" 1>&2 +} +function estepw() { + show_info || return; eflush; __estepw "$*" 1>&2 +} +function estepn() { + show_info || return; eflush; __estepn "$*" 1>&2 +} +function estepi() { + show_info || return; eflush; __estepi "$*" 1>&2 +} +function estep_() { + show_info || return; eflush; __estep_ "$*" 1>&2 +} +function estepe_() { + show_info || return; eflush; __estepe_ "$*" 1>&2 +} +function estepw_() { + show_info || return; eflush; __estepw_ "$*" 1>&2 +} +function estepn_() { + show_info || return; eflush; __estepn_ "$*" 1>&2 +} +function estepi_() { + show_info || return; eflush; __estepi_ "$*" 1>&2 +} +function ebegin() { +# Afficher le message $1, qui décrit le début d'une opération. Cette fonction +# débute une section, qu'il faut terminer avec eend. +# Si $2..$* est spécifié, c'est une commande. Lancer la commande dans le +# contexte de la section. Puis, la section est terminée automatiquement, sauf si +# l'option -s est spécifiée, auquel cas la section reste ouverte. + local __b_eend=default + while [ -n "$1" ]; do + if [ "$1" == "--" ]; then + shift + break + elif [ "$1" == "-s" ]; then + __b_eend= + shift + elif [ "$1" == "--eend" ]; then + __b_eend=1 + shift + else + break + fi + done + local __b_msg="$1"; shift + local __b_s=0 + # ebegin + NUCORE__ESTACK="$NUCORE__ESTACK:b" + if show_info; then + eflush + __ebegin "$__b_msg" 1>&2 + fi + # commande + if [ $# -gt 0 ]; then + "$@" + __b_s=$? + [ "$__b_eend" == "default" ] && __b_eend=1 + fi + # eend + [ "$__b_eend" == "default" ] && __b_eend= + [ -n "$__b_eend" ] && eend $__b_s + return $__b_s +} +function edot() { +# Afficher une étape d'une opération, matérialisée par un point '.' ou une +# croix 'x' en cas de succès ou d'erreur. Cette fonction est particulièrement +# appropriée dans le contexte d'un ebegin. + local s=$? + show_info || return + eflush + [ -n "$1" ] && s="$1" + shift + if [ "$s" == "0" ]; then + __edoto 1>&2 + else + __edotx 1>&2 + fi + show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2 + return $s +} +function edotw() { +# Afficher un avertissement comme étape d'une opération, matérialisée par une +# lettre 'w' (typiquement de couleur jaune). Cette fonction est particulièrement +# appropriée dans le contexte d'un ebegin. + local s=$? + show_info || return + eflush + [ -n "$1" ] && s="$1" + shift + __edotw 1>&2 + show_verbose && [ $# -gt 0 ] && __edotd "$*" 1>&2 + return $s +} +function ewait() { +# Afficher les étapes d'une opération qui dure, matérialisées par des '+' toutes +# les secondes tant que le processus $1 tourne. +# A utiliser de cette manière: +# ebegin "msg" +# cmd & +# ewait $! +# eend + [ -n "$1" ] || return 1 + if show_info; then + local count=2 + eflush + little_sleep # certains processus retournent tout de suite + while is_running "$1"; do + sleep 1 + if [ $count -gt 0 ]; then + # attendre 2 secondes avant de commencer à afficher des '+' + count=$(($count - 1)) + else + __edotp 1>&2 + fi + done + # terminer par un '.' + __edoto 1>&2 + else + # ne rien afficher, mais attendre quand même la fin de l'opération + wait "$1" + fi +} +function eend() { +# Terminer une section. +# Avec l'option -c, remettre à zéro toutes les informations de section +# Si la section en cours est un ebegin, afficher la fin de l'opération: [ok] ou +# [error] en fonction du code de retour de la dernière commande (ou de $1 si +# cette valeur est donnée) +# Si la section en cours est un etitle, marquer la fin de la section concernée +# par le titre. + local s=$? + if [ "$1" == "-c" ]; then + NUCORE__ESTACK= + NUCORE__TLEVEL= + elif [ "${NUCORE__ESTACK%:b}" != "$NUCORE__ESTACK" ]; then + # terminer ebegin + NUCORE__ESTACK="${NUCORE__ESTACK%:b}" + show_info || return + eflush + [ -n "$1" ] && s="$1" + if [ "$s" == "0" ]; then + __eendo 1>&2 + else + __eendx 1>&2 + fi + elif [ "${NUCORE__ESTACK%:t}" != "$NUCORE__ESTACK" ]; then + # terminer etitle -s + NUCORE__ESTACK="${NUCORE__ESTACK%:t}" + NUCORE__TLEVEL="${NUCORE__TLEVEL% }" + fi +} +function __elinedots() { + ebegin "$1" + local line + if show_debug; then + while read line; do + __edoto 1>&2 + __edotd "$line" 1>&2 + done + else + while read line; do + __edoto 1>&2 + done + fi + eend +} +function elinedots() { +# Afficher un message comme avec ebegin "$1", puis afficher un point '.' pour +# chaque ligne lue sur stdin. Cela permet de suivre une opération. En mode +# DEBUG, afficher la ligne affichée plutôt qu'un point. +# Si $2..$* sont spécifiés, lancer la commande et suivre sa sortie. Ainsi, +# 'elinedots msg cmd args' est un raccourci pour 'cmd args | elinedots msg' + local msg="$1"; shift + if [ $# -gt 0 ]; then + "$@" | __elinedots "$msg" + else + __elinedots "$msg" + fi +} diff --git a/bash/src/base.path.sh b/bash/src/base.path.sh index 37f312c..734eb3e 100644 --- a/bash/src/base.path.sh +++ b/bash/src/base.path.sh @@ -1,7 +1,6 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments module: base.path "Fonctions de base: gestion des chemins et des fichiers" -require: base.core function: in_path "tester l'existence d'un programme dans le PATH" function in_path() { diff --git a/bash/src/base.sh b/bash/src/base.sh index 0981a11..a0a3525 100644 --- a/bash/src/base.sh +++ b/bash/src/base.sh @@ -8,12 +8,12 @@ if [ -z "$NUCOREDIR" -o "$NUCOREDIR" != "$NUCOREINIT" ]; then fi ##@include base.init.sh ##@include base.core.sh -##@include base.str.sh -##@include base.arr.sh -##@include base.io.sh -##@include base.eval.sh +##@include base.string.sh +##@include base.array.sh +##@include base.output.sh +##@include base.input.sh ##@include base.split.sh ##@include base.path.sh ##@include base.args.sh module: base "Chargement de tous les modules base.*" -require: base.init base.core base.str base.arr base.io base.eval base.split base.path base.args +require: base.init base.core base.string base.array base.output base.input base.split base.path base.args diff --git a/bash/src/base.split.sh b/bash/src/base.split.sh index dd79d25..dac4eeb 100644 --- a/bash/src/base.split.sh +++ b/bash/src/base.split.sh @@ -1,7 +1,6 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments module: base.split "Fonctions de base: analyse et découpage de valeurs" -require: base.arr function: splitfsep "\ Découper \$1 de la forme first[SEPsecond] entre first, qui est placé dans la diff --git a/bash/src/base.str.sh b/bash/src/base.string.sh similarity index 98% rename from bash/src/base.str.sh rename to bash/src/base.string.sh index e83e714..be29579 100644 --- a/bash/src/base.str.sh +++ b/bash/src/base.string.sh @@ -1,6 +1,6 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments -module: base.str "Fonctions de base: gestion des valeurs chaines" +module: base.string "Fonctions de base: gestion des valeurs chaines" function: strmid "Afficher la plage \$1 de la valeur \$2..* diff --git a/bash/tests/test-output.sh b/bash/tests/test-output.sh new file mode 100755 index 0000000..f8b8330 --- /dev/null +++ b/bash/tests/test-output.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +source "$(dirname -- "$0")/../src/nucore.sh" || exit 1 +#NUCORE_NO_DISABLE_SET_X=1 + +eerror "erreur" +ewarn "warning" +enote "note" +ebanner "banner" +eimportant "important" +eattention "attention" +einfo "info" +eecho "vanilla" +edebug "debug" + +etitle "section" + +estep "step" +estepe "stepe" +estepw "stepw" +estepn "stepn" +estepi "stepi" +ebegin "begin" +edot +edotw +eend +echo "\ +line1 +line2 +line3" | elinedots "lines" + +eend +