# -*- 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" }