nulib-base/bash/src/template.sh

313 lines
10 KiB
Bash

# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
##@cooked nocomments
module: template "Mise à jour de templates à partir de modèles"
function: template_locals "\
Afficher les variables qui doivent être locales
Utiliser de cette façon:
~~~
eval \$(template_locals)
~~~"
function template_locals() {
echo "local -a userfiles; local updated"
}
function: template_copy_replace "\
Copier \$1 vers \$2 de façon inconditionnelle
Si \$2 n'est pas spécifié, on assume que \$1 est de la forme '.file.ext'
et \$2 vaudra alors 'file'
si un fichier \${2#.}.local existe (e.g 'file.ext.local'), prendre ce fichier à
la place comme source
Ajouter file au tableau userfiles
retourner:
- 0 en cas de copie avec succès
- 2 si la source n'existe pas
- 3 si une erreur I/O s'est produite lors de la copie"
function template_copy_replace() {
local src="$1" dest="$2"
local srcdir srcname lsrcname
setx srcdir=dirname "$src"
setx srcname=basename "$src"
if [ -z "$dest" ]; then
dest="${srcname#.}"
dest="${dest%.*}"
dest="$srcdir/$dest"
fi
lsrcname="${srcname#.}.local"
[ -e "$srcdir/$lsrcname" ] && src="$srcdir/$lsrcname"
[ -e "$src" ] || return 2
userfiles+=("$dest")
local have_backup
if [ -e "$dest" ]; then
# copie de sauvegarde avant
if ! cp -P --preserve=all "$dest" "$dest.bck.$$"; then
rm "$dest.bck.$$"
return 3
fi
have_backup=1
fi
if ! cp -P "$src" "$dest"; then
rm "$dest"
if [ -n "$have_backup" ]; then
# restaurer la sauvegarde en cas d'erreur
cp -P --preserve=all "$dest.bck.$$" "$dest" &&
rm "$dest.bck.$$"
fi
return 3
fi
[ -n "$have_backup" ] && rm "$dest.bck.$$"
return 0
}
function: template_copy_missing "\
Copier \$1 vers \$2 si ce fichier n'existe pas déjà
Si \$2 n'est pas spécifié, on assume que \$1 est de la forme '.file.ext'
et \$2 vaudra alors 'file'
si un fichier \${1#.}.local existe (e.g 'file.ext.local'), prendre ce fichier à
la place comme source
Ajouter file au tableau userfiles
retourner:
- 0 en cas de copie avec succès
- 1 si le fichier existait déjà
- 2 si la source n'existe pas
- 3 si une erreur I/O s'est produite lors de la copie"
function template_copy_missing() {
local src="$1" dest="$2"
local srcdir srcname lsrcname
setx srcdir=dirname "$src"
setx srcname=basename "$src"
if [ -z "$dest" ]; then
dest="${srcname#.}"
dest="${dest%.*}"
dest="$srcdir/$dest"
fi
lsrcname="${srcname#.}.local"
[ -e "$srcdir/$lsrcname" ] && src="$srcdir/$lsrcname"
[ -e "$src" ] || return 2
userfiles+=("$dest")
[ -e "$dest" ] && return 1
if ! cp -P "$src" "$dest"; then
# ne pas garder le fichier en cas d'erreur de copie
rm "$dest"
return 3
fi
return 0
}
function: template_ioerror "\
tester si une erreur de copie s'est produite lors de l'appel à
template_copy_missing() ou template_copy_replace(), par exemple en cas de
dépassement de capacité du disque ou si le fichier source n'existe pas
il faut appeler cette fonction avec la valeur de retour de ces fonctions, e.g
template_copy_missing file
template_ioerror $? && die"
function template_ioerror() {
local r="${1:-$?}"
[ $r -ge 2 ]
}
function: template_dump_vars "\
Lister les variables mentionnées dans les fichiers \$@
Seules sont prises en compte les variables dont le nom est de la forme
[A-Z][A-Za-z_]*
Cette fonction est utilisée par template_source_envs(). Elle utilise la
fonction outil _template_dump_vars() qui peut être redéfinie si nécessaire."
function template_dump_vars() {
_template_dump_vars "$@"
}
function _template_dump_vars() {
[ $# -gt 0 ] || return 0
cat "$@" |
grep -E '^[A-Z][A-Za-z_]*=' |
sed 's/=.*//' |
sort -u
}
function: template_source_envs "\
Cette fonction doit être implémentée par l'utilisateur et doit:
- initialiser le tableau template_vars qui donne la liste des variables scalaires
- initialiser te tableau template_lists qui donne la liste des variables listes
- charger ces variables depuis les fichiers \$@
Cette fonction utilise la fonction outil _template_source_envs() qui peut être
redéfinie si nécessaire."
function template_source_envs() {
_template_source_envs "$@"
}
function _template_source_envs() {
local e_
for e_ in "$@"; do
[ -f "$e_" ] && source "$e_"
done
setx -a template_vars=template_dump_vars "$@"
template_lists=()
}
function: template_resolve_scripts "\
Générer le script awk \$1 et le script sed \$2 qui remplacent dans les fichiers
destination les marqueurs @@VAR@@ par la valeur de la variable \$VAR
correspondante
Les fichiers \$3..@ contiennent les valeurs des variables
Les marqueurs supportés sont les suivants et sont évalués dans cet ordre:
- XXXRANDOMXXX remplacer cette valeur par une chaine de 16 caractères au hasard
- @@FOR:VARS@@ VARS étant une liste de valeurs séparées par des espaces:
dupliquer la ligne autant de fois qu'il y a de valeurs dans \$VARS
dans chaque ligne, remplacer les occurrences de @@VAR@@ par la valeur
de l'itération courante
- #@@IF:VAR@@ afficher la ligne si VAR est non vide, supprimer la ligne sinon
- #@@UL:VAR@@ afficher la ligne si VAR est vide, supprimer la ligne sinon
- #@@if:VAR@@
- #@@ul:VAR@@ variantes qui ne suppriment pas la ligne mais sont remplacées par #
- @@VAR:-string@@ remplacer par 'string' si VAR a une valeur vide ou n'est pas défini, \$VAR sinon
- @@VAR:+string@@ remplacer par 'string' si VAR est défini a une valeur non vide
"
function template_generate_scripts() {
local awkscript="$1"; shift
local sedscript="$1"; shift
(
template_source_envs "$@"
NL=$'\n'
# random, for
exec >"$awkscript"
echo '@include "base.tools.awk"'
echo 'BEGIN {'
for list in "${template_lists[@]}"; do
items="${!list}"; read -a items <<<"${items//
/ }"
let i=0
echo " $list[0] = 0; delete $list"
for item in "${items[@]}"; do
item="${item//\\/\\\\}"
item="${item//\"/\\\"}"
echo " $list[$i] = \"$item\""
let i=i+1
done
done
echo '}'
echo '{ if (should_generate_password()) { generate_password() } }'
for list in "${template_lists[@]}"; do
items="${!list}"; read -a items <<<"${items//
/ }"
echo "/@@FOR:$list@@/ {"
if [ ${#items[*]} -gt 0 ]; then
if [ "${list%S}" != "$list" ]; then item="${list%S}"
elif [ "${list%s}" != "$list" ]; then item="${list%s}"
else item="$list"
fi
echo " sub(/@@FOR:$list@@/, \"\")"
echo " for (i in $list) {"
echo " print gensub(/@@$item@@/, $list[i], \"g\")"
echo " }"
fi
echo " next"
echo "}"
done
echo '{ print }'
# if, ul, var
exec >"$sedscript"
for var in "${template_vars[@]}"; do
value="${!var}"
value="${value//\//\\\/}"
value="${value//[/\\[}"
value="${value//\*/\\\*}"
value="${value//$NL/\\n}"
if [ -n "$value" ]; then
echo "s/#@@IF:${var}@@//g"
echo "s/#@@if:${var}@@//g"
echo "/#@@UL:${var}@@/d"
echo "s/#@@ul:${var}@@/#/g"
echo "s/@@${var}:-([^@]*)@@/${value}/g"
echo "s/@@${var}:+([^@]*)@@/\\1/g"
else
echo "/#@@IF:${var}@@/d"
echo "s/#@@if:${var}@@/#/g"
echo "s/#@@UL:${var}@@//g"
echo "s/#@@ul:${var}@@//g"
echo "s/@@${var}:-([^@]*)@@/\\1/g"
echo "s/@@${var}:+([^@]*)@@//g"
fi
echo "s/@@${var}@@/${value}/g"
done
)
#etitle "awkscript" cat "$awkscript"
#etitle "sedscript" cat "$sedscript"
}
function: _template_can_process "\
Indiquer si \$1 est un fichier texte, qui peut être traité par
template_process_userfiles"
function _template_can_process() {
case "$1" in
*.png|*.jpg|*.gif|*.bmp) return 1;;
*.zip|*.jar|*.war|*.ear) return 1;;
*.tar|*.gz|*.tgz|*.bz2|*.tbz2) return 1;;
*) return 0;;
esac
}
function: template_process_userfiles "\
retourner:
- 0 en cas de traitement avec succès des fichiers
- 3 si une erreur I/O s'est produite lors du traitement d'un des fichiers"
function template_process_userfiles() {
local awkscript sedscript workfile userfile
local have_backup
ac_set_tmpfile awkscript
ac_set_tmpfile sedscript
template_generate_scripts "$awkscript" "$sedscript" "$@"
ac_set_tmpfile workfile
for userfile in "${userfiles[@]}"; do
_template_can_process "$userfile" || continue
if cat "$userfile" | awk -f "$awkscript" | sed -rf "$sedscript" >"$workfile"; then
if testdiff "$workfile" "$userfile"; then
# n'écrire le fichier que s'il a changé
if [ -e "$userfile" ]; then
# copie de sauvegarde avant
if ! cp -P --preserve=all "$userfile" "$userfile.bck.$$"; then
rm "$userfile.bck.$$"
return 3
fi
have_backup=1
fi
if ! cat "$workfile" >"$userfile"; then
rm "$userfile"
if [ -n "$have_backup" ]; then
# restaurer la sauvegarde en cas d'erreur
cp -P --preserve=all "$userfile.bck.$$" "$userfile" &&
rm "$userfile.bck.$$"
fi
return 3
fi
[ -n "$have_backup" ] && rm "$userfile.bck.$$"
fi
fi
done
ac_clean "$awkscript" "$sedscript" "$workfile"
return 0
}