# -*- 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 $NULIB_INPUT_ENCODING), transformer cette chaine en # utf-8 local __var="$1" __from="${2:-$NULIB_INPUT_ENCODING}" if [ "$__from" != "NULIB__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 $NULIB_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 set_interaction() { :;} 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 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 echo_ " $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 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 "$NULIB_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 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 echo_ ": " 1>&2 uread -e ${__rv_d:+-i"$__rv_d"} "${__rv_opts[@]}" __rv_r else if [ -n "$__rv_d" ]; then if [ -n "$__rv_showdef" ]; then echo_ " [$__rv_d]" 1>&2 else echo_ " [****]" 1>&2 fi fi echo_ ": " 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 echo 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) eval "$(local_args)" local __sm_title= __sm_yourchoice= __sm_default= args=( -t:,--title __sm_title= -m:,--prompt __sm_yourchoice= -d:,--default __sm_default= ) parse_args "$@"; set -- "${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}" 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 echo_ ": " 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 echo 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) eval "$(local_args)" 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= args=( -t:,--title __am_title= -m:,--prompt __am_optyc= -M:,--no-prompt __am_actyc= -e: __am_void_actions -d: __am_default_action= -q: __am_quit_action= -o: __am_default_option= ) parse_args "$@"; set -- "${args[@]}" __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() { local c=0 choice while true; do if [ $c -eq 0 ]; then [ -n "$title" ] && __etitle "$title" 1>&2 __eecho_ "=== Actions disponibles: " 1>&2 recho "$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 echo_ ": " 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 echo 1>&2 c=0 fi done __am_select_action="$select_action" __am_select_option= } function __options_actions_menu() { 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 echo "$i*- $option" 1>&2 else echo "$i - $option" 1>&2 fi let i=$i+1 done __estepn_ "Actions disponibles: " 1>&2 recho "$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 echo_ ": " 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 echo "" 1>&2 c=0 fi done __am_select_action="$select_action" __am_select_option="$select_option" }