355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| ##@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 "$@"
 | |
| }
 | 
