##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ## Gestion de fichiers modèles ##@cooked nocomments ##@require base uprovide template urequire base function __template_prel_abspath() { # afficher le chemin absolu du fichier $1 si on est dans le répertoire de # destination $2, sinon l'exprimer par rapport au répertoire de destination # $2 si c'est un chemin relatif. if withinpath "$2" "$(pwd)"; then abspath "$1" else abspath "$1" "$2" fi } function __template_check_srcdir() { [ -n "$1" ] || { eerror "Vous devez spécifier le répertoire source" return 1 } [ -d "$1" ] || { eerror "$1: répertoire source introuvable" return 1 } return 0 } function __template_check_destdir() { [ -n "$1" ] || { eerror "Vous devez spécifier le répertoire de destination" return 1 } [ -d "$1" ] || { eerror "$1: répertoire destination introuvable" return 1 } return 0 } function __template_plsort() { # Trier par taille du chemin awk '{print length($0) ":" $0}' | sort -n "$@" | awk '{sub(/[0-9]+:/, ""); print}' } # liste des variables qu'il faut remplacer dans les fichiers sources TEMPLATE_VARS=() function __template_setup_tmpfile() { if [ ${#TEMPLATE_VARS[*]} -gt 0 ]; then # S'il y a des variables à remplacer, préparer un fichier temporaire ac_set_tmpfile tmpfile fi } function __template_clean_tmpfile() { if [ ${#TEMPLATE_VARS[*]} -gt 0 ]; then ac_clean "$tmpfile" fi } function __template_fillvars() { # Pour chacune des variables VAR de TEMPLATE_VARS, remplacer dans le fichier # $1 les occurences de @@VAR@@ par la valeur $VAR # Afficher le chemin vers le fichier temporaire qui contient le fichier # modifié. Si $2 est spécifié, prendre ce chemin comme fichier temporaire. # Sinon, créer un nouveau fichier temporaire. Si le fichier ne contient # aucune occurence de variable, afficher le chemin original $1. [ ${#TEMPLATE_VARS[*]} -eq 0 ] && { echo "$1"; return; } # chercher si le fichier contient au moins un des tags de TEMPLATE_VARS local __var __tag __found for __var in "${TEMPLATE_VARS[@]}"; do __tag="@@${__var}@@" if quietgrep "$__tag" "$1"; then __found=1 break fi done [ -n "$__found" ] || { echo "$1"; return; } # construire le script sed pour le remplacement des variables local __script __first=1 for __var in "${TEMPLATE_VARS[@]}"; do [ -n "$__first" ] || __script="$__script"$'\n' __first= __script="${__script}s/@@${__var}@@/$(qseds "${!__var}")/g" done sed "$__script" <"$1" >"$2" echo "$2" } function template_list() { # Lister les fichiers du répertoire source $1 qui seraient fusionnées avec # template_merge() ou supprimés avec template_unmerge() du répertoire # destination $2. Si des chemins sont spécifiés avec les arguments $3..@, ne # traiter que les fichiers qui correspondent à ces spécifications local srcdir="$1"; shift __template_check_srcdir "$srcdir" || return 1 setx srcdir=abspath "$srcdir" local destdir="${1:-.}"; shift __template_check_destdir "$destdir" || return 1 setx destdir=abspath "$destdir" local rel2pwd withinpath "$destdir" "$(pwd)" && rel2pwd=1 if [ $# -eq 0 ]; then [ -n "$rel2pwd" ] && set -- . || set -- "$destdir" fi local tmpfile; __template_setup_tmpfile local spec srcspec src content dest list local -a srcfiles for spec in "$@"; do setx srcspec=__template_prel_abspath "$spec" "$destdir" withinpath "$destdir" "$srcspec" || continue srcspec="${srcspec#$destdir}" [ -e "$srcdir$srcspec" ] || continue array_from_lines srcfiles "$(find "$srcdir$srcspec" -type f)" for src in "${srcfiles[@]}"; do setx content=__template_fillvars "$src" "$tmpfile" dest="$destdir/${src#$srcdir/}" list= if [ -L "$dest" ]; then : elif [ -f "$dest" ]; then testsame "$content" "$dest" && list=1 else list=1 fi [ -n "$list" ] || continue if [ -n "$rel2pwd" ]; then relpath "$dest" else echo "${src#$srcdir/}" fi done done | csort -u __template_clean_tmpfile } function template_merge() { # Copier dans le répertoire destination $2 tous les fichiers provenant du # répertoire source $1 correspondant aux spécifications $3..@, si ces fichiers # n'ont pas été modifiés dans le répertoire de destination. # Les fichiers sources ayant l'extension .template sont ignorés par défaut, sauf # s'ils sonts demandés explicitement. local srcdir="$1"; shift __template_check_srcdir "$srcdir" || return 1 setx srcdir=abspath "$srcdir" local destdir="${1:-.}"; shift __template_check_destdir "$destdir" || return 1 setx destdir=abspath "$destdir" [ $# -gt 0 ] || set -- "$destdir" local tmpfile; __template_setup_tmpfile local spec template srcspec src content dest local srcfiles for spec in "$@"; do setb template=[ "${spec%.template}" != "$spec" ] setx srcspec=__template_prel_abspath "$spec" if ! withinpath "$destdir" "$srcspec"; then ewarn "$spec: fichier ignoré" continue fi srcspec="${srcspec#$destdir}" [ -e "$srcdir$srcspec" ] || continue ebegin "$(ppath "$destdir$srcspec")" s=0 if [ -n "$template" ]; then array_from_lines srcfiles "$(find "$srcdir$srcspec" -type f)" else array_from_lines srcfiles "$(find "$srcdir$srcspec" -type f | grep -v '\.template$')" fi for src in "${srcfiles[@]}"; do setx content=__template_fillvars "$src" "$tmpfile" dest="$destdir/${src#$srcdir/}" [ -n "$template" ] && dest="${dest%.template}" if [ -L "$dest" ]; then edotw 0 "LOCALLY MODIFIED: $(ppath "$dest")" elif [ -f "$dest" ]; then if testsame "$content" "$dest"; then show_debug && edot 0 "ALREADY COPIED: $(ppath "$dest")" else edotw 0 "LOCALLY MODIFIED: $(ppath "$dest")" fi else mkdirof "$dest" cp "$content" "$dest"; r=$? edot $r "COPY: $(ppath "$dest")" [ $r -eq 0 ] || s=$r fi done eend $s done __template_clean_tmpfile } function template_unmerge() { # Supprimer du répertoire de destination $2 tous les fichiers provenant du # répertoire source $1 et qui n'ont pas été modifiés. Si des chemins sont # spécifiés avec les arguments $3..@, ne traiter que les fichiers qui # correspondent à ces spécifications. local srcdir="$1"; shift __template_check_srcdir "$srcdir" || return 1 setx srcdir=abspath "$srcdir" local destdir="${1:-.}"; shift __template_check_destdir "$destdir" || return 1 setx destdir=abspath "$destdir" [ $# -gt 0 ] || set -- "$destdir" local tmpfile; __template_setup_tmpfile local spec srcspec src content dest local srcfiles for spec in "$@"; do setx srcspec=__template_prel_abspath "$spec" if ! withinpath "$destdir" "$srcspec"; then ewarn "$spec: fichier ignoré" continue fi srcspec="${srcspec#$destdir}" [ -e "$srcdir$srcspec" ] || continue s=0 ebegin "$(ppath "$destdir$srcspec")" array_from_lines files "$(find "$srcdir$srcspec" -type f)" for src in "${files[@]}"; do setx content=__template_fillvars "$src" "$tmpfile" dest="$destdir/${src#$srcdir/}" if [ -L "$dest" ]; then edotw 0 "LOCALLY MODIFIED: $(ppath "$dest")" elif [ -f "$dest" ]; then if testsame "$content" "$dest"; then rm -f "$dest"; r=$? edot $r "REMOVE: $(ppath "$dest")" [ $r -eq 0 ] || s=$r else edotw 0 "LOCALLY MODIFIED: $(ppath "$dest")" fi else show_debug && edot 0 "ALREADY REMOVED: $(ppath "$dest")" fi done eend $s done __template_clean_tmpfile } function template_cleandest() { # Supprimer dans le répertoire de destination $1 tous les répertoires vides. # Cette fonction est habituellement utilisée après template_unmerge() local -a dirs [ -d "$1" ] || return 1 array_from_lines dirs "$(cd "$1"; find . -type d | __template_plsort -r)" array_del dirs . (cd "$1"; rmdir "${dirs[@]}" 2>/dev/null) } function template_diff() { # Afficher les différences entre les fichiers du répertoire de destination $2 et # les fichiers du répertoire source $1 local srcdir="$1"; shift __template_check_srcdir "$srcdir" || return 1 setx srcdir=abspath "$srcdir" local destdir="${1:-.}"; shift __template_check_destdir "$destdir" || return 1 setx destdir=abspath "$destdir" local tmpfile; __template_setup_tmpfile local src content dest local -a srcfiles array_from_lines srcfiles "$(find "$srcdir" -type f)" for src in "${srcfiles[@]}"; do setx content=__template_fillvars "$src" "$tmpfile" dest="$destdir/${src#$srcdir/}" if [ -f "$dest" ] && testdiff "$content" "$dest"; then diff -uwB "$content" "$dest" fi done | page_maybe -S __template_clean_tmpfile } function templatesrc() { # obtenir le chemin vers le répertoire source de templates $1, situé dans # ULIBDIR/templates urequire ulib if [ -n "$ULIBDIR" ]; then echo "$ULIBDIR/templates/$1" else abspath "templates/$1" fi } function __templatectl_display_help() { :; } __TEMPLATECTL_SUPPLOPTS=() function __templatectl_parseopts() { parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with __templatectl_display_help' \ -s:,--srcdir: __templatectl_srcdir= \ -d:,--destdir: __templatectl_destdir= \ -l,--list '$__templatectl_list=1; __templatectl_opt=1' \ -m,--merge '$__templatectl_merge=1; __templatectl_opt=1' \ -z,--unmerge '$__templatectl_unmerge=1; __templatectl_opt=1' \ -C,--clean '$__templatectl_clean=1; __templatectl_opt=1' \ -g,--diff '$__templatectl_diff=1; __templatectl_opt=1' \ "${__TEMPLATECTL_SUPPLOPTS[@]}" \ @ args -- "$@" } function __templatectl_do() { __template_check_srcdir "$__templatectl_srcdir" || return 1 [ -n "$__templatectl_destdir" ] || __templatectl_destdir=. __template_check_destdir "$__templatectl_destdir" || return 1 [ -n "$__templatectl_auto" ] && __templatectl_list=1 [ -n "$__templatectl_list" ] && template_list "$__templatectl_srcdir" "$__templatectl_destdir" "$@" [ -n "$__templatectl_merge" ] && template_merge "$__templatectl_srcdir" "$__templatectl_destdir" "$@" [ -n "$__templatectl_unmerge" ] && template_unmerge "$__templatectl_srcdir" "$__templatectl_destdir" "$@" [ -n "$__templatectl_clean" ] && template_cleandest "$__templatectl_destdir" "$@" [ -n "$__templatectl_diff" ] && template_diff "$__templatectl_srcdir" "$__templatectl_destdir" "$@" return 0 } function templatectl() { # fonction de haut niveau qui facilite l'utilisation des fonctions template_* # définir la fonction __templatectl_display_help() pour l'affichage de l'aide local -a args local __templatectl_list __templatectl_merge __templatectl_unmerge __templatectl_clean __templatectl_diff __templatectl_opt __templatectl_auto local __templatectl_destdir __templatectl_parseopts "$@" && set -- "${args[@]}" || { eerror "$args"; return 1; } [ -z "$__templatectl_opt" ] && __templatectl_auto=1 __templatectl_do "$@" }