diff --git a/bash/src/TEMPLATE b/bash/src/TEMPLATE new file mode 100644 index 0000000..aba64fa --- /dev/null +++ b/bash/src/TEMPLATE @@ -0,0 +1,4 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +##@cooked nocomments +module: TEMPLATE "DESCRIPTION" + diff --git a/bash/src/template.sh b/bash/src/template.sh new file mode 100644 index 0000000..ad200ff --- /dev/null +++ b/bash/src/template.sh @@ -0,0 +1,180 @@ +# -*- 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_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' + +Ajouter file au tableau userfiles" +function template_copy_replace() { + local src="$1" dest="$2" + if [ -z "$dest" ]; then + local srcdir srcname + setx srcname=basename "$src" + dest="${srcname#.}" + dest="${dest%.*}" + setx srcdir=dirname "$src" + dest="$srcdir/$dest" + fi + + userfiles+=("$dest") + cp -P "$src" "$dest" + 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' + +Ajouter file au tableau userfiles" +function template_copy_missing() { + local src="$1" dest="$2" + if [ -z "$dest" ]; then + local srcdir srcname + setx srcname=basename "$src" + dest="${srcname#.}" + dest="${dest%.*}" + setx srcdir=dirname "$src" + dest="$srcdir/$dest" + fi + + userfiles+=("$dest") + if [ ! -e "$dest" ]; then + cp -P "$src" "$dest" + return 0 + fi + return 1 +} + +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_]*" +function template_dump_vars() { + 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 \$@" +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 # +" +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" + else + echo "/#@@IF:${var}@@/d" + echo "s/#@@if:${var}@@/#/g" + echo "s/#@@UL:${var}@@//g" + echo "s/#@@ul:${var}@@//g" + fi + echo "s/@@${var}@@/${value}/g" + done + ) + #etitle "awkscript" cat "$awkscript" + #etitle "sedscript" cat "$sedscript" +} + +function template_process_userfiles() { + local awkscript sedscript workfile userfile + ac_set_tmpfile awkscript + ac_set_tmpfile sedscript + template_generate_scripts "$awkscript" "$sedscript" "$@" + + ac_set_tmpfile workfile + for userfile in "${userfiles[@]}"; do + cat "$userfile" | awk -f "$awkscript" | sed -f "$sedscript" >"$workfile" && + cat "$workfile" >"$userfile" + done + + ac_clean "$awkscript" "$sedscript" "$workfile" +} diff --git a/bash/tests/.gitignore b/bash/tests/.gitignore new file mode 100644 index 0000000..3ee43c2 --- /dev/null +++ b/bash/tests/.gitignore @@ -0,0 +1 @@ +/template-dest.txt diff --git a/bash/tests/_template-dest.txt b/bash/tests/_template-dest.txt new file mode 100644 index 0000000..8de596b --- /dev/null +++ b/bash/tests/_template-dest.txt @@ -0,0 +1,21 @@ +--- +PROFILES vaut prod test +c'est à dire, si on fait un par ligne: +- prod +- test +--- +PASSWORD is vaeRL6ADYKmWndEA +--- +hosts: +- first +- second +--- +--- +IF valeur +if valeur +#ul valeur +--- +#if +UL +ul +--- diff --git a/bash/tests/_template-source.txt b/bash/tests/_template-source.txt new file mode 100644 index 0000000..d98644e --- /dev/null +++ b/bash/tests/_template-source.txt @@ -0,0 +1,23 @@ +--- +PROFILES vaut @@PROFILES@@ +c'est à dire, si on fait un par ligne: +@@FOR:PROFILES@@- @@PROFILE@@ +--- +PASSWORD is XXXRANDOMXXX +--- +#@@IF:HOSTS@@hosts: +@@FOR:HOSTS@@- @@HOST@@ +--- +#@@IF:VALUES@@values: +@@FOR:VALUES@@- @@VALUE@@ +--- +#@@IF:PLEIN@@IF @@PLEIN@@ +#@@if:PLEIN@@if @@PLEIN@@ +#@@UL:PLEIN@@UL @@PLEIN@@ +#@@ul:PLEIN@@ul @@PLEIN@@ +--- +#@@IF:VIDE@@IF @@VIDE@@ +#@@if:VIDE@@if @@VIDE@@ +#@@UL:VIDE@@UL @@VIDE@@ +#@@ul:VIDE@@ul @@VIDE@@ +--- diff --git a/bash/tests/_template-source_envs b/bash/tests/_template-source_envs new file mode 100755 index 0000000..e2aadd5 --- /dev/null +++ b/bash/tests/_template-source_envs @@ -0,0 +1,19 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 + +echo "\ +source ./_template-values.env +template_vars=( + PROFILES + PASSWORD + HOSTS + VALUES + PLEIN + VIDE +) +template_lists=( + PROFILES + HOSTS + VALUES +) +" diff --git a/bash/tests/_template-values.env b/bash/tests/_template-values.env new file mode 100644 index 0000000..03af4ac --- /dev/null +++ b/bash/tests/_template-values.env @@ -0,0 +1,12 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 + +PROFILES="prod test" + +HOSTS=" +first +second +" +VALUES= + +PLEIN=valeur +VIDE= diff --git a/bash/tests/test-template.sh b/bash/tests/test-template.sh new file mode 100755 index 0000000..ca297c8 --- /dev/null +++ b/bash/tests/test-template.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +source "$(dirname -- "$0")/../src/nulib.sh" || exit 1 +require: template +#NULIB_NO_DISABLE_SET_X=1 + +args=( + "description" + #"usage" +) +parse_args "$@"; set -- "${args[@]}" + +function template__source_envs() { + eval "$("$MYDIR/_template-source_envs")" +} + +cd "$MYDIR" +#template_generate_scripts \ +# /tmp/awkscript /tmp/sedscript \ +# template_values.env +# +#for i in awk sed; do +# etitle "$i" cat "/tmp/${i}script" +#done + +template_copy_replace _template-source.txt _template-dest.txt +template_process_userfiles _template_values.env + +cat _template-dest.txt diff --git a/bash/tests/test.awk b/bash/tests/test.awk new file mode 100644 index 0000000..2db1ba8 --- /dev/null +++ b/bash/tests/test.awk @@ -0,0 +1,30 @@ +#@include "base.tools.awk" +BEGIN { + PROFILES[0] = 0; delete PROFILES + PROFILES[0] = "prod" + PROFILES[1] = "test" + HOSTS[0] = 0; delete HOSTS + HOSTS[0] = "first" + HOSTS[1] = "second" + VALUES[0] = 0; delete VALUES +} +{ if (should_generate_password()) { generate_password() } } +/\#@@EACH:PROFILES@@/ { + sub(/\#@@EACH:PROFILES@@/, "") + for (index in PROFILES) { + print gensub(/@@PROFILE@@/, PROFILES[index], "g") + } + next +} +/\#@@EACH:HOSTS@@/ { + sub(/\#@@EACH:HOSTS@@/, "") + for (index in HOSTS) { + print gensub(/@@HOST@@/, HOSTS[index], "g") + } + next +} +/\#@@EACH:VALUES@@/ { + sub(/\#@@EACH:VALUES@@/, "") + next +} +{ print }