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