nutools/ulib/conf

612 lines
20 KiB
Bash

##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Gestion des fichiers de configuration
##@cooked nocomments
##@require base
uprovide conf
urequire base
################################################################################
# Gestion des fichiers de configuratio où les directives sont de la forme
# 'name=value' et où les commentaires débutent par '#'. C'est typiquement le cas
# des scripts de profile du shell
# Note: #@$*! de MacOS X qui ne supporte pas \(expr\)\? dans sed. Toutes les
# lignes sed de la forme '/prefix\(expr\)\?suffix/s/from/to/g' sont remplacées
# par une double expression
# '/prefixexprsuffix/s/from/to/g
# /prefixsuffix/s/from/to/g'
function conf_enable() {
# Dans le fichier de configuration $1, activer les paramètres $2..*
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Dans tous les cas, toutes les directives de ce nom sont recherchées et
# décommentées. Si value est précisée, les directives sont mises à jour. Si
# la directive ne figure pas dans le fichier, elle y est rajoutée à la fin
# avec la valeur spécifiée.
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value to
for param in "$@"; do
splitvar "$param" name value
# Essayer d'abord de décommenter la valeur dans le fichier
if quietgrep "^[ $TAB]*##*[ $TAB]*\(export[ $TAB]*\)\?$name[ $TAB]*=" "$conf"; then
sedi "\
/^[ $TAB]*##*[ $TAB]*export[ $TAB]*$name[ $TAB]*=/s/^[ $TAB]*##*\([ $TAB]*export[ $TAB]*\)/\\1/g
/^[ $TAB]*##*[ $TAB]*$name[ $TAB]*=/s/^[ $TAB]*##*//g
" "$conf"
modified=0
elif ! quietgrep "^[ $TAB]*\(export[ $TAB]*\)\?$name[ $TAB]*=" "$conf"; then
echo "$name=" >>"$conf"
modified=0
fi
if [ "$name" != "$param" ]; then
# Ensuite, mettre à jour le fichier avec la valeur spécifiée
to="$(quote_seds "$value")"
sedi "\
/^[ $TAB]*export[ $TAB]*$name[ $TAB]*=/s/^\([ $TAB]*export[ $TAB]*$name[ $TAB]*=[ $TAB]*\).*\$/\\1$to/
/^[ $TAB]*$name[ $TAB]*=/s/^\([ $TAB]*$name[ $TAB]*=[ $TAB]*\).*\$/\\1$to/
" "$conf"
modified=0
fi
done
unfix_mode "$conf" "$mode"
return $modified
}
function conf_enableq() {
# Comme conf_enable(), mais s'assure que les valeurs sont quotées dans le
# fichier. Ceci permet de stocker des valeurs avec des espaces ou des
# caractères spéciaux.
local args arg name value
args=("$1"); shift
for arg in "$@"; do
splitvar "$arg" name value
array_add args "$name=$(quoted_arg "$value")"
done
conf_enable "${args[@]}"
}
function conf_disable() {
# Dans le fichier de configuration $1, désactiver les paramètres $2..*
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Toutes les directives de ce noms sont recherchées et commentées. La valeur
# si elle est spécifiée, est ignorée. Si la directive ne figure pas dans le
# fichier, c'est un NOP.
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value from0 from1
for param in "$@"; do
splitvar "$param" name value
# Essayer simplement de commenter la directive dans le fichier
from0="^[ $TAB]*export[ $TAB]*$name[ $TAB]*="
from1="^[ $TAB]*$name[ $TAB]*="
if [ "$name" != "$param" ]; then
from0="$from0$(quote_seds "$value")\$"
from1="$from1$(quote_seds "$value")\$"
fi
if quietgrep "$from" "$conf"; then
sedi "\
/$from0/s/^/#/g
/$from1/s/^/#/g
" "$conf"
modified=0
fi
done
unfix_mode "$conf" "$mode"
return $modified
}
CONF_APPEND_SEP=:
function conf_append() {
# Dans le fichier de configuration $1, augmenter les valeurs des variables
# correspondant aux paramètres $2..*
# Chaque argument de cette fonction correspond à une variable du fichier de
# configuration, et doit être de la forme name=value
# Une ligne 'name="${name:+$name:}$value"' est générée à la fin du fichier
# de configuration.
# Par défaut, le séparateur CONF_APPEND_SEP vaut ':', mais il est possible
# de changer cette valeur, de façon globale
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value from
for param in "$@"; do
splitvar "$param" name value
echo "$name=\"\${$name:+\$$name$CONF_APPEND_SEP}$(quote_arg "$value")\"" >>"$conf"
modified=0
done
unfix_mode "$conf" "$mode"
return $modified
}
function conf_array_append() {
# Dans le fichier de configuration $1, augmenter les valeurs des variables
# de tableau correspondant aux paramètres $2..*
# Chaque argument de cette fonction correspond à une variable du fichier de
# configuration, et doit être de la forme name=value
# Une ligne name=("${name[@]}" "$value") est générée à la fin du fichier de
# configuration
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value from
for param in "$@"; do
splitvar "$param" name value
if quietgrep "^[ $TAB]*\(export[ $TAB]*\)\?$name=(" "$conf"; then
# variable déjà existante
[ "$name" != "$param" ] || continue
echo "$name=(\"\${$name[@]}\" $(quoted_arg "$value"))" >>"$conf"
else
# nouvelle variable
if [ "$name" != "$param" ]; then
echo "$name=($(quoted_arg "$value"))" >>"$conf"
else
echo "$name=()" >>"$conf"
fi
fi
modified=0
done
unfix_mode "$conf" "$mode"
return $modified
}
function conf_check() {
# Dans le fichier de configuration $1, tester si tous les paramètres $2..*
# sont présents.
# Chaque argument de cette fonction correspond à une variable du fichier de
# configuration, et doit être de la forme name[=value]
# Si une valeur est spécifiée, vérifier que le fichier contient la valeur
# correspondante. Sinon, tester uniquement la présence de la directive.
local conf="$1"; shift
[ -n "$*" ] || return 1
local param name value from
for param in "$@"; do
splitvar "$param" name value
from="^[ $TAB]*\(export[ $TAB]*\)\?$name[ $TAB]*="
if [ "$name" != "$param" ]; then
from="$from$(quote_seds "$value")\$"
fi
quietgrep "$from" "$conf" || return 1
done
return 0
}
################################################################################
# Gestion des fichiers de configuration où les directives sont de la forme 'name
# value', et où les commentaires débutent par '#'. C'est typiquement le cas des
# fichiers de configuration d'apache
function aconf_enable() {
# Dans le fichier de configuration $1, activer les paramètres $2..*
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Toutes les directives de ce nom sont recherchées et décommentées, et la
# valeur mise à jour. Si la directive ne figure pas dans le fichier, elle y
# est rajoutée à la fin. A cause du mode opératoire, cette fonction ne
# convient pas pour les directives dont le nom peut apparaitre plusieurs
# fois dans le fichier
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value to
for param in "$@"; do
splitvar "$param" name value
# Essayer d'abord de décommenter la valeur dans le fichier
if quietgrep "^[ $TAB]*##*[ $TAB]*$name[ $TAB]*" "$conf"; then
sedi "/^[ $TAB]*##*[ $TAB]*$name[ $TAB]*/s/^[ $TAB]*##*\([ $TAB]*\)/\\1/g" "$conf"
modified=0
elif ! quietgrep "^[ $TAB]*$name[ $TAB]*" "$conf"; then
echo "$name" >>"$conf"
modified=0
fi
if [ "$name" != "$param" ]; then
# Ensuite, mettre à jour le fichier avec la valeur spécifiée
to="$(quote_seds "$value")"
sedi "\
/^[ $TAB]*$name[ $TAB][ $TAB]*/s/^\([ $TAB]*$name[ $TAB]*\).*$/\\1$to/
/^[ $TAB]*$name\$/s/^\([ $TAB]*$name\)\$/\\1 $to/" "$conf"
modified=0
fi
done
unfix_mode "$conf" "$mode"
return $modified
}
function aconf_disable() {
# Dans le fichier de configuration $1, désactiver les paramètres $2..*
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Si la valeur est précisée, la directive correspondant à ce nom et cette
# valeur est recherchée et commentée. Sinon, toutes les directives de ce
# noms sont recherchées et commentées. Si la directive ne figure pas dans le
# fichier, c'est un NOP.
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value from
for param in "$@"; do
splitvar "$param" name value
# Essayer simplement de commenter la valeur dans le fichier
from="^[ $TAB]*$name[ $TAB]*"
if [ "$name" != "$param" ]; then
from="$from$(quote_seds "$value")\$"
fi
if quietgrep "$from" "$conf"; then
sedi "/$from/"'s/^/#/g' "$conf"
modified=0
fi
done
unfix_mode "$conf" "$mode"
return $modified
}
function aconf_append() {
# Dans le fichier de configuration $1, ajouter des directives correspondant
# aux paramètres $2..*
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name=value
# Une ligne '$name $value' est ajoutée à la fin du fichier de configuration
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
local conf="$1"; shift
[ -n "$*" ] || return 1
local mode="$(fix_mode "$conf")"
local modified=1
local param name value from
for param in "$@"; do
splitvar "$param" name value
echo "$name${value:+ $value}" >>"$conf"
modified=0
done
unfix_mode "$conf" "$mode"
return $modified
}
function aconf_array_append() { aconf_append "$@"; }
function aconf_check() {
# Dans le fichier de configuration $1, tester si tous les paramètres $2..*
# sont présents.
# Chaque argument de cette fonction correspond à une variable du fichier de
# configuration, et doit être de la forme name[=value]
# Si une valeur est spécifiée, vérifier que le fichier contient la valeur
# correspondante. Sinon, tester uniquement la présence de la directive.
local conf="$1"; shift
[ -n "$*" ] || return 1
local param name value from
for param in "$@"; do
splitvar "$param" name value
from="^[ $TAB]*$name[ $TAB]*"
if [ "$name" != "$param" ]; then
from="$from$(quote_seds "$value")\$"
fi
quietgrep "$from" "$conf" || return 1
done
return 0
}
################################################################################
# Gestion des fichiers de configuration où les directives sont placées dans des
# sections identifiées par une chaine de la forme [section]. Les directives sont
# de la forme 'name=value', et les commentaires débutent par '#' ou ';'. C'est
# typiquement le cas des fichiers de configuration de MySQL (my.cnf) et de PHP
# (php.ini)
function mconf_enable() {
# Dans le fichier de configuration $1, activer les paramètres $3..* de la
# section $2
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Toutes les directives de ce nom sont recherchées et décommentées, et la
# valeur mise à jour. Si la directive ne figure pas dans le fichier, elle y
# est rajoutée à la fin. A cause du mode opératoire, cette fonction ne
# convient pas pour les directives dont le nom peut apparaitre plusieurs
# fois dans le fichier
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
# Cette fonction nécessite gawk
local conf="$1"; shift
local section="$1"; shift
[ -n "$*" ] || return 1
local script i param name value
script="BEGIN {
modified = 1
section = $(quoted_awk "$section")
in_section = 0"
i=0
for param in "$@"; do
splitvar "$param" name value
script="$script
names[$i] = $(quoted_awk "$name")"
if [ "$name" != "$param" ]; then
script="$script
hasvalues[$i] = 1
values[$i] = $(quoted_awk "$value")"
else
script="$script
hasvalues[$i] = 0
values[$i] = \"\""
fi
script="$script
seen[$i] = 0"
let i=$i+1
done
script="$script"'
}
function write_unseen(write_section, wrote_section) {
for (i in seen) {
if (!seen[i]) {
if (write_section && !wrote_section) {
print "[" section "]"
wrote_section = 1
}
print names[i] "=" values[i]
seen[i] = 1
modified = 0
}
}
}
/^\[.*\]/ {
was_in_section = in_section
match($0, /^\[(.*)\]/, vs)
in_section = (vs[1] == section)
if (!in_section && was_in_section) {
write_unseen()
}
}
'
i=0
for param in "$@"; do
splitvar "$param" name value
script="$script
in_section && \$0 ~ /^[ $TAB]*((##*|;;*)[ $TAB]*)?$name[ $TAB]*=/ {
\$0 = gensub(/^[ $TAB]*(##*|;;*)[ $TAB]*($name[ $TAB]*=)/, \"\\\\2\", \"\")
if (hasvalues[$i]) {
\$0 = gensub(/^([ $TAB]*$name[ $TAB]*=[ $TAB]*).*\$/, \"\\\\1\" values[$i], \"\")
}
seen[$i] = 1
modified = 0
}"
let i=$i+1
done
script="$script
{ print }
END {
write_unseen(!in_section)
exit modified
}"
ac_set_tmpfile tmpfile
local mode="$(fix_mode "$conf")"
local modified=1
if <"$conf" >"$tmpfile" awk "$script"; then
cat "$tmpfile" >"$conf"
modified=0
fi
unfix_mode "$conf" "$mode"
ac_clean "$tmpfile"
return $modified
}
function mconf_disable() {
# Dans le fichier de configuration $1, désactiver les paramètres $3..* de la
# section $2.
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name[=value]
# Si la valeur est précisée, la directive correspondant à ce nom et cette
# valeur est recherchée et commentée. Sinon, toutes les directives de ce
# noms sont recherchées et commentées. Si la directive ne figure pas dans le
# fichier, c'est un NOP.
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
# Cette fonction nécessite gawk
local conf="$1"; shift
local section="$1"; shift
[ -n "$*" ] || return 1
local script match
script="BEGIN {
modified = 1
section = $(quoted_awk "$section")
in_section = 0
"
local param name value
for param in "$@"; do
splitvar "$param" name value
match="${match:+$match || }\$0 ~ /^[ $TAB]*$name[ $TAB]*="
if [ "$name" != "$param" ]; then
match="$match[ $TAB]*$(quote_seds "$value")\$"
fi
match="$match/"
done
script="$script
}"'
/^\[.*\]/ {
match($0, /^\[(.*)\]/, vs)
in_section = (vs[1] == section)
}
in_section && ('"$match"') {
$0 = "#" $0
modified = 0
}
{ print }
END {
exit modified
}
'
ac_set_tmpfile tmpfile
local mode="$(fix_mode "$conf")"
local modified=1
if <"$conf" >"$tmpfile" awk "$script"; then
cat "$tmpfile" >"$conf"
modified=0
fi
unfix_mode "$conf" "$mode"
ac_clean "$tmpfile"
return $modified
}
function mconf_append() {
# Dans le fichier de configuration $1, ajouter des directives correspondant
# aux paramètres $3..* dans la section $2
# Chaque argument de cette fonction correspond à une directive du fichier de
# configuration et doit être de la forme name=value
# Une ligne '$name = $value' est ajoutée à la fin de la section, qui est
# créée si nécessaire à la fin du fichier de configuration
# Retourner 0 si une modification a été faite dans le fichier, 1 sinon
# Cette fonction nécessite gawk
local conf="$1"; shift
local section="$1"; shift
[ -n "$*" ] || return 1
local script match
script="BEGIN {
modified = 1
section=$(quoted_awk "$section")
in_section=0
"
i=0
for param in "$@"; do
splitvar "$param" name value
script="$script
names[$i] = $(quoted_awk "$name")
values[$i] = $(quoted_awk "$value")
seen[$i] = 0"
let i=$i+1
done
script="$script"'
}
function write_vars(write_section, wrote_section) {
for (i in seen) {
if (!seen[i]) {
if (write_section && !wrote_section) {
print "[" section "]"
wrote_section = 1
}
print names[i] "=" values[i]
seen[i] = 1
modified = 0
}
}
}
/^\[.*\]/ {
was_in_section = in_section
match($0, /^\[(.*)\]/, vs)
in_section = (vs[1] == section)
if (!in_section && was_in_section) {
write_vars()
}
}
{ print }
END {
write_vars(!in_section)
exit modified
}'
ac_set_tmpfile tmpfile
local mode="$(fix_mode "$conf")"
local modified=1
if <"$conf" >"$tmpfile" awk "$script"; then
cat "$tmpfile" >"$conf"
modified=0
fi
unfix_mode "$conf" "$mode"
ac_clean "$tmpfile"
return $modified
}
function mconf_array_append() { mconf_append "$@"; }
function mconf_check() {
# Dans le fichier de configuration $1, tester si tous les paramètres $3..*
# sont présents dans la section $2
# Chaque argument de cette fonction correspond à une variable du fichier de
# configuration, et doit être de la forme name[=value]
# Si une valeur est spécifiée, vérifier que le fichier contient la valeur
# correspondante. Sinon, tester uniquement la présence de la directive.
# Cette fonction nécessite gawk
local conf="$1"; shift
local section="$1"; shift
[ -n "$*" ] || return 1
local script i param name value
script="BEGIN {
section = $(quoted_awk "$section")
in_section = 0"
i=0
for param in "$@"; do
splitvar "$param" name value
script="$script
names[$i] = $(quoted_awk "$name")
values[$i] = $(quoted_awk "$value")
seen[$i] = 0"
let i=$i+1
done
script="$script"'
}
/^\[.*\]/ {
match($0, /^\[(.*)\]/, vs)
in_section = (vs[1] == section)
}
'
i=0
for param in "$@"; do
splitvar "$param" name value
script="$script
in_section && \$0 ~ /^[ $TAB]*$name[ $TAB]*="
if [ "$name" != "$param" ]; then
script="$script$(quote_seds "$value")\$"
fi
script="$script/ {
seen[$i] = 1
}"
let i=$i+1
done
script="$script
END {
for (i in seen) {
if (!seen[i]) exit 1
}
exit 0
}"
<"$conf" awk "$script"
}