nulib/bash/src/base.output.sh

604 lines
18 KiB
Bash

# -*- 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"
nulib__load: _output_vanilla
NULIB__TAB=$'\t'
NULIB__LATIN1=iso-8859-1
NULIB__LATIN9=iso-8859-15
NULIB__UTF8=utf-8
[ -n "$LANG" ] || export LANG=fr_FR.UTF-8
if [ ! -x "$(which iconv 2>/dev/null)" ]; then
function iconv() { cat; }
fi
function nulib__lang_encoding() {
case "${LANG,,}" in
*@euro) echo "iso-8859-15";;
*.utf-8|*.utf8) echo "utf-8";;
*) echo "iso-8859-1";;
esac
}
function nulib__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 nulib__init_encoding() {
local default_encoding="$(nulib__lang_encoding)"
[ -n "$default_encoding" ] || default_encoding=utf-8
[ -n "$NULIB_OUTPUT_ENCODING" ] || NULIB_OUTPUT_ENCODING="$default_encoding"
NULIB_OUTPUT_ENCODING="$(nulib__norm_encoding "$NULIB_OUTPUT_ENCODING")"
[ -n "$NULIB_INPUT_ENCODING" ] || NULIB_INPUT_ENCODING="$NULIB_OUTPUT_ENCODING"
NULIB_INPUT_ENCODING="$(nulib__norm_encoding "$NULIB_INPUT_ENCODING")"
}
[ -n "$NULIB_LANG" -a -z "$LANG" ] && export NULIB_LANG LANG="$NULIB_LANG"
nulib__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 uecho() {
# $1 étant une chaine encodée en utf-8, l'afficher dans l'encoding de sortie $2
# qui vaut par défaut $NULIB_OUTPUT_ENCODING
local value="$1" to="${2:-$NULIB_OUTPUT_ENCODING}"
if [ "$to" == "$NULIB__UTF8" ]; then
recho "$value"
else
iconv -f -utf-8 -t "$to" <<<"$value"
fi
}
function uecho_() {
# $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 $NULIB_OUTPUT_ENCODING
local value="$1" to="${2:-$NULIB_OUTPUT_ENCODING}"
if [ "$to" == "$NULIB__UTF8" ]; then
recho_ "$value"
else
recho_ "$value" | iconv -f utf-8 -t "$to"
fi
}
export NULIB_QUIETLOG
export NULIB__TMPLOG
function: quietc "\
N'afficher la sortie de la commande \$@ que si on est en mode DEBUG ou si elle se termine en erreur"
function quietc() {
local r
[ -z "$NULIB__TMPLOG" ] && ac_set_tmpfile NULIB__TMPLOG
"$@" >&"$NULIB__TMPLOG"; r=$?
[ -n "$NULIB_QUIETLOG" ] && cat "$NULIB__TMPLOG" >>"$NULIB_QUIETLOG"
[ $r -ne 0 -o -n "$NULIB_DEBUG" ] && cat "$NULIB__TMPLOG"
return $r
}
function: quietc_logto "\
Si quietc est utilisé, sauvegarder quand même la sortie dans le fichier \$1
Utiliser l'option -a pour ajouter au fichier au lieu de l'écraser e.g
quietc_logto -a path/to/logfile
Tous les autres arguments sont du contenu ajoutés au fichier, e.g
quietc_logto -a path/to/logfile \"\\
================================================================================
= \$(date +%F-%T)\""
function quietc_logto() {
local append
if [ "$1" == -a ]; then
shift
append=1
fi
NULIB_QUIETLOG="$1"; shift
[ -n "$NULIB_QUIETLOG" ] || return
if [ -z "$append" ]; then
>"$NULIB_QUIETLOG"
fi
if [ $# -gt 0 ]; then
echo "$*" >>"$NULIB_QUIETLOG"
fi
}
function: quietc_echo "Ajouter \$* dans le fichier mentionné par quietc_logto() le cas échéant"
function quietc_echo() {
if [ -n "$NULIB_QUIETLOG" ]; then
echo "$*" >>"$NULIB_QUIETLOG"
fi
}
# faut-il dater les messages des fonctions e* et action?
# Faire NULIB_ELOG_DATE=1 en début de script pour activer cette fonctionnalité
# faut-il rajouter aussi le nom du script? (nécessite NULIB_ELOG_DATE)
# Faire NULIB_ELOG_MYNAME=1 en début de script pour activer cette fonctionnalité
export NULIB_ELOG_DATE NULIB_ELOG_MYNAME
function __edate() {
[ -n "$NULIB_ELOG_DATE" ] || return
local prefix="$(date +"[%F %T.%N] ")"
[ -n "$NULIB_ELOG_MYNAME" ] && prefix="$prefix$MYNAME "
echo "$prefix"
}
export NULIB_ELOG_OVERWRITE
function __set_no_colors() { :; }
function elogto() {
# Activer NULIB_ELOG_DATE 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 NULIB_ELOG_OVERWRITE=1, alors le fichier en sortie est écrasé. Sinon, les
# lignes en sortie lui sont ajoutées
NULIB_ELOG_DATE=1
NULIB_ELOG_MYNAME=1
if [ -n "$1" -a -n "$2" ]; then
LANG=fr_FR.UTF8
NULIB_OUTPUT_ENCODING="$NULIB__UTF8"
__set_no_colors 1
if [ -n "$NULIB_ELOG_OVERWRITE" ]; then
exec >"$1" 2>"$2"
else
exec >>"$1" 2>>"$2"
fi
elif [ -n "$1" ]; then
LANG=fr_FR.UTF8
NULIB_OUTPUT_ENCODING="$NULIB__UTF8"
__set_no_colors 1
if [ -n "$NULIB_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
# NULIB__ESTACK est la liste des invocations de 'etitle' et 'action' en cours
export NULIB__ESTACK NULIB__INDENT=
function __eindent0() {
# afficher le nombre d'espaces correspondant à l'indentation
local indent="${NULIB__ESTACK//?/ }"
indent="${indent% }$NULIB__INDENT"
[ -n "$indent" ] && echo "$indent"
}
function __eindent() {
# indenter les lignes de $1, sauf la première
local -a lines; local line first=1
local indent="$(__eindent0)$2"
setx -a lines=recho "$1"
for line in "${lines[@]}"; do
if [ -n "$first" ]; then
recho "$line"
first=
else
recho "$indent$line"
fi
done
}
function __complete() {
# compléter $1 avec $3 jusqu'à obtenir une taille de $2 caractères
local columns="${COLUMNS:-80}"
local line="$1" maxi="${2:-$columns}" sep="${3:- }"
while [ ${#line} -lt $maxi ]; do
line="$line$sep"
done
echo "$line"
}
PRETTYOPTS=()
function set_verbosity() {
case "$1" in
-D|--debug) NULIB_DEBUG=1;;
esac
}
function check_verbosity() { return 0; }
function get_verbosity_option() { :;}
# tester respectivement si on doit afficher les messages d'erreur,
# d'avertissement, d'information, de debug
function show_error() { return 0; }
function show_warn() { return 0; }
function show_info() { return 0; }
function show_verbose() { return 0; }
function show_debug() { [ -n "$NULIB_DEBUG" ]; }
# note: toutes les fonctions d'affichage e* écrivent sur stderr
function esection() {
# Afficher une section. Toutes les indentations sont remises à zéro
show_info || return
eval "$NULIB__DISABLE_SET_X"
NULIB__ESTACK=
__esection "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function etitle() {
# Afficher le titre $1. Le contenu sous des titres imbriqués est affiché
# indenté.
# - si $2..$* est spécifié, c'est une commande qui est lancée dans le contexte
# du titre, ensuite le titre est automatiquement terminé
# - sinon il faut terminer le titre explicitement avec eend
local title="$1"; shift
# etitle
NULIB__ESTACK="$NULIB__ESTACK:t"
if show_info; then
eval "$NULIB__DISABLE_SET_X"
__etitle "$title" 1>&2
eval "$NULIB__ENABLE_SET_X"
fi
# commande
local r=0
if [ $# -gt 0 ]; then
"$@"; r=$?
eend
fi
return $r
}
function eend() {
# Terminer un titre
if [ "${NULIB__ESTACK%:t}" != "$NULIB__ESTACK" ]; then
NULIB__ESTACK="${NULIB__ESTACK%:t}"
fi
}
function edesc() {
# Afficher une description sous le titre courant
show_info || return
eval "$NULIB__DISABLE_SET_X"
__edesc "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function ebanner() {
# Afficher un message très important encadré, puis attendre 5 secondes
show_error || return
eval "$NULIB__DISABLE_SET_X"
__ebanner "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
sleep 5
}
function eimportant() {
# Afficher un message très important
show_error || return
eval "$NULIB__DISABLE_SET_X"
__eimportant "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qimportant() { eimportant "$*"; quietc_echo "IMPORTANT: $*"; }
function eattention() {
# Afficher un message important
show_warn || return
eval "$NULIB__DISABLE_SET_X"
__eattention "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qattention() { eattention "$*"; quietc_echo "ATTENTION: $*"; }
function eerror() {
# Afficher un message d'erreur
show_error || return
eval "$NULIB__DISABLE_SET_X"
__eerror "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qerror() { eerror "$*"; quietc_echo "ERROR: $*"; }
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 msg="$1"; shift
local r=1
if [ $# -eq 0 ]; then
[ -n "$msg" ] && eerror "$msg"
else
"$@"; r=$?
if [ $r -ne 0 -a -n "$msg" ]; then
eerror "$msg"
fi
fi
return $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 msg="$1"; shift
local r=0
if [ $# -gt 0 ]; then
"$@"; r=$?
if [ $r -eq 0 -a -n "$msg" ]; then
eerror "$msg"
fi
fi
return $r
}
function set_die_return() {
NULIB__DIE="return 1"
}
function die() {
[ $# -gt 0 ] && eerror "$@"
local die="${NULIB__DIE:-exit 1}"
eval "$die" || return
}
function die_with {
[ $# -gt 0 ] && eerror "$1"
shift
[ $# -gt 0 ] && "$@"
local die="${NULIB__DIE:-exit 1}"
eval "$die" || return
}
function die_unless() {
# Afficher $1 et quitter le script avec die() si la commande $2..@ retourne FAUX
local msg="$1"; shift
local die="${NULIB__DIE:-exit 1}"
local r=1
if [ $# -eq 0 ]; then
[ -n "$msg" ] && eerror "$msg"
else
"$@"; r=$?
if [ $r -ne 0 -a -n "$msg" ]; then
eerror "$msg"
fi
fi
if [ $r -ne 0 ]; then
eval "${die% *} $r" || return $r
fi
return $r
}
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 msg="$1"; shift
local die="${NULIB__DIE:-exit 1}"
local r=0
if [ $# -gt 0 ]; then
"$@"; r=$?
if [ $r -eq 0 -a -n "$msg" ]; then
eerror "$msg"
fi
fi
if [ $r -eq 0 ]; then
eval "${die% *} $r" || return $r
fi
return $r
}
function exit_with {
if [ $# -gt 0 ]; then "$@"; fi
exit $?
}
function ewarn() {
# Afficher un message d'avertissement
show_warn || return
eval "$NULIB__DISABLE_SET_X"
__ewarn "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qwarn() { ewarn "$*"; quietc_echo "WARN: $*"; }
function enote() {
# Afficher un message d'information de même niveau qu'un avertissement
show_info || return
eval "$NULIB__DISABLE_SET_X"
__enote "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qnote() { enote "$*"; quietc_echo "NOTE: $*"; }
function einfo() {
# Afficher un message d'information
show_info || return
eval "$NULIB__DISABLE_SET_X"
__einfo "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qinfo() { einfo "$*"; quietc_echo "INFO: $*"; }
function eecho() {
# Afficher un message d'information sans préfixe
show_info || return
eval "$NULIB__DISABLE_SET_X"
__eecho "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
function qecho() { eecho "$*"; quietc_echo "$*"; }
function eecho_() {
show_info || return
eval "$NULIB__DISABLE_SET_X"
__eecho_ "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
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 && { __eecho "\$ $cmd" 1>&2; }
"$@"; r=$?
if [ $r -ne 0 ]; then
if show_info; then
__eecho "^ [EC #$r]" 1>&2
elif show_error; then
__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 "$@")"
if show_error; then
__eecho "^ $cmd [EC #$r]" 1>&2
fi
fi
return $r
}
function edebug() {
# Afficher un message de debug
show_debug || return
eval "$NULIB__DISABLE_SET_X"
__edebug "$*" 1>&2
eval "$NULIB__ENABLE_SET_X"
}
# 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.
function estep() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estep "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepe() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepe "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepw() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepw "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepn() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepn "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepi() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepi "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estep_() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estep_ "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepe_() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepe_ "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepw_() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepw_ "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepn_() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepn_ "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function estepi_() { show_info || return; eval "$NULIB__DISABLE_SET_X"; __estepi_ "$*" 1>&2; eval "$NULIB__ENABLE_SET_X"; }
function qstep() { estep "$*"; quietc_echo "* $*"; }
function action() {
# commencer l'action $1
# - si $2..$* est spécifié, c'est une commande qui est lancée dans le contexte
# de l'action, ensuite l'action est terminée en succès ou en échec suivant le
# code de retour. ne pas afficher la sortie de la commande comme avec quietc()
# - sinon il faut terminer le titre explicitement avec eend
eval "$NULIB__DISABLE_SET_X"
local action="$1"; shift
local r=0
if [ $# -gt 0 ]; then
[ -z "$NULIB__TMPLOG" ] && ac_set_tmpfile NULIB__TMPLOG
[ -n "$action" ] && quietc_echo "$(__edate) ACTION: $action:"
"$@" >&"$NULIB__TMPLOG"; r=$?
[ -n "$NULIB_QUIETLOG" ] && cat "$NULIB__TMPLOG" >>"$NULIB_QUIETLOG"
if [ $r -ne 0 -o -n "$NULIB_DEBUG" ]; then
NULIB__ESTACK="$NULIB__ESTACK:a"
[ -n "$action" ] && action="$action:"
__action "$action" 1>&2
cat "$NULIB__TMPLOG"
aresult $r
else
if [ $r -eq 0 ]; then
[ -n "$action" ] || action="succès"
__asuccess "$action" 1>&2
else
[ -n "$action" ] || action="échec"
__afailure "$action" 1>&2
fi
fi
else
NULIB__ESTACK="$NULIB__ESTACK:a"
[ -n "$action" ] && action="$action:"
__action "$action" 1>&2
fi
eval "$NULIB__ENABLE_SET_X"
return $r
}
function asuccess() {
# terminer l'action en cours avec le message de succès $*
[ "${NULIB__ESTACK%:a}" != "$NULIB__ESTACK" ] || return
eval "$NULIB__DISABLE_SET_X"
[ -n "$*" ] || set -- "succès"
NULIB__INDENT=" " __asuccess "$*" 1>&2
NULIB__ESTACK="${NULIB__ESTACK%:a}"
eval "$NULIB__ENABLE_SET_X"
return 0
}
function afailure() {
# terminer l'action en cours avec le message d'échec $*
[ "${NULIB__ESTACK%:a}" != "$NULIB__ESTACK" ] || return
eval "$NULIB__DISABLE_SET_X"
[ -n "$*" ] || set -- "échec"
NULIB__INDENT=" " __afailure "$*" 1>&2
NULIB__ESTACK="${NULIB__ESTACK%:a}"
eval "$NULIB__ENABLE_SET_X"
return 1
}
function aresult() {
# terminer l'action en cours avec un message de succès ou d'échec $2..* en
# fonction du code de retour $1 (0=succès, sinon échec)
[ "${NULIB__ESTACK%:a}" != "$NULIB__ESTACK" ] || return
eval "$NULIB__DISABLE_SET_X"
local r="${1:-0}"; shift
if [ "$r" == 0 ]; then
[ -n "$*" ] || set -- "succès"
NULIB__INDENT=" " __asuccess "$*" 1>&2
else
[ -n "$*" ] || set -- "échec"
NULIB__INDENT=" " __afailure "$*" 1>&2
fi
NULIB__ESTACK="${NULIB__ESTACK%:a}"
eval "$NULIB__ENABLE_SET_X"
return $r
}
function adone() {
# terminer l'action en cours avec le message neutre $*
[ "${NULIB__ESTACK%:a}" != "$NULIB__ESTACK" ] || return
eval "$NULIB__DISABLE_SET_X"
[ -n "$*" ] && NULIB__INDENT=" " __adone "$*" 1>&2
NULIB__ESTACK="${NULIB__ESTACK%:a}"
eval "$NULIB__ENABLE_SET_X"
return 0
}