#!/bin/sh

: <<SCRIPTHEADER
Description: DNS zones helper for BIND9
Version: 3.7.10
Copyright: GNU GPL (2010-2025) Narcis Garcia
Homepage: https://www.somtecnologia.com
License: GNU GPL
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 .
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
SCRIPTHEADER

# Program development at projectes_publics/gesdonis/gesdonis
# Software releases can be downloaded from: https://...##

# Script template version: 1.6.8
# Copyright 2017-2025 (GNU GPL) Narcis Garcia
# Compatible with package managers integration: Dpkg/deb

# NOTAS:
#	- Servidores DNS públicos de https://servers.opennic.org/ : 94.247.43.254 194.36.144.87 130.61.69.123 89.163.140.67 195.10.195.195 144.24.181.253 151.80.222.79
#	  Funcionan pero dejaron de constar en OpenNIC: 185.84.81.194 91.217.137.37
#	  Antiguos que se interrupieron: 216.146.35.35 193.183.98.66
#	- Servidores DNS públicos privativos:
#		Cloudflare: 1.1.1.1 1.0.0.1 2606:4700:4700::1111 2606:4700:4700::1001
#		Google:     8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844
#		Quad9:      9.9.9.9 2620:fe::fe
#	- El servicio DNS de BIND9 requiere acceso TCP y UDP por el puerto 53. El puerto 953 sólo es
#	  para administrar mediante RNDC.
#	- Para las zonas de tipo "slave" se registra la IP de su NS principal (requisito de BIND)
#	- Para crear espejos de zonas entre vistas:
#	  $INCLUDE /srv/named/views/nolocal-nets/master/example.net
#	- (?) El primer NS de una hoja de zona debe coincidir con el servidor SOA (/srv/named/common-soa);
#	  para hacer las cosas fáciles, un mismo servidor no debe atender como example.NET y example.COM a la vez.
#	- El SOA de las distintas réplicas de zonas (entre maestro y esclavos) debe ser el mismo.
#	- File templates allow own variables to generate real zone for Bind9:
#		{a:smtp.example.net}	<- Solved IP address to regenerate only when edited
#		{a:smtp.example.net:h}	<- Solved IP address to regenerate hourly
#		{a:smtp.example.net:d}	<- Solved IP address to regenerate daily
#		{a:smtp.example.net:w}	<- Solved IP address to regenerate weekly
#		{a:smtp.example.net:m}	<- Solved IP address to regenerate monthly
#		{a,1.1.1.1:smtp.example.net}	<- Solved IP address with specified NS
#		{a:/srv/named/fqdn.txt}	<- Solved IP address from a file contained name
#		{a:https://www.example.net/fqdn.html}	<- Solved IP address from HTTP downloaded name
#		{f:/srv/named/value.txt}	<- File content
#		{h:https://www.example.net/value.html}	<- HTTP content
#		{x:command}	<- Execution stdout
#	- DNS software alternatives: knot, nsd

# GUIAS:
#	- Consultar todos los registros de un dominio/subdominio:
#	  dig ANY example.net
#	  dig ANY subdomain.example.net
#	- Obtener solo la respuesta esquematica de una consulta: dig +nocmd +multiline +noall +answer ANY example.net
#	- Consultar un servidor DNS determinado: dig @dns.example.org example.net
#	- Sitio de BIND9 https://www.isc.org/bind/
#	- Referencia de parámetros configurables https://bind9.readthedocs.io/en/latest/reference.html
#	- Guia técnica de servidores y zonas DNS https://www.zytrax.com/books/dns/
#	- Revisión de zona DNS pública
#		https://www.zonemaster.net/es/
#		http://webdnstools.com/dnstools/domain_check
#		https://mxtoolbox.com/DnsLookup.aspx
#	- Verificar respuestas de servidor con nslookup https://www.akadia.com/services/check_dns.html
#	- Obtener los «glue records» publicados para un dominio https://www.adminsehow.com/2010/05/how-to-check-domain-ns-glue-records-using-dig/
#		FQDN=example.net
#		FQDN2="$(printf '%s' "$FQDN" | sed -e 's|\.$||' | tr -s '.' '\n' | tail -n 2)"
#		FQDN2="$(echo TrimAndSingle $FQDN2 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
#		FQDN2="$(printf '%s' "$FQDN2" | tr -s ' ' '.')"
#		TLD="$(printf '%s' "$FQDN" | sed -e 's|\.$||' | tr -s '.' '\n' | tail -n 1)"
#		RootNS="$(dig NS "$TLD" | tr -s '\t' ' ' | grep -e "^${TLD}\. " | sort -R | head -n 1 | tr -s ' ' '\n' | tail -n 1 | sed -e 's|\.$||')"
#		dig NS @$RootNS $FQDN2
#		(El TTL ahí reportado es el que pueden esperar otros servidores en actualizarse cambios de NS)
#	  Comprobar los DNS del registro que concuerdan con los «glue records»:
#		FQDN=example.net
#		FQDN2="$(printf '%s' "$FQDN" | sed -e 's|\.$||' | tr -s '.' '\n' | tail -n 2)"
#		FQDN2="$(echo TrimAndSingle $FQDN2 | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
#		FQDN2="$(printf '%s' "$FQDN2" | tr -s ' ' '.')"
#		whois $FQDN2 | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ie '^Name Server:'
#	- Códigos de error EDNS que puede reportar "dig" en sus consultas https://tools.ietf.org/html/rfc8914
#	  Un servidor con soporte a EDNS: 1.1.1.1

# Program development ToDo:
#	- Change $data_path to some path allowed by /etc/apparmor.d/usr.sbin.named
#	  (this is to survive to AppArmor scenarios, because of some hosted/shared environments)
#	- Implement action "zone export" to pack all included files in a zone.
#	- Enhance and use WorkingInternetDNS() to get OpenNIC servers addresses.
#	- Implement {soa:ns1.example.net} to replace this by: $INCLUDE /srv/named/common-records/soa/ns1.example.net
#	- Develop FileInclusions() to support full $INCLUDE directives
#	- Acciones:
#	  zone empty (borrar todas las las entradas de una hoja)
#	  ptr empty (borrar todas las las entradas de una hoja)
#	  entry show (tanto para hoja de nombres como para PTR)
#	  entry create (tanto para hoja de nombres como para PTR)
#	  entry delete (tanto para hoja de nombres como para PTR)
#	- Instalando del gestor de paquetes, evitar la pregunta: Parece la primera vez... ¿Sustituir ya el fichero...?
#	+ pendent.txt
#	- Importación/exportación completa y parcial, para migraciones.
#	- Contemplar https://en.wikipedia.org/wiki/Open_Root_Server_Network como alternativa a ICANN
#	- Service_restaurar()
#	- Activación/Desactivación de zones
#	- Plantilla de hoja DNS personalizable.
#	- Preveer el uso de variables propias en las hojas DNS, incluyendo el resultado de una ejecución
#	  y el contenido de un fichero. Especialmente útil para el DNS dinámico con consultas a terceros.
#	- Envio de informe de resultado por correo-e (--mailinfo para todo --mailerror para sólo en caso de error)
#	- Parámetro --rotate para la copia de seguridad
#	- Asistente para importar dominios completos ajenos
#	  (que el usuario suministre una serie de nombres y reproducir sus zonas)
#	- Adición/Supresión/Consulta de registros individuales.
#	- Compatibilizar BIND con el servicio Avahi de los clientes, quizas como propone Erik Grootjans:
#	  cambiando la referencia "db.local" por "db.localhost" en /etc/bind/named.conf.default-zones
#	- Preveer DDNS, que ademas permita actualizar un fichero con "@ IN A 1.2.3.4" para incluir en hojas.
#	- Service_instalar(), Service_desinstalar()
#	- Prever DNSSEC; http://www.gandibar.net/post/2012/03/02/DNSSEC-at-Gandi
#	- 'updateforwarders' should allow FQDN specification of DNS
#	- Revisar ServiceName="bind9" porque en Debian recientes se denomina «named» solo que el named.service le otorga «bind9» como alias
#	- "delete" action should move object to trash instead of removing files. Then add an "undelete" action, and warn when creating/deleting an object that already exists in trash.


# ProgramName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ProgramName="gesdonis"
# DependsOnSoftware: Space separated list of required programs. Each item's syntaxes: executable/package ExecutableOption1:ExecutableOption2/PackageOption1|PackageOption2...
DependsOnSoftware="tr:cat/coreutils|debianutils grep sed perl/perl-base named/bind9 awk/gawk ip/iproute bc wget:curl/wget|curl"
RecommendedSoftware=""
SuggestedSoftware=""
SystemConfigDir="/etc/${ProgramName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# InstalledExpected: If help information drives user to install program before using it (1) or not (0)
InstalledExpected=1
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=1
# DataIsPublic: If Configuration and logs can be read by others (1) or not (0)
DataIsPublic=1
# LogRotation: Logrotate frequency to setup (see man logrotate). Empty to not configure rotation of log files.
LogRotation="size 1M"


##### TEMPLATE FUNCTIONS from @-funcions 0:2025.01.13 #####

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
if [ "$TERM" != "" ] ; then
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"
	RedirectedStdout=$?
fi
if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
	# TO DO: Replace all tput calls by printf. Many documentation tell how to escape through "echo -e"
	# https://stackoverflow.com/questions/2924697/how-does-one-output-bold-text-in-bash
	# https://gist.github.com/izabera/9903f9d942e2667ef2cb
	cbDGRAY="$(tput setab 0)"
	cbRED="$(tput setab 196)"
	cbGREEN="$(tput setab 46)"
	cbCBROWN="$(tput setab 3)"
	cbVIOLET="$(tput setab 5)"
	cbBLUEGREEN="$(tput setab 6)"
	cbCGRAY="$(tput setab 7)"
	cbGRAY="$(tput setab 8)"
	cbCGREEN="$(tput setab 10)"
	cbCYELLOW="$(tput setab 11)"  # Darker
	cbYELLOW="$(tput setab 226)"
	cbCBLUE="$(tput setab 12)"
	cbCVIOLET="$(tput setab 13)"
	cbCYAN="$(tput setab 51)"
	cbWHITE="$(tput setab 15)"
	cbBLACK="$(tput setab 16)"
	cbBLUE="$(tput setab 21)"
	cbPINK="$(tput setab 26)"
	
	cfDGRAY="$(tput setaf 0)"
	cfRED="$(tput setaf 196)"
	cfGREEN="$(tput setaf 46)"
	cfCBROWN="$(tput setaf 3)"
	cfVIOLET="$(tput setaf 5)"
	cfBLUEGREEN="$(tput setaf 6)"
	cfCGRAY="$(tput setaf 250)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"  # Darker
	cfYELLOW="$(tput setaf 226)"
	cfCBLUE="$(tput setaf 12)"
	cfCVIOLET="$(tput setaf 13)"
	cfCYAN="$(tput setaf 51)"
	cfWHITE="$(tput setaf 15)"
	cfBLACK="$(tput setaf 16)"
	cfBLUE="$(tput setaf 21)"
	cfPINK="$(tput setaf 26)"
	
	fBOLD="$(tput bold)"
	fBOLD_="$(printf '\033[22m')"  # not bold	https://stackoverflow.com/questions/15579739/in-an-xterm-can-i-turn-off-bold-or-underline-without-resetting-the-current-colo
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fLOW_="$(printf '\033[22m')"  # not dark
	fITALIC="$(tput sitm)"
	fITALIC_="$(printf '\033[23m')"  # not italic
	fUNDERL="$(tput smul)"
	fUNDERL_="$(printf '\033[24m')"  # not underline
	fUNDERLx="$(tput rmul)"  # not underline
#	fNOSTANDOUT="$(tput rmso)"  # No bold, no lines  # Not working in GNU/Linux
#	fNOSTANDOUT="$(echo -e '\033[0m' | sed -e 's|-e||')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
#	fNOSTANDOUT="$(/bin/echo -e '\033[0m')"  # Workaround to use Bash if installed
	fNOSTANDOUT="$(printf '\033[0m')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
	fBELL="$(tput bel)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${fBELL}${cfWHITE}${cbDGRAY}${fBOLD}"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${fBELL}${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
	sGOODEM="$sGOOD"
	sGOODNORMAL="${cbBLACK}${cfGREEN}${fBOLD}"
	sDISABLED="${fLOW}"
else
	cbDGRAY=''
	cbRED=''
	cbGREEN=''
	cbCBROWN=''
	cbVIOLET=''
	cbBLUEGREEN=''
	cbCGRAY=''
	cbGRAY=''
	cbCGREEN=''
	cbCYELLOW=''
	cbYELLOW=''
	cbCBLUE=''
	cbCVIOLET=''
	cbCYAN=''
	cbWHITE=''
	cbBLACK=''
	cbBLUE=''
	cbPINK=''
	
	cfDGRAY=''
	cfRED=''
	cfGREEN=''
	cfCBROWN=''
	cfVIOLET=''
	cfBLUEGREEN=''
	cfCGRAY=''
	cfGRAY=''
	cfCGREEN=''
	cfCYELLOW=''
	cfYELLOW=''
	cfCBLUE=''
	cfCVIOLET=''
	cfCYAN=''
	cfWHITE=''
	cfBLACK=''
	cfBLUE=''
	cfPINK=''
	
	fBOLD=''
	fREVERSEC=''
	fLOW=''
	fUNDERL=''
	fUNDERLx=''
	fNOSTANDOUT=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
	sGOODEM=''
	sGOODNORMAL=''
	sDISABLED=''
fi

Is_Executable ()
# Syntax as a function: Is_Executable $Command
# Description: Returns (exitcode 0) TRUE if specified command's argument is a directly available executable file, internal program or function; or FALSE otherwise.
# Use example (without brackets []):
#	if Is_Executable nano ; then echo "Program exists." ; fi
# Depends on functions: (none)
# Depends on software packages: (none)
{
	ie__Program="$1"
	ie__TrueCode=254  # 254=FALSE
	ie__TestValue=''
	
	ie__TestValue="$(command -v "$ie__Program" 2>/dev/null)"
	IsEmptyString () { WordsNr () { printf '%s' $#; }; return $(WordsNr $*); }
	if ! IsEmptyString "$ie__TestValue" ; then
		# Some scenarios "command" returns directories as executables.
		if [ ! -d "$ie__TestValue" ] || [ "$(printf '%s' "$ie__TestValue" | tr '/' ' ')" = "$ie__TestValue"  ] ; then
			ie__TrueCode=0
		fi
	fi
	return $ie__TrueCode
}

Is_IntegerNr ()
# Syntax as a function: Is_IntegerNr $Valor
# Description: Returns (exitcode 0) TRUE if specified value is an integer number, or FALSE otherwise. (IsNumeric/EsNumerico)
# Use example (without brackets []):
#	if Is_IntegerNr $Respuesta ; then echo "Correcto." ; fi
# Use example verifying also there is only 1 word:
#	if Is_IntegerNr "$Respuesta" ; then echo "Correcto." ; fi
# Notes:
#	- Only first parameter is evaluated. No matter next parameters contain.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	iin__TestValue="$1"
	iin__TrueCode=254  # 254=FALSE
	iin__LastStatus=0
	
	iin__TestValue="$(expr "$iin__TestValue" : '[ ]*\(.*[^ ]\)[ ]*$')"	# Trim spaces
	if [ "$iin__TestValue" = "" ] ; then iin__TestValue='.' ; fi
	[ "$iin__TestValue" -eq "$iin__TestValue" ] > /dev/null 2>&1
	iin__LastStatus=$?
	if [ $iin__LastStatus -eq 0 ] ; then	iin__TrueCode=0 ; fi
	return $iin__TrueCode
}

PrepareExitcodeToFile ()
# Syntax as a sentence: PrepareExitcodeToFile
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just before desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 will be assumed.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
{
	petf__LastStatus=0
	petf__LegatedIndex=0
	petf__CurLegatedName=''
	if [ "$NozeroStatusToFile" != "" ] ; then
		petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		while [ "$(eval "printf '%s' \"\$$petf__CurLegatedName\"")" != "" ] ; do
			petf__LegatedIndex=$((petf__LegatedIndex + 1))
			petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		done
		eval "$petf__CurLegatedName=\"$NozeroStatusToFile\""
		NozeroStatusToFile=''
	fi
	if [ -d "/run/shm" ] ; then
		NozeroStatusToFile="$(mktemp -u -q -p /run/shm 2>/dev/null)"
		petf__LastStatus=$?
	else
		NozeroStatusToFile="$(mktemp -u 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] ; then
		NozeroStatusToFile="$(mktemp 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] || [ "$NozeroStatusToFile" = "" ] ; then
		NozeroStatusToFile=/tmp/exitcode.$$
	fi
	export NozeroStatusToFile="$NozeroStatusToFile"
}

GetFiledExitcode ()
# Syntax as a sentence: GetFiledExitcode $?
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just after desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 is assumed.
#	- This also accumulates pipelining exitcode.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
# Depends on functions: Is_IntegerNr
# Depends on software packages: (none)
{
	gfe__LastStatus="$1"
	gfe__StatusCode=0
	gfe__LegatedIndex=0
	gfe__CurLegatedName=''
	gfe__LastLegatedName=''
	gfe__LastLegatedValue=''
	
	# Get value
	gfe__StatusCode=$(cat "$NozeroStatusToFile" 2>/dev/null)
	if ! Is_IntegerNr "$gfe__StatusCode" ; then gfe__StatusCode=0 ; fi
	rm -f "$NozeroStatusToFile"
	if [ $gfe__StatusCode -eq 0 ] && Is_IntegerNr "$gfe__LastStatus" ; then gfe__StatusCode=$gfe__LastStatus ; fi
	# Pop legacies array stack
	gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	while [ "$(eval "printf '%s' \"\$$gfe__CurLegatedName\"")" != "" ] ; do
		gfe__LastLegatedName="$gfe__CurLegatedName"
		gfe__LegatedIndex=$((gfe__LegatedIndex + 1))
		gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	done
	if [ "$gfe__LastLegatedName" != "" ] ; then
		eval "gfe__LastLegatedValue=\"\$$gfe__LastLegatedName\""
	fi
	if [ "$gfe__LastLegatedValue" != "" ] ; then
		export NozeroStatusToFile="$gfe__LastLegatedValue"
	else
		if [ "$(help export 2>/dev/null)" ] ; then
			# Bashism to remove variable
			export -n NozeroStatusToFile
		else
			# Other POSIX store empty variables
			export NozeroStatusToFile=
		fi
	fi
	return $gfe__StatusCode
}

Dirname ()
# Independent version of GNU dirname
# Useful for Busybox and other minimalistic environments
# Notes:
#	- Only allows 1 path specified
#	- Does not allow any option (such as -z or --version)
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local Path="$1"
	local NrElement=0
	local TotalElements=0
	local CurrentElement=''
	local PreviousElement=''
	local Value=''
	
	if [ "$Path" != "" ] ; then
		if [ "$Path" != "/" ] ; then
			IFS="/" ; for CurrentElement in $Path ; do unset IFS
				NrElement=$((NrElement + 1))
				if [ $NrElement -gt 1 ] ; then
					if [ $NrElement -gt 2 ] ; then
						if [ "$Value" != "/" ] ; then Value="${Value}/" ; fi
					fi
					Value="${Value}${PreviousElement}"
				else
					if [ "$CurrentElement" = "" ] ; then Value="/" ; fi
				fi
				PreviousElement="$CurrentElement"
			done
			unset IFS
			if [ $NrElement -eq 1 ] && [ "$Value" = "" ] ; then Value="." ; fi
		else
			Value="/"
		fi
	else
		Value="."
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ReadlinkF ()
# Old compatibility replacement for modern "readlink -f"
# "canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist"
# Depends on functions: Dirname
# Depends on software packages: grep, sed
{
	local Element="$1"
	local PreviousDir=''
	local CurrentElement=''
	local CurrentPath=''
	local TmpElement=''
	local BaseElement=''
	local ReadlinkStatus=0
	local LastPath=''
	local CurStep=''
	local Value=''

	if [ "$Element" != "" ] ; then
		PreviousDir="$(pwd)"
		CurrentRelElement="$Element"
		if [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] && [ -d "$(Dirname "$CurrentRelElement")" ] ; then
			# Same as "readlink -f" behaviour: If element doesn't exist (but path yes), return as if existed.
			cd "$(Dirname "$CurrentRelElement")"
			Value="$(pwd | sed -e 's|/$||g')"
			Value="${Value}/$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"
		fi
		while [ "$(ls "$CurrentRelElement" 2>/dev/null)" != "" ] ; do	# A broken link is not detected with -e
			if [ -d "$(Dirname "$CurrentRelElement")" ] ; then
				cd "$(Dirname "$CurrentRelElement")"
				CurrentPath="$(pwd)"
			else
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
			fi
			BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
			if [ "$CurrentPath" = "/" ] ; then CurrentPath="" ; fi
			Value="${CurrentPath}/${BaseElement}"
			NextRelElement="$(readlink "$BaseElement" 2>/dev/null)"
			ReadlinkStatus=$?
			if [ "$NextRelElement" != "" ] ; then
				CurrentRelElement="$NextRelElement"
			else
				if [ $ReadlinkStatus -eq 0 ] || [ $ReadlinkStatus -eq 1 ] ; then
					# Old readlink (such as v1.13.3 in Potato debianutils) returns exitcode 1 if element is not a link.
					CurrentRelElement=""
				else
					NextRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					if [ "$NextRelElement" != "" ] ; then
						CurrentRelElement="$NextRelElement"
					else
						CurrentRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					fi
				fi
			fi
			if [ "$CurrentRelElement" != "" ] && [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] ; then
				# Broken link
				BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
				Value="${CurrentPath}/${BaseElement}"
			fi
		done
		cd "$PreviousDir"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

Lowercase ()
# Description: Converts specified string to lower letter case, using found tools.
# Depends on functions: Is_Executable
# Depends on software packages: grep, tr|awk|sed
{
	local OriginalString="$1"
	
	if [ "$OriginalString" != "" ] ; then
		# Busybox tr has no upper/lower conversion
		if Is_Executable tr && [ "$(tr --help 2>&1 | grep -e ':lower:')" != "" ] ; then
			printf '%s\n' "$OriginalString" | tr '[:upper:]' '[:lower:]'
		else
			if Is_Executable awk ; then
				printf '%s\n' "$OriginalString" | awk '{print tolower($0)}'
			else
				if Is_Executable sed && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:upper:]]?)/\L\1/g'
				fi
			fi
		fi
	fi
}

DependenciasFaltan ()
# Sintaxis como función: $(DependenciasFaltan "$ListaDependencias" [SenyalarEjecutable])
# Descripción:
#	Comprueba la disponibilidad de los programas especificados y
#	devuelve (stdout) una lista de los que faltan. Si se encuentra todo
#	no devuelve nada.
# Parámetros esperados:
#	$1	Lista separada por espacios de cada ejecutable y paquete respectivo,
#		separados entre si por "/"
#		Para varias opciones de ejecutable, separarlos entre ":"
#	$2	(opcional) Especifique "0" para evitar que a cada mención de paquete se [añada] el nombre del ejecutable que falta.
# Ejemplo1:	$(DependenciasFaltan "cat/coreutils grep/grep dpkg-deb/dpkg")
#		puede devolver "grep dpkg[dpkg-deb]" si faltan grep y dpkg-deb
# Ejemplo2:	$(DependenciasFaltan "insserv:update-rc.d/sysv-rc cat/coreutils")
#		puede devolver "sysv-rc[update-rc.d]" si tanto falta insserv como update-rc.d
# ToDo:
#	- Cambiar la sintaxis para permitir rutas de ejecutable: /bin/gzip
#	  Alternativas a la barra /: ? ! ;
#	- Para un ejecutable que condiciona requerir otro, separarlos entre "¡"
#	  Por ejemplo: postfix¡policyd-spf/postfix-policyd-spf-python
#	  significaría: SI existe "postfix" entonces debe existir "policyd-spf" y ello depende del paquete postfix-policyd-spf-python
#	- Para un ejecutable que es incompatible con otro, separarlos entre "!"
#	  Por ejemplo: postfix!exigrep/exim4-base
#	  significaría: SI existe "postfix" entonces NO debe existir "exigrep" y ello se resuelve desinstalando el paquete exim4-base
# Depends on functions: Is_Executable
# Depends on software packages: grep, sed
{
	local ListaDependencias="$1"
	local SenyalarEjecutable="$2"
	local DependenciaActual=''
	local EjecutablesActuales=''
	local EjecutableActual=''
	local EncontradoActual=''
	local PaqueteActual=''
	local Value=''
	
	unset IFS ; for DependenciaActual in $ListaDependencias ; do
		EjecutablesActuales="$(printf '%s' "$DependenciaActual" | cut -f 1 -d '/')"
		PaqueteActual="$(printf '%s' "$DependenciaActual" | cut -f 2 -d '/')"
		EjecutablesActuales="$(printf '%s' "$EjecutablesActuales" | tr -s ':' ' ')"
		EncontradoActual=''
		unset IFS ; for EjecutableActual in $EjecutablesActuales ; do
			if Is_Executable "$EjecutableActual" ; then
				EncontradoActual="1"
			fi
		done
		if [ "$EncontradoActual" = "" ] ; then
			if [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual\[")" = "" ] || [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual ")" = "" ] ; then
				if [ "$EjecutableActual" = "$PaqueteActual" ] || [ "$EjecutableActual" != "$EjecutablesActuales" ] || [ "$SenyalarEjecutable" = "0" ] ; then
					Value="$Value $PaqueteActual"
				else
					Value="$Value $PaqueteActual[$EjecutableActual]"
				fi
			fi
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

NuevoTemporalSeguro ()
# Sintaxis como función: $(NuevoTemporalSeguro "$NombreBase" "$Extension" "$DirTemp")
# Descripción:
#	Crea y devuelve (stdout) la ruta de un fichero de tipo
#	/run/shm/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
#	/tmp/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
# Parámetros esperados:
#	$1	(opcional) Etiqueta ilustrativa
#	$2	(opcional) Extensión a añadir al nombre del fichero
#	$3	(opcional) Directorio-madre preferido en lugar de /run/shm o /tmp
# Notas:
#	- Si necesita ejecutar el fichero, debe evitar su creación en puntos como /run (noexec)
#	  especificando DirTemp
#	- Tambien es útil porque salva la falta de opciones --xx de versiones antiguas de mktemp
#	- Para directorios es mejor usar: TempName="${DirTemp}/${ProgramName}.$(id -un).$$.funcion" ; MkdirPP "$TempName" '' u=rw,g=r,o=
{
	local NombreBase="$1"
	local Extension="$2"
	local DirTemp="$3"
	local RutaDir=''
	local Tiempo=''
	local Valor=''
	local ModernTool=''
	local LastStatus=0
	local StatusCode=0
	
	RutaDir="${DirTemp}/$(id -un)"
	if [ "$NombreBase" != "" ] ; then
		RutaDir="${RutaDir}/${NombreBase}"
	fi
	mkdir -p "$RutaDir"
	chmod u=rwX,go= "${DirTemp}/$(id -un)"
	Tiempo="$(date +'%Y%m%d-%H%M%S')"
#	Tiempo="$(printf '%(%Y%m%d-%H%M%S)T')"  BASHISM
	if [ "$Extension" = "" ] ; then
		Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		ModernTool="$(mktemp --help 2>&1 | grep -e "--suffix")"
		if [ "$ModernTool" != "" ] ; then
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX --suffix=.${Extension})"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				mv "$Valor" "${Valor}.${Extension}"
				Valor="${Valor}.${Extension}"
			fi
		fi
	fi
	if [ "$Valor" != "" ] ; then
		if [ -f "$Valor" ] ; then
			printf '%s\n' "$Valor"
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

IniSectionContent ()
# Syntax as a function: $(IniSectionContent "$FileOrContent" "$SectionName")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	[section name] without brackets []. Example: "global"
#		specifying "[]" returns file content before any section declaration.
# Notes:
#	- Includes section header 
# Depends on functions:	(none)
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local SectionName="$2"
	local LinesNr=0
	local Value=''
	
	if [ "$FileOrContent" != "" ] && [ "$SectionName" != "" ] ; then
		if [ -f "$FileOrContent" ] ; then
			Value="$(cat "$FileOrContent")"
		else
			Value="$FileOrContent"
		fi
		if [ "$SectionName" != "[]" ] ; then
			Value="$(printf '%s\n' "$Value" | sed -rne "/^[[:blank:]]*[[]${SectionName}[]]/,//p")"
			if [ "$Value" != "" ] ; then
				printf '%s\n' "$Value" | head -n 1	# Print before being filtered
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				# Omit current section header
				Value="$(printf '%s\n' "$Value" | tail -n $((LinesNr - 1)))"
				Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q')"
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				if [ "$(printf '%s\n' "$Value" | tail -n 1 | grep -e '^ [[].*[]]' -e '^[[].*[]]')" != "" ] && [ $LinesNr -ge 2 ] ; then
					# Omit next section header
					Value="$(printf '%s\n' "$Value" | head -n $((LinesNr - 1)))"
				else
					# Omit when desired section is empty and first line is next section's header
					Value="$(printf '%s\n' "$Value" | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
				fi
			fi
		else
			Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IniVarValue ()
# Syntax as a function: $(IniVarValue "$FileOrContent" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Default (empty): no section consideration. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. Default; empty string.
#	$5	(optional or empty) Separator between variable name and assigned value. Default: "=". Examples: "=" ":="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value. Default (empty): No end mark. Example: ";"
#	$7	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# To Do:
#	- This (or another function) to load more than one variable at once.
# Notes:
#	- if section is not specified or empty, returns last match from whole file content.
#	- if section is "[]", queries only file content before any [section] declaration.
# Depends on functions: IniSectionContent
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local VariableName="$2"
	local SectionName="$3"
	local NotFoundValue="$4"
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local DefaultsFile="$7"
	local LinesNr=0
	local SectionContent=''
	local SeparatorMask='IVVtmpbHckF2LMB4tmpWz2coasdb3tmpX7LuyGTvrW'
	local NotFoundKey='IVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	
	if [ "$FileOrContent" != "" ] ; then
		if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator='=' ; fi
		if [ "$SectionName" = "" ] ; then
			if [ -f "$FileOrContent" ] ; then
				SectionContent="$(cat "$FileOrContent")"
			else
				SectionContent="$FileOrContent"
			fi
		else
			SectionContent="$(IniSectionContent "$FileOrContent" "$SectionName")"
		fi
		Value="$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}" | tail -n 1 | sed -e "s|${NameValueSeparator}|${SeparatorMask}|")"
		if [ "$Value" != "" ] ; then
			# Variable found; let's separate value.
			Value="$(printf '%s\n' "$Value" | sed -e "s|.*${SeparatorMask}||")"
			Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			if [ "$EndVariableSymbol" != "" ] ; then
				Value="$(printf '%s\n' "$Value" | sed -e "s|${EndVariableSymbol}$||")"
				Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			fi
			if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
				# Double quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
			else
				if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
					# Single quotes
					Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
				fi
			fi
		else
			Value="$NotFoundKey"
		fi
	else
		Value="$NotFoundKey"
	fi
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$DefaultsFile" != "$FileOrContent" ] && [ -f "$DefaultsFile" ] ; then
			Value="$(IniVarValue "$DefaultsFile" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" '')"
		else
			Value="$NotFoundValue"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SetIniVarValue ()
# Syntax as a sentence: SetIniVarValue "$File" "$VariableName" "$SectionName" "$NewValue" "$NameValueSeparator" "$PreComment"
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to write for the variable entry.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#		PreComment can contain escaped \n to be converted into line breaks.
# To Do:
#	- Process content separated by section (treat different variable as in different section)
#	- This (or another function) to set more than one variable at once.
# Notes:
#	- if section is not specified or empty, sets matches in the whole file.
#	- if section is "[]", sets variable only before any [section] declaration in the file.
#	- Some blank lines can be lost in resulting file content
# Depends on functions: Dirname IniSectionContent
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"	# Optional or empty (examples: "global" "")
	local NewValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"	# Optional or empty (example: ":=") Default is "="
	local PreComment="$6"	# Optional line to precede variable's line if it must be added. Comment mark should be included.
	local OldContent=''
	local Part1=''
	local SectionContent=''
	local Part2=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$File" != "" ] && [ "$VariableName" != "" ] ; then
		if [ ! -f "$File" ] ; then
			mkdir -p "$(Dirname "$File")"
			touch "$File"
		fi
		if [ -f "$File" ] ; then
			if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator="=" ; fi
			OldContent="$(cat "$File")"
			if [ "$SectionName" != "" ] ; then
				OldContentLinesNr=$(printf '%s\n' "$OldContent" | wc -l)
				if [ "$SectionName" = "[]" ] ; then
					SectionContent="$(printf '%s\n' "$OldContent" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^[[].*[]]' -ve '^ [[].*[]]')"
					Part1LinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
					cat /dev/null > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
						if [ "$SectionContent" != "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PreComment" != "" ] ; then
							if [ "$(cat "$File" 2>/dev/null)" != "" ] && [ "$(cat "$File" | tail -n 1)" != "" ] ; then
								printf '%s\n' "" >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr)) >> "$File"
				else
					if [ "$(printf '%s\n' "$OldContent" | grep -e "^[[]${SectionName}[]]" -e "^ [[]${SectionName}[]]")" != "" ] ; then
						# Case sensitive to match sed that cannot be insensitive with q/p commands
						Part1="$(printf '%s\n' "$OldContent" | sed -re "/^[[:blank:]]*[[]${SectionName}[]]/q")"
						Part1LinesNr=$(printf '%s\n' "$Part1" | wc -l)
						Part1LinesNr=$((Part1LinesNr - 1))
						Part1="$(printf '%s\n' "$Part1" | grep -ive "^[[]${SectionName}[]]" -ive "^ [[]${SectionName}[]]")"
						SectionContent="$(IniSectionContent "$File" "$SectionName")"
						SectionLinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
						Part2="$(printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr - SectionLinesNr)))"
						cat /dev/null > "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$Part1" != "" ] ; then
							printf '%s\n' "$Part1" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$PreComment" != "" ] ; then
								printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$Part2" != "" ] ; then
							printf '%s\n' "$Part2" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						printf '%s\n' "[${SectionName}]" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$PreComment" != "" ] ; then
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ "$(printf '%s\n' "$OldContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
					if [ "$PreComment" != "" ] ; then
						printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "$OldContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|i" > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: SetIniVarValue: File and/or VariableName not specified.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

GetOrSetIniVarValue ()
# Syntax as a function: $(GetOrSetIniVarValue "$File" "$VariableName" "$SectionName" "$DefaultValue" "$NameValueSeparator" "$EndVariableSymbol" "$PreComment" "$ReadOnly" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value and to be written when needed. Example: ";"
#	$7	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#	$8	(optional or empty) "1" or "ro" if $File must not be modified. Assumed no by default (0)
#	$9	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# Notes:
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- PreComment can contain escaped \n to be converted into line breaks.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local PreComment="$7"
	local ReadOnly="$8"
	local DefaultsFile="$9"
	local NotFoundKey='GOSIVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	Value="$(IniVarValue "$File" "$VariableName" "$SectionName" "$NotFoundKey" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")"
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$ReadOnly" != "1" ] && [ "$ReadOnly" != "ro" ] ; then
			SetIniVarValue "$File" "$VariableName" "$SectionName" "${DefaultValue}${EndVariableSymbol}" "$NameValueSeparator" "$PreComment"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		Value="$DefaultValue"
		# EndVariableSymbol does not come with DefaultValue
		if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
			# Double quotes
			Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
		else
			if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
				# Single quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

LoadVarsValues ()
# Syntax as a sentence: LoadVarsValues $FileOrContent $VariablesNames $VarsRequired $DefaultsFileOrContent
# Descripcion: Runs global variables assignation same as specified in $FileOrContent
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$3	Variable names to filter (case sensitive, space separated). If empty "", all file variables will be loaded.
#	$2	0 = No error on variables lack. 1 = Only error if all variables lack. 2 = Result error on any variable lack (it only returns exitcode 104; no messages)
#	$4	(optional or empty) File path (or vars string) to do a previous load from, if found.
# To Do:
#	- 
# Notes:
#	- Indentation are supported (initial tabs and/or spaces).
# Depends on functions: (none)
# Depends on software packages: grep, sed
{
	lvv__FileOrContent="$1"
	lvv__VariablesNames="$2"
	lvv__VarsRequired="$3"
	lvv__DefaultsFileOrContent="$4"
	lvv__CurVarName=''
	lvv__VarsFilter=''
	lvv__LoadedContent=''
	lvv__LastStatus=0
	lvv__StatusCode=0
	
	if [ "$lvv__VariablesNames" != "" ] ; then
		unset IFS ; for lvv__CurVarName in $lvv__VariablesNames ; do
			lvv__VarsFilter="$lvv__VarsFilter -e ^${lvv__CurVarName}="
		done
	else
		lvv__VarsFilter=" -ie ^[a-z].*="
	fi
	if [ "$lvv__DefaultsFileOrContent" != "" ] ; then
		if [ -f "$lvv__DefaultsFileOrContent" ] ; then
			lvv__DefaultsFileOrContent="$(cat "$lvv__DefaultsFileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__DefaultsFileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
		fi
	fi
	if [ "$lvv__FileOrContent" != "" ] ; then
		if [ -f "$lvv__FileOrContent" ] ; then
			lvv__FileOrContent="$(cat "$lvv__FileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__FileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
			if [ "$lvv__VarsRequired" = "2" ] && [ "$lvv__VariablesNames" != "" ] ; then
				unset IFS ; for lvv__CurVarName in $lvv__VariablesNames ; do
					if [ "$(printf '%s' "$lvv__LoadedContent" | grep -e "^${lvv__CurVarName}=")" = "" ] ; then
						lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
					fi
				done
			fi
		else
			if [ "$lvv__VarsRequired" = "1" ] || [ "$lvv__VarsRequired" = "2" ] ; then
				lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
			fi
		fi
	fi
	return $lvv__StatusCode
}

LogToFile ()
# Syntax as a sentence: LogToFile $LogFile $DatetimeStamp $MaxLogLines $Message
# Description: Appends message to text file
# Expected parameters:
#	$1	File path to write to
#	$2	"0" to not prepend line with date-time stamp. Any other value (1) to yes write it.
#	$3	"0" to infinite log lines or a greater number to leave limited log tail
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- This function can be called without message to only cut file to last $MaxLogLines
# Depends on functions: Is_IntegerNr Dirname
# Depends on software packages: sed
# Supports environment variables: LogTime
{
	local LogFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DatetimeStamp="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local MaxLogLines="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local AskedText=''
	local TextLinesNr=0
	local CurString=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LogFile" != "" ] ; then
		if [ "$DatetimeStamp" = "0" ] ; then
			DatetimeStamp=''
		else
#			DatetimeStamp="$(printf '%(%Y-%m-%dT%T%z)T')"  BASHISM
			if Is_IntegerNr "$LogTime" ; then
				DatetimeStamp="$(date -d @$LogTime '+%Y-%m-%dT%T%z') "
			else
				DatetimeStamp="$(date '+%Y-%m-%dT%T%z') "
			fi
		fi
		unset IFS ; for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ "$AskedText" != "" ] ; then
			TextLinesNr=$(printf '%s\n' "$AskedText" | wc -l)
		else
			TextLinesNr=0
		fi
		mkdir -p "$(Dirname "$LogFile")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			if Is_IntegerNr "$MaxLogLines" && [ $MaxLogLines -gt 0 ] && [ $(cat "$LogFile" 2>&1 | wc -l) -gt $((MaxLogLines+$TextLinesNr)) ] ; then
				LoggingLog="$(cp --attributes-only -a "$LogFile" "${LogFile}.tmp" 2>&1)"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					cat "$LogFile" | tail -n $((MaxLogLines-TextLinesNr)) > "${LogFile}.tmp"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $StatusCode -eq 0 ] ; then
					mv "${LogFile}.tmp" "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				rm -f "${LogFile}.tmp"
				if [ $StatusCode -eq 0 ] ; then
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					# If it's out of space, then try to overwrite previous log.
					printf '%s\n' "$LoggingLog" > "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ $TextLinesNr -gt 0 ] ; then
					printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Log file not specified${fRESET}" 1>&2
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

LogProgram ()
# Syntax as a sentence: LogProgram $ThisMessageLevel $Message
# Description: Writes message to configured log file, depending on configured LogLevel vs ThisMessageLevel for message.
# Expected parameters:
#	$1	Level number of the message:
#		0=NothingToSay 1=Error 2=Warning 3=NormalInfo 4=DebugInfo
#		(same numbers in negative sign to not write date-time stamp)
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- TTY warning/error/debug formats are already put
#	- stderr is already used for warning/error/debug messages
#	- I:/W:/E: prefixes are NOT added automatically.
# Depends on functions: Is_IntegerNr Dirname LogToFile
# Depends on software packages: perl-base sed
# Supports environment variables: LogLevel MainLog MainControllerLog INIT_SCRIPT_InitCall MaxLogLines DataIsPublic LogTime
{
	local ThisMessageLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local LogFile=''
	local DatetimeStamp=0
	local AskedText=''
	local CurString=''
	local ProgramLogLevel=3
	local RedirectedStdout=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"  #'
	RedirectedStdout=$?
	if [ $RedirectedStdout -ne 0 ] ; then
		sWARN=''
		fRESET=''
		sERROR=''
		sGOOD=''
	fi
	if [ $ThisMessageLevel -ge 0 ] ; then
		DatetimeStamp=1
	fi
	ThisMessageLevel="$(printf '%s\n' "$ThisMessageLevel" | sed -e 's|^-||g')"
	if Is_IntegerNr "$LogLevel" ; then ProgramLogLevel=$LogLevel ; fi
	if [ "$*" != "" ] && [ $ProgramLogLevel -ge $ThisMessageLevel ] && [ $ThisMessageLevel -gt 0 ] ; then
		unset IFS ; for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ $ThisMessageLevel -ge 4 ] ; then
			printf '%s\n' "${sINFO}${AskedText}${fRESET}" 1>&2
		else
			if [ $ThisMessageLevel -eq 3 ] ; then
				printf '%s\n' "$AskedText"
			else
				if [ $ThisMessageLevel -eq 2 ] ; then
					printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
				else
					if [ $ThisMessageLevel -eq 1 ] ; then
						printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
					else
						printf '%s\n' "$AskedText" 1>&2
					fi
				fi
			fi
		fi
		LogFile="$MainLog"
		if [ "$LogFile" = "" ] && [ "$MainControllerLog" != "" ] ; then
			LogFile="$MainControllerLog"
			if [ "$INIT_SCRIPT_InitCall" != "1" ] ; then
				CallType=" ${ParO} Manual ${ParC} "
			fi
		fi
		if [ "$LogFile" != "" ] ; then
			if [ "$DataIsPublic" != "" ] && [ ! -f "$LogFile" ] ; then
				if [ ! -d "$(Dirname "$LogFile")" ] ; then
					mkdir -p "$(Dirname "$LogFile")"
					if [ "$DataIsPublic" = "1" ] ; then
						chmod a+rX "$(Dirname "$LogFile")"
					else
						chmod g-w,o= "$(Dirname "$LogFile")"
					fi
				fi
				touch "$LogFile"
				if [ "$DataIsPublic" = "1" ] ; then
					chmod a+r "$LogFile"
				else
					chmod g-w,o= "$LogFile"
				fi
			fi
			LoggingLog="$(LogToFile "$LogFile" "$DatetimeStamp" "$MaxLogLines" "${CallType}${AskedText}")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "${sERROR}${LoggingLog}${fRESET}" 1>&2
			fi
		fi
	fi
	return $StatusCode
}

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes:
#	- Defined functions are not copied to temporay script, so don't make it call them
#	- In some rare case, neither eval/command/exec may work well
# Depends on functions: NuevoTemporalSeguro
# Depends on software packages: (none)
{
	local ScriptFile=""
	local ArgumentNr=0
	local CurrentArgument=""
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	ScriptFile="$(NuevoTemporalSeguro '' sh "$DirTempX")"
	printf '%s\n' '#!/bin/sh' > "$ScriptFile"
	while [ $# -gt 0 ] ; do
		CurrentArgument="$1"
		ArgumentNr=$((ArgumentNr + 1))
		if [ $ArgumentNr -gt 1 ] ; then
			printf ' ' >> "$ScriptFile"
		fi
		if [ "$CurrentArgument" = "" ] ; then CurrentArgument="''" ; fi
		if [ "$CurrentArgument" != "\"\"" ] && [ "$CurrentArgument" != "''" ] ; then
			EvalArgument="$(eval printf '%s' "$CurrentArgument" 2>/dev/null)"
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then CurrentArgument="$EvalArgument" ; fi
		fi
		printf '%s' "$CurrentArgument" >> "$ScriptFile"
		shift
	done
	printf '\n' >> "$ScriptFile"
	printf '%s\n' 'exit $?' >> "$ScriptFile"
	chmod u+x "$ScriptFile"
	"$ScriptFile"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm "$ScriptFile"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}


##### PROGRAM TEMPLATE BASE FUNCTIONS #####

BreakingControls ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: script.sh
{
	local ReservedWords="reload,create,delete,list,details,uninstall,purge,start,stop,restart,status,enable,disable"
	
	if [ $(id -g) -ne 0 ] && [ "$RootRequired" != "0" ] ; then
		printf '%s\n' "${sERROR}E: This program needs to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
		exit 45
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ServiceName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ServiceName cannot match the reserved word \"${ServiceName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ProgramName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ProgramName cannot match the reserved word \"${ProgramName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildSingularName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildSingularName cannot match the reserved word \"${ChildSingularName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildsPluralName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildsPluralName cannot match the reserved word \"${ChildsPluralName}\"${fRESET}" 1>&2
		exit 92
	fi
}

MkdirAndOrPublic ()
# Syntax as a sentence: MkdirAndOrPublic $NewPath [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: script.sh
# Description: Similar job as "mkdir -p" but is allows owner setting and permissions and different behaviour by $DataIsPublic variable
# Expected parameters:
#	$1	Directory path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown.
#	$3	(optional) Permissions specification as allowed by chmod. Environment variable $DataIsPublic will override permissions to others can read or not
#	$4	(optional) Additional permissions specification to set (useful for g+s not being applied by MkdirAndOrPublic)
# Notes:
#	- Owners/permissions are only set when creating subdirectories; not when they already exist.
#	- Purpose on not using MkdirPP() is to avoid its large dependencies.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local NewPath="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local CurrentElement=''
	local PreviousDir=''
	local LastStatus=0
	local StatusCode=0
	
	PreviousDir="$(pwd)"
	IFS='/' ; for CurrentElement in $NewPath ; do unset IFS
#	IFS="$(printf "/")" ; for CurrentElement in $NewPath ; do unset IFS
		if [ $StatusCode -eq 0 ] ; then
			if [ "$CurrentElement" = "" ] ; then
				cd /
			else
				if [ -e "$CurrentElement" ] ; then
					cd "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					mkdir "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
							chown "$OwningForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
							chmod "$PermissionsForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] ; then
							chmod "$MorePermissions" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$DataIsPublic" = "1" ] ; then
							chmod a+rX "$CurrentElement"
						else
							chmod g-w,o= "$CurrentElement"
						fi
						cd "$CurrentElement"
					fi
				fi
			fi
		fi
	done
	cd "$PreviousDir"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DeleteIfNotMe ()
{
	local FileToDelete="$1"
	local LastStatus=0
	
	if [ "$FileToDelete" != "" ] && [ "$FileToDelete" != "$MeCallFile" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeCallFile" ] && [ "$FileToDelete" != "$MeExecutable" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeExecutable" ] ; then
		rm -fr "$FileToDelete"
		LastStatus=$?
	fi
	return $LastStatus
}

FoundProgramScript ()
{
	local Value=""
	if [ -x "/bin/${ProgramName}" ] ; then Value="/bin/${ProgramName}" ; fi
	if [ -x "/sbin/${ProgramName}" ] ; then Value="/sbin/${ProgramName}" ; fi
	if [ -x "/usr/bin/${ProgramName}" ] ; then Value="/usr/bin/${ProgramName}" ; fi
	if [ -x "/usr/sbin/${ProgramName}" ] ; then Value="/usr/sbin/${ProgramName}" ; fi
	if [ -x "/usr/local/bin/${ProgramName}" ] ; then Value="/usr/local/bin/${ProgramName}" ; fi
	if [ -x "/usr/local/sbin/${ProgramName}" ] ; then Value="/usr/local/sbin/${ProgramName}" ; fi
	if [ -x "/bin/${ProgramName}.sh" ] ; then Value="/bin/${ProgramName}.sh" ; fi
	if [ -x "/sbin/${ProgramName}.sh" ] ; then Value="/sbin/${ProgramName}.sh" ; fi
	if [ -x "/usr/bin/${ProgramName}.sh" ] ; then Value="/usr/bin/${ProgramName}.sh" ; fi
	if [ -x "/usr/sbin/${ProgramName}.sh" ] ; then Value="/usr/sbin/${ProgramName}.sh" ; fi
	if [ -x "/usr/local/bin/${ProgramName}.sh" ] ; then Value="/usr/local/bin/${ProgramName}.sh" ; fi
	if [ -x "/usr/local/sbin/${ProgramName}.sh" ] ; then Value="/usr/local/sbin/${ProgramName}.sh" ; fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ScriptHeaderValue ()
# Note: it's absolutely case-sensitive
{
	local HeaderKey="$1"
	local VariableKey=""
	local Value=""
	
	if [ "$(cat "$MeExecutable" | grep -e '^:.*SCRIPTHEADER$')" != "" ] && [ "$(cat "$MeExecutable" | grep -e '^SCRIPTHEADER$')" != "" ] ; then
		Value="$(echo aaa$(cat "$MeExecutable" | sed -ne '/^:.*SCRIPTHEADER$/,//p' | sed -e '/^SCRIPTHEADER$/q' | grep -e "^${HeaderKey}:" | head -n 1 | cut -f 2- -d ':') | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

MeInstalled ()
# Returns 1 if this script is placed as system installed path.
{
	local Value=0
	
	if [ "$(printf '%s\n' "$MeExecutable" | grep -e 'bin/')" != "" ] ; then
		Value=1
	fi
	printf '%s\n' "$Value"
}

Install_precp ()
# WARNING: Package manager cannot invoke this action before this script file is installed. Install_postcp() calls this with argument "postcp" if didn't run.
{
	local Mode="$1"
	local Mode2="$2"
	local TheFoundProgramScript=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Pre "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	TheFoundProgramScript="$(FoundProgramScript)"
	if [ $StatusCode -eq 0 ] && [ -x "$TheFoundProgramScript" ] ; then
		if [ "$Mode" != "postcp" ] ; then
			if [ "$PackageManager_Call" = "" ] && [ "$Mode" != "force" ] && [ "$Mode2" != "force" ] ; then
#				if [ "$(IniVarValue "$(cat "$TheFoundProgramScript" | grep -e '^ProgramInstaller=' | head -n 1)" ProgramInstaller '' '' = '' '')" = "0" ] ; then
				if [ "$(cat "$TheFoundProgramScript" | grep -e '^ProgramInstaller=' | head -n 1 | grep -e '^ProgramInstaller=0')" != "" ] ; then
					printf '%s\n' "${sERROR}E: Program already installed. Use your package manager to un/install $(printf '%s\n' "$TheFoundProgramScript" | tr -s '/' '\n' | tail -n 1).${fRESET}" 1>&2
					LastStatus=99 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "Trying to uninstall previous $ProgramName"
				"$TheFoundProgramScript" uninstall "$@"
				LastStatus=$?
				if [ $LastStatus -eq 87 ] || [ $LastStatus -eq 127 ] ; then # Unknown action
					rm "$TheFoundProgramScript"
					LastStatus=$?
				fi
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ -x "$TheFoundProgramScript" ] ; then
					printf '%s\n' "${sWARN}W: old executable $TheFoundProgramScript not removed.${fRESET}" 1>&2
					printf '%s\n' "Actions recommended:"
					printf '%s\n' "	1. Retry un/installation"
					printf '%s\n' "	2. Remove old program manually if necessary"
				fi
				if [ $StatusCode -eq 0 ] ; then
					# Important to do this after uninstalling previous version.
					Configuration "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ -d "$SystemConfigDir" ] ; then
						printf '%s\n' "System level configurations directory is: $SystemConfigDir"
					fi
					printf '%s\n' "System level parameters can be at: $SystemConfigFile"
				fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Post "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Old executables were a duplicity of program script.
		DeleteIfNotMe "/bin/${ProgramName}"
		DeleteIfNotMe "/sbin/${ProgramName}"
		DeleteIfNotMe "/usr/bin/${ProgramName}"
		DeleteIfNotMe "/usr/sbin/${ProgramName}"
		DeleteIfNotMe "/usr/local/bin/${ProgramName}"
		DeleteIfNotMe "/usr/local/sbin/${ProgramName}"
		DeleteIfNotMe "/bin/${ProgramName}.sh"
		DeleteIfNotMe "/sbin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/bin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/sbin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/local/bin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/local/sbin/${ProgramName}.sh"
		DeleteIfNotMe "$InitExecutableLink"
		DeleteIfNotMe "$ProgramExecutablePath"
	fi
	touch "${DirTemp}/${ProgramName}.install-precp.done"
	return $StatusCode
}

Install_cp ()
{
	local SourceScript="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$SourceScript" != "$ProgramExecutablePath" ] && [ "$SourceScript" != "$InitExecutableLink" ] ; then
			PreviousUmask="$(umask)"
			umask u=rwx,go=rx
			mkdir -p "$(Dirname "$ProgramExecutablePath")"
			umask "$PreviousUmask"
			cp "$SourceScript" "$ProgramExecutablePath"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chown root:root "$ProgramExecutablePath"
			chmod u=rwx,go=rx "$ProgramExecutablePath"
		else
			printf '%s\n' "${sERROR}E: Cannot install from destination path.${fRESET}" 1>&2
			LastStatus=94 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Install_postcp ()
{
	local MinAgeLine=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -f "${DirTemp}/${ProgramName}.install-precp.done" ] ; then
		Install_precp postcp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		rm -f "${DirTemp}/${ProgramName}.install-precp.done"
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_postcp_More "$(Dirname "$MeExecutable")" "$@"	#"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Writing system-wide defaults
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$LogRotation" != "" ] && [ -d /etc/logrotate.d ] && [ $(id -u) -eq 0 ] ; then
		if [ "$LogRotation" != "hourly" ] ; then MinAgeLine='minage 1' ; fi
		printf '%s\n' "\"${MainLog}\" {
	compress
	notifempty
	nocreate
	rotate 5
	$LogRotation
	$MinAgeLine
	missingok
}" > "/etc/logrotate.d/${ProgramName}"
	fi
	return $StatusCode
}

Uninstall_predel ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_predel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_del ()
{
	local MeVersion=''
	MeVersion="$(ScriptHeaderValue Version)"
#	DeleteIfNotMe "$ProgramExecutablePath"
#	DeleteIfNotMe "/bin/${ProgramName}"
#	DeleteIfNotMe "/sbin/${ProgramName}"
#	DeleteIfNotMe "/usr/bin/${ProgramName}"
#	DeleteIfNotMe "/usr/sbin/${ProgramName}"
#	DeleteIfNotMe "/usr/local/bin/${ProgramName}"
#	DeleteIfNotMe "/usr/local/sbin/${ProgramName}"
	rm -f "/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "$ProgramExecutablePath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "$ProgramName $MeVersion : Program files removed."
	fi
}

Uninstall_postdel ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_Postdeletion "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_purge ()
{
	local LastStatus=0
	local StatusCode=0
	
	rm -f "${DirTempX}/${ProgramName}.uninstall.tmp" "/var/tmp/${ProgramName}.uninstall.tmp"
	Uninstall_purge_MorePre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ -f "$SystemConfigFile" ] ; then
		printf '%s\n' "Removing: $SystemConfigFile"
		rm "$SystemConfigFile"
	fi
	if [ -d "$SystemConfigDir" ] ; then
		printf '%s\n' "Removing: $SystemConfigDir"
		rm -r "$SystemConfigDir"
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		rm -fr "$Childs_logs"
	fi
	rm -f "$MainLog"
	rm -f "/var/log/upstart/${ProgramName}."*
	rm -f "/etc/logrotate.d/${ProgramName}"
	Uninstall_purge_MorePost "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -eq 0 ] ; then
		printf '%s\n' "Configurations and logs cleaned."
	fi
	return $StatusCode
}

GetSetLocalConfig ()
# Syntax as a function: $(GetSetLocalConfig "$ReadOnly" "$VariableName" "$SectionName" "$DefaultValue" "$PreComment")

# Function taken from script.sh template.

# Description: Gets the most local value for a configuration (1:user 2:system 3:filedefault 4:specifieddefault) and optionally writes if nowhere found.
# Expected parameters:
#	$1	"1" to not write anything at files; "0" to save variable when not found.
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
# Notes:
#	- NOT USEFUL when default value can be invalid for unprivileged user, such as file/dir paths to write: [system.conf=]/etc/custom.list vs [user.conf=]~/custom.list
#	  NOT USEFUL for other config files but SystemConfigFile & UserConfigFile
#	  In these cases, it's convenient to use GetOrSetIniVarValue()
#	- USEFUL when need to use system-level value on user-level lack.
#	- $SystemDefaults is used as DefaultsFile. A found value here prevails over 4th parameter (DefaultValue)
#	- If DefaultValue is to be written (because no value found anywhere and ReadOnly==0), root user will write to $SystemConfigFile and others will write to $UserConfigFile
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- Separator between variable name and assigned value is assumed to be '='
#	- No end variable/assignation mark assumed.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on other software: grep
{
	local ReadOnly="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local PreComment="$5"
	local DefaultsFile="$SystemDefaults"
	local NotFoundKey=''
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	NotFoundKey="#GOSIVV$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')#"
	Value="$(IniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' '')"
	if [ "$Value" = "$NotFoundKey" ] ; then
		Value="$(IniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' "$DefaultsFile")"
		if [ "$Value" = "$NotFoundKey" ] ; then
			if [ $ReadOnly -eq 0 ] ; then
				if [ $(id -u) -eq 0 ] ; then
					SetIniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					SetIniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			Value="$DefaultValue"
			if [ "$(printf '%s\n' "$Value" | grep -e '^".*"$')" != "" ] ; then Value="$(printf '%s\n' "$Value" | cut -f 2 -d '"')" ; fi	#'
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	return $StatusCode
}

Configuration ()
{
	local ReadOnly=0
	local BaseName=''
	local LastStatus=0
	local StatusCode=0

	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
	MeExecutable="$(ReadlinkF "$MeCallFile")"
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	BreakingControls
	if [ "$1" = "pmc" ] ; then
		PackageManager_Call="$2"
		PackageManager_Mode="$3"
		PackageManager_ForVersion="$4"
	else
		PackageManager_Call=''
	fi
	if [ "$RootRequired" = "0" ] ; then
		ProgramExecutablePath="/bin/${ProgramName}"
	else
		ProgramExecutablePath="/sbin/${ProgramName}"
	fi
#	if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
		ProgramExecutablePath="/usr${ProgramExecutablePath}"
#	fi
	Configuration_Saved_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ ! -d "$SystemConfigDir" ] && [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigDir")" ] ; then
		MkdirAndOrPublic "$SystemConfigDir" '' u=rwX,go=rX
	fi
	SystemDefaults="${SystemConfigDir}/system.defaults"	# This file is never written by program; only provided by package manager and updates
	SystemConfigFile="${SystemConfigDir}/system.conf"
	if [ -f "${SystemConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] && [ $(id -u) -eq 0 ] ; then
			mv "${SystemConfigDir}/default.conf" "$SystemConfigFile"
		else
			SystemConfigFile="${SystemConfigDir}/default.conf"
		fi
	fi
	BaseName="$(printf '%s\n' "$SystemConfigDir" | tr -s '/' '\n' | tail -n 1)"
	UserDir="$HOME"
	if [ ! -d "$UserDir" ] ; then
		# Sometimes Systemd does not set HOME variable
		UserDir="$(cat /etc/passwd | grep -e "^$(id -un):" | cut -f 6 -d ':')"
		if [ ! -d "$UserDir" ] && [ "$HOME" != "" ] ; then
			UserDir="$HOME"
		fi
	fi
	if [ "$(printf '%s\n' "$BaseName" | grep -e '^\.')" != "" ] ; then
		UserConfigDir="${UserDir}/${BaseName}"
	else
		UserConfigDir="${UserDir}/.${BaseName}"
	fi
	if [ ! -d "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	if [ ! -w "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	UserDefaults="${UserConfigDir}/user.defaults"	# This file is never written by program; only provided by package manager/skel
	UserConfigFile="${UserConfigDir}/user.conf"
	if [ $ReadOnly -eq 0 ] && [ ! -d "$UserConfigDir" ] ; then
		MkdirAndOrPublic "$UserConfigDir"
	fi
	if [ -f "${UserConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] ; then
			mv "${UserConfigDir}/default.conf" "$UserConfigFile"
		else
			UserConfigFile="${UserConfigDir}/default.conf"
		fi
	fi
	if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigFile")" ] ; then
		if [ ! -f "$SystemConfigFile" ] ; then
			printf '%s\n' "# $(ScriptHeaderValue Description)" >> "$SystemConfigFile"
			if [ "$RootRequired" = "0" ] ; then
				printf '%s\n' "# $ProgramName system-wide and user-default working parameters" >> "$SystemConfigFile"
			else
				printf '%s\n' "# $ProgramName working parameters" >> "$SystemConfigFile"
			fi
			printf '%s\n' "" >> "$SystemConfigFile"
		fi
	fi
	CurrentConfigDir="$SystemConfigDir"
	CurrentConfigFile="$SystemConfigFile"
	if [ $(id -u) -ne 0 ] && [ "$RootRequired" = "0" ] ; then
		CurrentConfigDir="$UserConfigDir"
		CurrentConfigFile="$UserConfigFile"
		if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$UserConfigFile")" ] ; then
			if [ ! -f "$UserConfigFile" ] ; then
				printf '%s\n' "# $(ScriptHeaderValue Description)" >> "$UserConfigFile"
				printf '%s\n' "# $ProgramName user working parameters. This overrides configurations over $SystemConfigFile" >> "$UserConfigFile"
				printf '%s\n' "" >> "$UserConfigFile"
			fi
		fi
	fi
	MainLog="/var/log/${ProgramName}.log"
	if [ $(id -u) -ne 0 ] ; then
		MainLog="${UserConfigDir}/main.log"
	fi
	if [ "$LogLevel" != "4" ] ; then	# Debugging probably a nested call (exported LogLevel)
		LoadVarsValues "$UserConfigFile" 'LogLevel MaxLogLines' 2 "$SystemConfigFile"
		LastStatus=$?
		if [ $LastStatus -eq 104 ] ; then
			LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '# LogLevel: 0=Nothing 1=Errors 2=Warnings+E 3=Info+W+E 4=Debug')"
			MaxLogLines="$(GetSetLocalConfig "$ReadOnly" MaxLogLines '' 0 '\n# MaxLogLines: 0=Unlimited')"
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if ! Is_IntegerNr "$LogLevel" ; then LogLevel=3 ; fi
	fi
	if [ $ReadOnly -eq 0 ] ; then
		Configuration_Saved_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		Configuration_Saved_Post ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}


##### PROGRAM FUNCTIONS from @-funcions x.x.x #####

#[gesdonis]
WhereProgram ()
# Syntax as a function: $(WhereProgram $Command)
# Description: Returns existing program filepath from specified command, or internal shell program or function
# Notes:
#	- Similar to the debianutils' "which" but also walks through typical OS binaries paths.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	wp__Program="$1"
	wp__CurDir=''
	wp__Value=''
	
	wp__Value="$(command -v "$wp__Program" 2>/dev/null)"
	if [ "$wp__Value" = "" ] && [ "$(printf '%s' "$wp__Program" | tr '/' ' ')" = "$wp__Program"  ] ; then
		# Walk through typical path
		if [ $(id -u) -eq 0 ] ; then
			unset IFS ; for wp__CurDir in /root/.local/bin /usr/local/bin /usr/local/sbin /usr/bin /usr/sbin /bin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		else
			unset IFS ; for wp__CurDir in ~/.local/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		fi
	fi
	if [ "$wp__Value" != "" ] ; then
		# Some scenarios "command" returns directories as executables.
		if [ -d "$wp__Value" ] && [ "$(printf '%s' "$wp__Value" | tr '/' ' ')" != "$wp__Value"  ] ; then wp__Value='' ; fi
	fi
	if [ "$wp__Value" != "" ] ; then
		printf '%s\n' "$wp__Value"
	fi
}

DfOutputValue ()
# Description: Parser for DF command, that emulates --output option for old versions without support for it. Only returns (stdout) the one asked output value.
# ToDo:
#	- Implement "no Path" to list all volumes for specified OutputField
#	- Implement alternative method to get fstype when using BusyBox (eg. in Maemo)
#	- Try to not depend on bc
# Notes:
#	- If no third parameter is specified (-b -k -m -g), sizes are returned in bytes.
#	- As of df v8, some filesystems (eg: btrfs) don't report inodes information and returned value can be "-"
# Depends on functions: Is_Executable ReadlinkF
# Depends on software packages: perl-base, grep, bc, sed
{
	local OutputField="$1"
	local Path="$2"
	local Unit="$3"
	local BlockSize=1
	local BlockFactor=1
	local LineInodes=''
	local LineSpace=''
	local WordsInodes=''
	local WordsSpace=''
	local ConvertSize=0
	local LastStatus=0
	local Posix='P'
	local PrintType='T'
	local Value=''
	
	case "$Unit" in
		"-b" ) BlockSize=1 ;;
		"-k" ) BlockSize=1024 ;;
		"-m" ) BlockSize=$((1024*1024)) ;;
		"-g" ) BlockSize=$((1024*1024*1024)) ;;
		* ) BlockSize=1 ;;
	esac
	df "$Path" >/dev/null 2>&1
	LastStatus=$?
	if [ $LastStatus -ne 0 ] ; then
		# /dev/simfs (VPS) doesn't work; let's detect mountpoint
		Path="$(df | grep -e "^${Path} " | tr -s '%' '\n' | sed -e 's|^ ||g' | tail -n 1)"
	fi
	if [ "$(df --help 2>&1 | grep -e '--output')" != "" ] ; then
		df "--output=${OutputField}" "$Path" >/dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			Value="$(env LANG=en df --block-size=$BlockSize "--output=${OutputField}" "$Path" 2>/dev/null | tail -n 1)"
		fi
	else
		df -P "$Path" >/dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -ne 0 ] ; then
			Posix=''
			df "$Path" >/dev/null 2>&1
			LastStatus=$?
		fi
		if [ $LastStatus -eq 0 ] ; then
			# Note: Different GNU distributions have complete different alignment policy.
			#       Most of fields is better to get counting words backwards since the common % symbol.
			#	This method is incompatible with devices with % symbol in path.
			df -T "$Path" >/dev/null 2>&1
			LastStatus=$?
			if [ $LastStatus -ne 0 ] ; then
				PrintType=''
			fi
			df -i "$Path" >/dev/null 2>&1
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then
				LineInodes="$(env LANG=en df -${Posix}${PrintType}i "$Path" | tail -n 1)"
			fi
			if [ "${Posix}${PrintType}" != "" ] ; then
				LineSpace="$(env LANG=en df -${Posix}${PrintType} --block-size=$BlockSize "$Path" 2>/dev/null)"
			else
				LineSpace="$(env LANG=en df --block-size=$BlockSize "$Path" 2>/dev/null)"
			fi
			LastStatus=$?
			if [ $LastStatus -ne 0 ] ; then
				if [ "${Posix}${PrintType}" != "" ] ; then
					LineSpace="$(env LANG=en df -${Posix}${PrintType} -k "$Path")"
				else
					LineSpace="$(env LANG=en df -k "$Path")"
				fi
				BlockFactor=1024
				if [ $BlockSize -ge $BlockFactor ] ; then
					BlockFactor=$((BlockSize / BlockFactor))
				else
					BlockFactor=-1024
				fi
			fi
			LineSpace="$(printf '%s\n' "$LineSpace" | tail -n 1)"
			if [ "$(printf '%s' "$LineInodes" | grep -e '%')" = "" ] ; then
				# Optical media
				LineInodes="$(printf '%s' "$LineInodes" | sed -e 's| - | 100% |g')"
			fi
			WordsInodes="$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 1 -d '%' | tr -s ' ' '\n')"
			WordsSpace="$(echo TrimAndSingle $LineSpace | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 1 -d '%' | tr -s ' ' '\n')"
			case "$OutputField" in
				"source" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 5 | head -n 1)	# Get fstype to use as field splitter
					Value="$(printf '%s' "$LineInodes" | perl -pe "s| ${Value} |\n|g" | head -n 1)"
					;;
				"fstype" )
					if [ "$PrintType" != "" ] ; then
						Value=$(printf '%s' "$WordsInodes" | tail -n 5 | head -n 1)
					fi
					;;
				"itotal" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 4 | head -n 1)
					;;
				"iused" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 3 | head -n 1)
					;;
				"iavail" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 2 | head -n 1)
					;;
				"ipcent" )
					Value=$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -e '%' | head -n 1)
					;;
				"size" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 4 | head -n 1)
					ConvertSize=1
					;;
				"used" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 3 | head -n 1)
					ConvertSize=1
					;;
				"avail" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 2 | head -n 1)
					ConvertSize=1
					;;
				"pcent" )
					Value=$(echo TrimAndSingle $LineSpace | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -e '%' | head -n 1)
					;;
				"target" )
					Value=$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 2- -d '%')
					;;
			esac
		fi
		if [ $ConvertSize -eq 1 ] && [ $BlockFactor -ne 1 ] ; then
			if Is_Executable bc ; then
				if [ $BlockFactor -ge 0 ] ; then
					Value=$(printf '%s\n' "$Value / $BlockFactor" | bc)
				else
					Value=$(printf '%s\n' "$Value * $BlockFactor" | bc)
				fi
			else
				if [ $BlockFactor -ge 0 ] ; then
					Value=$((Value / BlockFactor))
				else
					Value=$((Value * BlockFactor))
				fi
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then
		if [ "$OutputField" = "source" ] && [ "$(printf '%s' "$Value" | grep -e '^/')" != "" ] && [ "$(ReadlinkF "$Value" 2>/dev/null)" != "" ] ; then
			Value="$(ReadlinkF "$Value")"
		fi
		# Trim:
		Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"
		printf '%s\n' "$Value"
	fi
}

StatFormatValue ()
# Description: Parser for STAT command, that emulates --format option for old versions without support for it. Only returns (stdout) the one asked output value.
# Notes:
#	--file-system is not used.
# ToDo:
#	- Try to not depend on bc
# Depends on functions: DfOutputValue Lowercase
# Depends on software packages: grep, sed, bc
{
	local FormatField="$1"
	local Path="$2"
	local Value=''
	
	if [ -e "$Path" ] ; then
		if [ "$(stat --help 2>&1 | grep -e '--format')" != "" ] ; then
			stat "--format=${FormatField}" "$Path"
		else
			FormatField="$(printf '%s\n' "$FormatField" | sed -e 's|^%||g')"
			case "$FormatField" in
				"a" )	# access rights in octal
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:.*(.*/.*)' | cut -f 2 -d '(' | cut -f 1 -d '/')"
					;;
				"A" )	# access rights in human readable form
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:.*(.*/.*)' | cut -f 1 -d ')' | cut -f 2 -d '/')"
					;;
				"b" )	# number of blocks allocated (see %B)
					Value="$(env LANG=en stat "$Path" | grep -e 'Blocks:' | sed -e 's|.*Blocks:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"B" )	# the size in bytes of each block reported by %b
					Value=''
					;;
				"C" )	# SELinux security context string
					Value='?'
					;;
				"d" )	# device number in decimal
					Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					Value="$(printf '%s\n' "$Value" | sed -e 's|.*/||g' | sed -e 's|d$||g')"
					;;
				"D" )	# device number in hex
					Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					Value="$(printf '%s\n' "$Value" | sed -e 's|/.*||g')"	# Only when stat presents it
					Value="$(printf '%s\n' "$Value" | grep -ie 'h$' | sed -e 's|h$||g')"
					if [ "$Value" = "" ] ; then
						Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#						Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
						Value="$(printf '%s\n' "$Value" | sed -e 's|.*/||g' | sed -e 's|d$||g')"
						if [ "$Value" != "" ] ; then
							Value="$(printf '%s\n' "obase=16; $Value" | bc 2>/dev/null)"
							if [ "$Value" != "" ] ; then Value="$(Lowercase "$Value")" ; fi
						fi
					fi
					;;
				"f" )	# raw mode in hex
					Value=''
					;;
				"F" )	# file type
					Value="$(env LANG=en stat "$Path" | grep -e 'Blocks:' | sed -e 's|.*:||g')"
					Value=$(NextWords () { shift ; printf '%s' "$*"; }; NextWords $Value)
					;;
				"g" )	# group ID of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:.*(.*/.*)' | perl -pe 's|.*Uid:||gi' | cut -f 2 -d '(' | cut -f 1 -d '/' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"G" )	# group name of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:.*(.*/.*)' | perl -pe 's|.*Uid:||gi' | cut -f 1 -d ')' | cut -f 2 -d '/')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"h" )	# number of hard links
					Value="$(env LANG=en stat "$Path" | grep -ie 'Links:..*' | perl -pe 's|.*Links:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"i" )	# inode number
					Value="$(env LANG=en stat "$Path" | grep -ie 'Inode:..*' | perl -pe 's|.*Inode:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"m" )	# mount point
					Value="$(DfOutputValue target "$Path" | grep -ve '^/dev$')"
					;;
				"n" )	# file name
					Value="$(env LANG=en stat "$Path" | grep -ie 'File:.*"' | cut -f 2 -d '"')"
					;;
				"N" )	# quoted file name with dereference if symbolic link
					Value="'$(env LANG=en stat "$Path" | grep -ie 'File:.*"' | cut -f 2 -d '"')'"
					if [ "$(ls -l "$Path" | grep -e "$Path -> ")" != "" ] ; then
						Value="${Value} -> '$(ls -l "$Path" | sed -e "s|.*${Path} -> ||g")'"	#'
					fi
					;;
				"o" )	# optimal I/O transfer size hint
					Value=''
					;;
				"s" )	# total size, in bytes
					Value="$(env LANG=en stat "$Path" | grep -ie 'Size:..*' | perl -pe 's|.*Size:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"t" )	# major device type in hex
					Value=''
					;;
				"T" )	# minor device type in hex
					Value=''
					;;
				"u" )	# user ID of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:..*' | perl -pe 's|.*Uid:||gi' | tr -s '\t' ' ')"
					Value="$(printf '%s\n' "$Value" | cut -f 2 -d '(' | cut -f 1 -d '/' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"U" )	# user name of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:..*' | perl -pe 's|.*Uid:||gi')"
					Value="$(printf '%s\n' "$Value" | cut -f 1 -d ')' | cut -f 2 -d '/')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"w" )	# time of file birth, human-readable; - if unknown
					Value="$(env LANG=en stat "$Path" | grep -ie 'Birth:..*' | perl -pe 's|.*Birth:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ "$Value" = "" ] ; then Value="-" ; fi
					;;
				"W" )	# time of file birth, seconds since Epoch; 0 if unknown
					Value="$(env LANG=en stat "$Path" | grep -ie 'Birth:..*' | perl -pe 's|.*Birth:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"x" )	# time of last access, human-readable [file content read, except if 'noatime' in effect]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:..*'  | grep -ive 'Access:.*(.*/.*)' | perl -pe 's|.*Access:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"X" )	# time of last access, seconds since Epoch [file content read, except if 'noatime' in effect]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:..*'  | grep -ive 'Access:.*(.*/.*)' | perl -pe 's|.*Access:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"y" )	# time of last modification, human-readable [file content changes]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"Y" )	# time of last modification, seconds since Epoch [file content changes]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"z" )	# time of last change, human-readable [both file content changes and metadata only changes: owner, time, permissions, etc.]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Change:..*' | perl -pe 's|.*Change:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"Z" )	# time of last change, seconds since Epoch [both file content changes and metadata only changes: owner, time, permissions, etc.]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
			esac
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

MkdirPP ()
# Syntax as a sentence: MkdirPP $NewPath [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# Description: Same job as "mkdir -p" but setting specified owner and permissions to each new subdirectory
# Expected parameters:
#	$1	Directory path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown. If no specified, will take setting from existing tree
#	$3	(optional) Permissions specification as allowed by chmod. If no specified, will take setting from existing tree
#	$4	(optional) Additional permissions specification to set (useful for g+s not being applied by MkfilePP)
# Notes:
#	- Owners/permissions are only set when creating subdirectories; not when they already exist.
# Depends on functions: StatFormatValue
# Depends on software packages: (none)
{
	local NewPath="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local CurrentElement=''
	local PreviousDir=''
	local LastStatus=0
	local StatusCode=0
	
	PreviousDir="$(pwd)"
	IFS="$(printf "/")" ; for CurrentElement in $NewPath ; do unset IFS
		if [ $StatusCode -eq 0 ] ; then
			if [ "$CurrentElement" = "" ] ; then
				cd /
			else
				if [ -e "$CurrentElement" ] ; then
					cd "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$PermissionsForNewElements" = "" ] || [ "$PermissionsForNewElements" = "." ] ; then
						PermissionsForNewElements="$(StatFormatValue %a .)"
					fi
					if [ "$OwningForNewElements" = "" ] || [ "$OwningForNewElements" = "." ] ; then
						OwningForNewElements="$(StatFormatValue %U .)"
						OwningForNewElements="${OwningForNewElements}:$(StatFormatValue %G .)"
					fi
					mkdir "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
							chown "$OwningForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
							chmod "$PermissionsForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] ; then
							chmod "$MorePermissions" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						cd "$CurrentElement"
					fi
				fi
			fi
		fi
	done
	cd "$PreviousDir"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

MkfilePP ()
# Syntax as a sentence: MkfilePP $NewFile [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# Description: Creates a file only if not exists, and then sets it specified owner and permissions
# Expected parameters:
#	$1	Path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown. If no specified, will take setting from existing tree
#	$3	(optional) Permissions specification as allowed by chmod. If no specified, will take setting from existing tree
#	$4	(optional) Additional permissions specification to set (useful for g+s only being to directories)
# Notes:
#	- Owners/permissions are only set when creating the file.
#	- If directory path does not exist, it's created with MkdirPP using same parameters.
# Depends on functions: MkdirPP Dirname StatFormatValue
# Depends on software packages: (none)
{
	local NewFile="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local TheDirname=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -e "$NewFile" ] && [ ! -L "$NewFile" ] ; then
		TheDirname="$(Dirname "$NewFile")"
		MkdirPP "$TheDirname" "$OwningForNewElements" "$PermissionsForNewElements" "$MorePermissions" 2>/dev/null
		touch "$NewFile"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$OwningForNewElements" = "" ] || [ "$OwningForNewElements" = "." ] ; then
			OwningForNewElements="$(StatFormatValue %U "$TheDirname")"
			OwningForNewElements="${OwningForNewElements}:$(StatFormatValue %G "$TheDirname")"
		fi
		if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
			chown "$OwningForNewElements" "$NewFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$PermissionsForNewElements" = "" ] || [ "$PermissionsForNewElements" = "." ] ; then
			PermissionsForNewElements="$(StatFormatValue %a "$TheDirname")"
		fi
		if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
			chmod "$PermissionsForNewElements" "$NewFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] && [ "$MorePermissions" != "g+s" ] ; then
			chmod "$MorePermissions" "$NewFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

RespostaLletra ()
# Sintaxi com a funcio: $(RespostaLletra $JaEspecificada)
# Descripció: Reads a keyboard-typed answer (ENTER must be pressed) and returns first answer letter (lower case if possible - not in Maemo)
# Paràmetres esperats:
#	$1	(opcional) Tractar aquesta cadena en comptes de demanar-la de teclat.
# Depends on functions: Lowercase
# Depends on software packages: (none)
{
	JaEspecificada="$1"
	if [ "$JaEspecificada" = "" ] ; then read JaEspecificada ; fi
	JaEspecificada="$(printf '%s' "$JaEspecificada" | cut -c 1)"
	JaEspecificada="$(Lowercase "$JaEspecificada")"
	if [ "$JaEspecificada" != "" ] ; then printf '%s\n' "$JaEspecificada" ; fi
}

NrEncuentrosTexto ()
# Sintaxis como función: $(NrEncuentrosTexto "$Texto" "$ExpresionBuscada")
# Notas:
#	- Se pueden buscar saltos de línea (\n)
# Depends on functions: (none)
# Depends on software packages: sed
{
	local Texto="$1"
	local ExpresionBuscada="$2"
	local SlashMask=''
	local Valor=0
	
	if [ "$ExpresionBuscada" != "" ] ; then
		if [ "$Texto" != "" ] ; then
			SlashMask="NET747082025"
			# La máscara de salto de línea es constante (tmpbHckF2LMB4tmp_Wz2coasdb3tmpX7LuyGTvrW) porque sed debe interpretar $! sin interpretación de Bash.
			Texto="$(echo "$Texto" | sed -e "s/\//${SlashMask}/g" | sed ':a;N;$!ba;s/\n/tmpbHckF2LMB4tmp_Wz2coasdb3tmpX7LuyGTvrW/g')"
			ExpresionBuscada="$(echo "$ExpresionBuscada" | sed -e "s/\//${SlashMask}/g" | sed ':a;N;$!ba;s/\n/tmpbHckF2LMB4tmp_Wz2coasdb3tmpX7LuyGTvrW/g')"
			Texto="$(echo "$Texto" | sed -re "s/${ExpresionBuscada}/RELLENOLINEA\nRELLENOLINEA/g")"

			Valor=$(echo "$Texto" | wc -l)
			Valor=$((Valor - 1))
		else
			Valor=0
		fi
		echo $Valor
	fi
}

ClausulaEnFichero ()
# Sintaxis como funcion: "$(ClausulaEnFichero "$RutaFichero" "$IndicadorEtiqueta" "$ValorEtiqueta" "$SApertura" "$SCierre" "$SAperturaRegexp" "$SCierreRegexp")"
# Descripcion: Muestra del fichero una clausula con etiqueta y delimitadores, que pueden ser anidados.
# Ejemplo1 de fichero:
# micasa {
# 	color=0
# 	habitacion {
# 		silla=1
# 	}
# }
# Pidiendo $fichero "micasa" "" "{" "}" devolvera toda la clausula micasa{{...}} (si esta repetida solo devuelve la primera)
# Ejemplo2 de fichero:
# casa "grande" {
# 	color=0
# 	habitacion {
# 		silla=1
# 	}
# }
# Pidiendo $fichero "casa" "grande" "{" "}" devolvera toda la clausula casa grande{{...}} (si esta repetida solo devuelve la primera)
# Notas:
#	- La funcion resulta bastante lenta si se necesita llamar repetidamente en otros procesos.
#	- Si se omite el texto de cierre ($SCierre), se asume que la cláusula termina antes de aparecer otra $SApertura
#	- $SCierre y $SApertura no preveen símbolos de inicio (^) o fin ($) de línea, según la función NrEncuentrosTexto()
#	- $SAperturaRegexp y $SCierreRegexp son opcionales. Si se usan, reemplazan a $SCierre y $SApertura
# Depends on functions: NrEncuentrosTexto
# Depends on software packages: grep, sed
{
	# Variables locales
	local RutaFichero="$1"
	local IndicadorEtiqueta="$2"
	local ValorEtiqueta="$3"
	local SApertura="$4"
	local SCierre="$5"
	local SAperturaRegexp="$6"
	local SCierreRegexp="$7"
	local Abierto=''
	local Cerrado=''
	local CuanAbierto=0
	local Seleccion=''
	local LineasClausula=''
	local LineaActual=''
	local Valor=''
	local BlankLineMask=''
	local ValorEtiquetaRegexp=''
	local MostrarLineaActual=''
	local NrLiniaClausula=1
	
	if [ -f "$RutaFichero" ] && [ "${SApertura}${SAperturaRegexp}" != "" ] ; then
		if [ "$SAperturaRegexp" = "" ] ; then
			SAperturaRegexp="$(printf '%s\n' "$SApertura" | sed -e 's/\[/\\[/g' | sed -e 's/\]/\\]/g' | sed -e 's/[\{]/\\{/g' | sed -e 's/\}/\\}/g')"
		fi
		if [ "$SCierreRegexp" = "" ] ; then
			SCierreRegexp="$(printf '%s\n' "$SCierre" | sed -e 's/\[/\\[/g' | sed -e 's/\]/\\]/g' | sed -e 's/[\{]/\\{/g' | sed -e 's/\}/\\}/g')"
		fi
		Comprobacion="$(cat "$RutaFichero" | tr -s '\t' ' ' | tr -s ' ' | cut -f 1 -d '#' | sed -e 's|^//.*||g')"
		if [ "$ValorEtiqueta" != "" ] ; then
			ValorEtiquetaRegexp="$(printf '%s\n' "$ValorEtiqueta" | sed -e 's/(/\\(/g' | sed -e 's/)/\\)/g')"
			Comprobacion="$(printf '%s\n' "$Comprobacion" | grep -e "${IndicadorEtiqueta}.*${ValorEtiqueta}")"
		else
			Comprobacion="$(printf '%s\n' "$Comprobacion" | grep -e "$IndicadorEtiqueta")"
		fi
		if [ "$Comprobacion" != "" ] ; then
			if [ "$ValorEtiqueta" != "" ] ; then
				Seleccion="$(cat "$RutaFichero" | grep --after-context=9999 -E "(^${IndicadorEtiqueta}|([[:blank:]]+)${IndicadorEtiqueta})([[:blank:]]+).*${ValorEtiquetaRegexp}")"
			else
#				Seleccion="$(cat "$RutaFichero" | grep --after-context=9999 -E "(^${IndicadorEtiqueta}|([[:blank:]]+)${IndicadorEtiqueta})(([[:blank:]]+)${SApertura}|$)")"
				Seleccion="$(cat "$RutaFichero" | grep --after-context=9999 -E "(^${IndicadorEtiqueta}|[[:blank:]]+${IndicadorEtiqueta})([[:blank:]]+${SAperturaRegexp}|$)")"
			fi
#			BlankLineMask="$(printf '%s\n' $(basename $(mktemp --dry-run))$(basename $(mktemp --dry-run))$(basename $(mktemp --dry-run)) | tr -s '.' '_')"	Problems with a path begun with "-" in old basename versions
			BlankLineMask="CEFtmpbHckF2LMB4tmpWz2coasdb3tmpX7LuyGTvrW"
			LineasClausula="$(printf '%s\n' "$Seleccion" | sed -e "s/^$/${BlankLineMask}/g")"
			IFS="$(printf '\n\b')" ; for LineaActual in $LineasClausula ; do unset IFS
				if [ "$LineaActual" = "$BlankLineMask" ] ; then LineaActual="" ; fi
				if [ "$Cerrado" != "1" ] ; then
					MostrarLineaActual="1"
					CuanAbierto=$((CuanAbierto + $(NrEncuentrosTexto "$LineaActual" "$SAperturaRegexp")))
					if [ $CuanAbierto -gt 0 ] ; then
						Abierto="1"
					fi
					if [ "$Abierto" = "1" ] ; then
						if [ "$SCierre" != "" ] ; then
							CuanAbierto=$((CuanAbierto - $(NrEncuentrosTexto "$LineaActual" "$SCierreRegexp")))
							if [ $CuanAbierto -le 0 ] ; then
								Cerrado="1"
							fi
						else
							if [ $CuanAbierto -gt 1 ] ; then
								Cerrado="1"
								if [ $NrLiniaClausula -gt 1 ] ; then
									MostrarLineaActual="0"
								fi
							fi
						fi
					fi
					if [ "$MostrarLineaActual" = "1" ] ; then
						printf '%s\n' "$LineaActual"
						NrLiniaClausula=$((NrLiniaClausula + 1))
					fi
				fi
			done
		fi
	fi
}

HttpGetContent ()
# Syntax as function: $(HttpGetContent "$Url" [DNS] [BindAddress])
# Description: Returns (stdout) retrieved content from internet
# Expected parameters:
#	$1	URI to get the page from
#	$2	(optional or empty) Set the list of DNS servers to be used instead of the system default.  The list of IP addresses should be separated with commas.
#	$3	(optional) Local IP address to bind to when making client TCP/IP connections.
#		This option can be useful if your machine is bound to multiple IPs.

# Notes:
#	- Logging and error messages are returned to stderr
# Depends on functions: Is_Executable
# Depends on software packages: wget|curl
{
	local Url="$1"
	local OverrideDNS="$2"
	local BindAddress="$3"
	local StatusCode=0
	local WgetBind=''
	local CurlBind=''
	
	if [ "$BindAddress" != "" ] ; then
		WgetBind="--bind-address=$BindAddress"
		CurlBind="--interface $BindAddress"
	fi
	if Is_Executable wget ; then
		if [ "$(wget --help 2>/dev/null | grep -e '--no-check-certificate')" != "" ] ; then
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget $WgetBind --no-check-certificate \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate "--dns-servers=${OverrideDNS}" -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 2 ] ; then
					# unrecognized option '--dns-servers=... (because not libcares build)
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget $WgetBind --no-check-certificate -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				else
					if [ $StatusCode -eq 4 ] ; then
						# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
						if Is_Executable curl ; then
							printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					fi
				fi
			fi
		else
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget $WgetBind \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind "--dns-servers=${OverrideDNS}" -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 2 ] ; then
					# unrecognized option '--dns-servers=... (because not libcares build)
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget $WgetBind -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				else
					if [ $StatusCode -eq 4 ] ; then
						# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
						if Is_Executable curl ; then
							printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget $WgetBind -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					fi
				fi
			fi
		fi
	else
		if Is_Executable curl ; then
			# Warning: curl interprets some symbols as [] {}
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# A requested feature, protocol or option was not found built-in in this libcurl due to a build-time decision.
					printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
					curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
					StatusCode=$?
				fi
			else
				printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
				StatusCode=$?
			fi
		else
			printf '%s\n' "${sERROR}E: Neither wget nor curl program are available to obtain content from internet.${fRESET}" 1>&2
			StatusCode=52
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

EsIP ()
# Sintaxis como función: $(EsIP $Address)
# Descripción: Devuelve (stdout) "1" en caso de que el dato proporcionado valga como dirección IPv4
# Notas:
#	- Direcciones validas desde 0.0.0.0 hasta 255.255.255.255
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local Address="$1"
#	local NumeroActual=''
	local Address_b1=''
	local Address_b2=''
	local Address_b3=''
	local Address_b4=''
	local Value=0
	
	# Expresión encontrada por la web, pero que no funciona bien con todos los extremos numéricos.
	# Puede que sea un problema de desbordamiento de expresiones cuando hay pocos recursos.
	# echo "$Value" | grep -E "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
	# PENDIENTE: Puede que sea más sencillo con la técnica de Is_IntegerNr()
	
	if [ "$Address" != "" ] && [ "$(printf '%s' "$Address" | grep -e ' ')" = "" ] ; then
#		Value="$(printf '%s' "$Address" | tr -s '.' ' ')"
#		NumeroActual="$(UnaPalabra () { printf '%s' $1; }; UnaPalabra $Value)"
#		NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#		if [ "$NumeroActual" != "" ] ; then
#			NumeroActual="$(UnaPalabra () { printf '%s' $2; }; UnaPalabra $Value)"
#			NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#			if [ "$NumeroActual" != "" ] ; then
#				NumeroActual="$(UnaPalabra () { printf '%s' $3; }; UnaPalabra $Value)"
#				NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				if [ "$NumeroActual" != "" ] ; then
#					NumeroActual="$(UnaPalabra () { printf '%s' $4; }; UnaPalabra $Value)"
#					NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				fi
#			fi
#		fi
		Address_b1="$(printf '%s\n' "$Address" | cut -f 1 -d '.')"
		Address_b2="$(printf '%s\n' "$Address" | cut -sf 2 -d '.')"
		Address_b3="$(printf '%s\n' "$Address" | cut -sf 3 -d '.')"
		Address_b4="$(printf '%s\n' "$Address" | cut -sf 4 -d '.')"
		if Is_IntegerNr $Address_b1 && Is_IntegerNr $Address_b2 && Is_IntegerNr $Address_b3 && Is_IntegerNr $Address_b4 ; then
			if [ $Address_b1 -ge 0 ] && [ $Address_b2 -ge 0 ] && [ $Address_b3 -ge 0 ] && [ $Address_b4 -ge 0 ] ; then
				if [ $Address_b1 -le 255 ] && [ $Address_b2 -le 255 ] && [ $Address_b3 -le 255 ] && [ $Address_b4 -le 255 ] ; then
					Value=1
				fi
			fi
		fi
	fi
	printf '%s\n' "$Value"
}

WorkingInternetDNS ()
# Syntax as a function: "$(WorkingInternetDNS [AskedResultsNr [Proposals]])"
# Description: Returns IPv4 address of a working public domain name service. Space-separated list of hosts.
# Expected parameters:
#	$1	(optional or empty) Number of desired addresses if possible. Default is 1.
#	$2	(optional) IPv4 addresses to prioritize if they work (space separated).
# Notes:
#	- If does not resolve, returns empty "" string
# TO DO:
#	- Query opennic.org to get recommended servers
#	- Allow proposing first hosts candidates: Direct IP addresses or an URL where to get candidates from.
#	- Allow restrict type, such as only OpenNIC or only-proposed
# Depends on functions: Is_IntegerNr Is_Executable EsIP
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local AskedResultsNr=$1
	local Proposals="$2"
	local OpenNicServers='94.247.43.254 194.36.144.87 130.61.69.123 89.163.140.67 195.10.195.195 144.24.181.253 185.84.81.194 91.217.137.37 151.80.222.79'
	local DominationServers='9.9.9.9 1.1.1.1 8.8.8.8 8.8.4.4'
	local PossibleServers="$OpenNicServers $DominationServers"
	local CurServer=''
	local CurPointedIP=''
	local Netname='www.w3.org'
	local CurValue=''
	local ValuesNr=0
	local Value=''
	
	if ! Is_IntegerNr "$AskedResultsNr" ; then AskedResultsNr=1 ; fi
	if [ "$Proposals" != "" ] && [ "$Proposals" != "." ] ; then
		PossibleServers="$Proposals $PossibleServers"
	fi
	unset IFS ; for CurServer in $PossibleServers ; do
		if [ $ValuesNr -lt $AskedResultsNr ] ; then
			CurPointedIP=''
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable host ; then
				CurPointedIP="$(host -W 3 "$Netname" "$CurServer" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable dig ; then
				CurPointedIP="$(dig +short +timeout=3 +tries=1 "@${CurServer}" "$Netname")"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable dnsget ; then
				CurPointedIP="$(dnsget -q -n "$CurServer" "$Netname" | grep -e '..*\...*\...*\...*')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable nslookup ; then
				CurPointedIP="$(nslookup -retry=1 -timeout=3 "$Netname" "$CurServer" | grep -ive 'NXDOMAIN' -ive "${CurServer}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
				CurPointedIP="$(printf '%s\n' "$CurPointedIP" | cut -f 2 -d ':' | sed -e 's|^ ||g' | sed -e 's| $||g')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" = "1" ] ; then
				if [ "$Value" != "" ] ; then Value="$Value " ; fi
				Value="${Value}${CurServer}"
				ValuesNr=$((ValuesNr + 1))
			fi
		fi
	done
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

NetnameDestinations ()
# Syntax as a function: "$(NetnameDestinations $Netname $NetDNS [Timeout])"
# Description: Returns IPv4 A resolution of specified FQDN, one address per line.
# Expected parameters:
#	$1	Valid FQDN for DNS server
#	$2	(optional or empty) DNS service to query.
#		If not specified, an Internet one will be tried.
#		If minus "-" character is specified instead, no DNS will be selected (host's default nameserver will play)
#	$3	(optional) Timeout in seconds, when available
# Notes:
#	- If does not resolve, returns empty "" string
# To do:
#	- Allow to specify MX, CNAME and other types
# Depends on functions: Is_Executable Is_IntegerNr EsIP WorkingInternetDNS
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local Netname="$1"
	local NetDNS="$2"
	local Timeout="$3"
	local TimeoutParm=''
#	local PointedIP=''
	local LastStatus=0
	local StatusCode=0
	local Value=''
	
	if [ "$Netname" != "" ] ; then
		if [ "$NetDNS" = "" ] || [ "$NetDNS" = "." ] ; then NetDNS="$(WorkingInternetDNS 1)" ; fi
		if ! Is_IntegerNr "$Timeout" ; then Timeout='' ; fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable host ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable host ; then
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-W $Timeout" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(host $TimeoutParm "$Netname" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
			else
				Value="$(host $TimeoutParm "$Netname" "$NetDNS" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
			fi
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable dig ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable dig ; then
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="+timeout=$Timeout +tries=1" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(dig +short $TimeoutParm "$Netname")"
			else
				Value="$(dig +short $TimeoutParm "@${NetDNS}" "$Netname")"
			fi
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable dnsget ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable dnsget ; then
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-o timeout:${Timeout},attempts:1" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(dnsget -q $TimeoutParm "$Netname" | grep -e '..*\...*\...*\...*')"
			else
				Value="$(dnsget -q $TimeoutParm -n "$NetDNS" "$Netname" | grep -e '..*\...*\...*\...*')"
			fi
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable nslookup ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable nslookup ; then
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-timeout=$Timeout -retry=0" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(nslookup $TimeoutParm "$Netname" | grep -ive 'NXDOMAIN' -ive "${NetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
			else
				Value="$(nslookup $TimeoutParm "$Netname" "$NetDNS" | grep -ive 'NXDOMAIN' -ive "${NetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
			fi
			Value="$(printf '%s\n' "$Value" | cut -f 2 -d ':' | sed -e 's|^ ||g' | sed -e 's| $||g')"
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
	else
		printf '%s\n' "${sERROR}E: Netname not specified for function NetnameDestinations${ParO}${ParC}.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

LlistarIPpropies ()
# Sintaxi: LlistarIPpropies $LimitNr "$NIC" "$ExcludedAddresses"
# Descripcio: Returns (stdout) active IP addresses on system (one per line)
# Parametres esperats:
#	$1	Numero màxim d'adreces a llistar. Especifiqueu 0 per a totes.
#	$2	Nom de dispositiu de xarxa a analitzar. "eth" es valid per tots els "eth.."; "." o "*" serveix per a tots
#	$3	(opcional) Patro d'adreces a excloure de la llista. Per exemple "^127\."
# Notes:
#	- Only detects 1 IP per device. To get everyone use AllIPv4()
#	- Tracta adreces IPv4
# Depends on functions: WhereProgram Is_IntegerNr EsIP
# Depends on software packages: grep (>= 2.0), sed, ip/iproute2 (>= 1.0.0) | ip/iproute (>= 20020101) | netbase (>= 3.0) | busybox-symlinks-net-tools (>= 1)
{
	# Variables locales
	local LimitNr=$1
	local NIC="$2"
	local ExcludedAddresses="$3"
	local CurIPs=''
	local CurIP=''
	local FoundNr=0
	local IfConfig=''
	local IpRoute=''
	local CurDevice=''
	local NICs=''
	local LastStatus=0
	local StatusCode=0
	
	if ! Is_IntegerNr "$LimitNr" ; then LimitNr=0 ; fi
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
		if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi
		NICs="$($IpRoute address show | grep -ve '^ ' | grep -e ': .*: <' | cut -f 2 -d ':' | sed -e 's|@if..*||g')"
		IFS="$(printf '\n\b')" ; for CurDevice in $NICs ; do unset IFS
			CurDevice="$(echo TrimAndSingle $CurDevice | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$(printf '%s\n' "$CurDevice" | grep -e "^${NIC}")" != "" ] ; then
				CurIPs="$($IpRoute address show dev "$CurDevice")"
				if [ "$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')" != "" ] ; then
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')"
				else
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* ')"
				fi
				CurIPs="$(printf '%s' "$CurIPs" | tr -s '\t' ' ' | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | cut -f 1 -d '/')"
				CurIPs="$(printf '%s' "$CurIPs" | head -n 1)"  # LIMIT TO 1 ADDRESS PER NIC, to be consistent with ifconfig alternative
				unset IFS ; for CurIP in $CurIPs ; do
#					if [ "$(EsIP "$CurIP")" != "1" ] ; then
#						CurIP="$($IpRoute address show dev "$CurDevice" | grep -e 'inet ..*\...*\...*\...*/..* ' | head -n 1 | tr -s '\t' ' ')"
#						CurIP="$(echo TrimAndSingle $CurIP | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2 -d ' ' | cut -f 1 -d '/')"
#					fi
					if [ "$(EsIP "$CurIP")" = "1" ] ; then
						if [ "$ExcludedAddresses" = "" ] || [ "$(printf '%s' "$CurIP" | grep -ve "$ExcludedAddresses")" != "" ] ; then
							FoundNr=$((FoundNr + 1))
							if [ $FoundNr -le $LimitNr ] || [ $LimitNr -eq 0 ] ; then
								printf '%s\n' "$CurIP"
							fi
						fi
					fi
				done
			fi
		done
	else
		IfConfig="$(WhereProgram ifconfig)"
		if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
		if [ "$IfConfig" != "" ] ; then
			if [ "$LimitNr" = "" ] ; then LimitNr=0 ; fi
			if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi  # El comodi no serveix a ifconfig
			CurIPs="$(env LANG=en $IfConfig $NIC | grep -e 'inet ' -e ' ip ' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
			IFS="$(printf '\n\b')" ; for CurIP in $CurIPs ; do unset IFS
#				CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				if [ "$(EsIP "$CurIP")" != "1" ] ; then
					CurIP="$(printf '%s' "$CurIP" | tr -s ':' ' ')"
					CurIP="$(expr "$CurIP" : "[ ]*\(.*[^ ]\)[ ]*$")"  # Trim
					CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				fi
				if [ "$ExcludedAddresses" = "" ] || [ "$(printf '%s' "$CurIP" | grep -ve "$ExcludedAddresses")" != "" ] ; then
					FoundNr=$((FoundNr + 1))
					if [ $FoundNr -le $LimitNr ] || [ $LimitNr -eq 0 ] ; then
						printf '%s\n' "$CurIP"
					fi
				fi
			done
		else
			printf '%s\n' "${sERROR}E: Neither ipconfig/net-tools nor ip/iproute have been found.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DefaultNICs ()
# Sintaxis como funcion: $(DefaultNICs)
# Descripcion: Devuelve (stdout) los nombres de dispositivo que se usan para salir a internet/WAN, uno por línea
# Notas:
#	- Sólo se detecta un dispositivo si está habilitado (up).
#	- Function previously named DispositivoRedPredeterminado when only returned 1 NIC
#	- Alternativa con iproute2:
#	  ip r l | grep -ie '^default ' | head -n 1 | sed -e 's|.* dev ||g' | cut -f 1 -d ' '
# To do:
#	- Use alternative from iproute2: ss
# Depends on functions: WhereProgram Is_Executable
# Depends on software packages: grep, sed, net-tools|iproute
{
	local IPPrincipal=''
	local ListaDispositivos=''
	local IpRoute=''
	local List=''
	local OneValue=''
	
	if Is_Executable netstat ; then
		List="$(netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ')"
		IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
			OneValue="$(printf '%s\n' "$CurLine" | tr -s ' ' '\n' | tail -n 1)"
			if [ "$OneValue" != "" ] && [ "$(cat /proc/net/dev | tr -s ' ' | sed -e 's|^ ||g' | grep -e "^${OneValue}:")" != "" ] ; then printf '%s\n' "$OneValue" ; fi
		done
	fi
	if [ "$OneValue" = "" ] ; then
		IpRoute="$(WhereProgram ip 2)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			List="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default via .* dev ')"
			IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
				OneValue="$(printf '%s\n' "$CurLine" | tr -s ' ' '\n' | tail -n 1)"
				if [ "$OneValue" != "" ] && [ "$(ip link show "$OneValue" 2>/dev/null)" != "" ] ; then printf '%s\n' "$OneValue" ; fi
			done
			if [ "$OneValue" = "" ] ; then
				IPPrincipal="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -e '^default ' | grep -e ' src ' | head -n 1 | sed -re 's| |\n|g' | grep -ve '^$' | tail -n 1)"
				if [ "$IPPrincipal" != "" ] ; then
					Value="$($IpRoute address show | tr -s '\t' ' ' | tr -s ' ' | grep -e " ${IPPrincipal}/" | head -n 1 | sed -e 's| |\n|g' | grep -ve '^$' | sed -e 's|@if..*||g' | tail -n 1)"
				else
					Value="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -e '^default ' | grep -e ' dev ' | head -n 1 | sed -e 's| |\n|g' | grep -ve '^$' | head -n 5 | tail -n 1)"
				fi
				Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				if [ "$Value" != "" ] && [ "$(ip link show "$OneValue" 2>/dev/null)" != "" ] ; then printf '%s\n' "$Value" ; fi
			fi
		fi
	fi
}

PrimeraIPEquip ()
# Syntax as a function: $(PrimeraIPEquip)
# Dóna l'adreça IP de la sortida predeterminada a internet/WAN; si n'hi ha vàries en dóna la primera.
# Alternativa con iproute2 para obtener la NIC predeterminada:
#	NIC="$(ip r l | grep -ie '^default ' | head -n 1 | sed -e 's|.* dev ||g' | cut -f 1 -d ' ')"
# Alternativa con iproute2 para obtener la IP predeterminada de esa NIC:
#	ip r l | grep -e "..*\..*\...*\..* dev $NIC .* link src " | sed -e 's|.* src ||g' | cut -f 1 -d ' '
# Depends on functions: WhereProgram DefaultNICs LlistarIPpropies
# Depends on software packages: grep, sed, iproute
{
	local Devs=''
	local IpRoute=''
	local CurDev=''
	local Value=''
	
	IpRoute="$(WhereProgram ip)"
	if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
	if [ "$IpRoute" != "" ] ; then
		export LANG=en
		Value="$($IpRoute route 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default .* src ' | sed -e 's| |\n|g' | grep -ve '^$' | tail -n 1)"
	fi
	if [ "$Value" = "" ] ; then
		Devs="$(DefaultNICs)"
		IFS="$(printf '\n\b')" ; for CurDev in $Devs ; do unset IFS
			if [ "$Value" = "" ] ; then
				Value="$(LlistarIPpropies 1 "$CurDev" "^127\.")"
			fi
		done
		if [ "$Value" = "" ] ; then
			# Entonces sin excluir loopback
			IFS="$(printf '\n\b')" ; for CurDev in $Devs ; do unset IFS
				if [ "$Value" = "" ] ; then
					Value="$(LlistarIPpropies 1 "$CurDev" "^127\.")"
				fi
			done
		fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(LlistarIPpropies 1 . "^127\.")"
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

BitsMascara ()
# Syntax as a function: $(BitsMascara "$SubnetMask")
# Description: Converts old subnet mask into CIDR bits number.
# $(BitsMascara "255.255.255.0") => 24
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local SubnetMask="$1"
	local CurByte=''
	local Value=0
	
	if [ "$(printf '%s\n' "$SubnetMask" | grep -e '..*\...*\...*\...*')" != "" ] ; then
#		SubnetMask="$(printf '%s' "$SubnetMask" | tr -s '.' ' ')"
#		for CurByte in $(echo $SubnetMask) ; do
		IFS='.' ; for CurByte in $SubnetMask ; do unset IFS
			if Is_IntegerNr "$CurByte" && [ "$Value" != "" ] ; then 
				if [ $CurByte -ge 128 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 192 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 224 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 240 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 248 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 252 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 254 ] ; then Value=$((Value + 1)) ; fi
				if [ $CurByte -ge 255 ] ; then Value=$((Value + 1)) ; fi
			else
				Value=''
			fi
		done
		if [ "$Value" != "" ] ; then
			printf '%s\n' "$Value"
		else
			printf '%s\n' "${sERROR}E: Bad IPv4 numbers for BitsMascara()${fRESET}" 1>&2
		fi
	fi
}

MaskBits ()
# Syntax as a function: $(MaskBits $NrBits $TotalBytes)
# Example: $(MaskBits 24 4) => "255.255.255.0"
# Depends on functions:	(none)
# Depends on software packages: sed
{
	NrBits=$1
	TotalBytes=$2
	RemainTotalBits=$NrBits
	RemainTotalBytes=$TotalBytes
	ByteBits=0
	SpaceInByte=8
	CurrentHostsRest=256
	CurrentByte=0
	Value=''
	while [ $RemainTotalBits -gt 0 ] ; do
		if [ $SpaceInByte -gt 0 ] ; then
			CurrentHostsRest=$((CurrentHostsRest / 2))
			CurrentByte=$((CurrentByte + CurrentHostsRest))
			SpaceInByte=$((SpaceInByte - 1))
		else
			Value="${Value}.${CurrentByte}"
			CurrentHostsRest=$((256 / 2))
			CurrentByte=$CurrentHostsRest
			RemainTotalBytes=$((RemainTotalBytes - 1))
			SpaceInByte=$((8 - 1))
		fi
		RemainTotalBits=$((RemainTotalBits - 1))
	done
	if [ $CurrentByte -ne 0 ] ; then
		Value="${Value}.${CurrentByte}"
		RemainTotalBytes=$((RemainTotalBytes - 1))
	fi
	while [ $RemainTotalBytes -gt 0 ] ; do
		Value="${Value}.0"
		RemainTotalBytes=$((RemainTotalBytes - 1))
	done
	printf '%s\n' "$Value" | sed -e 's|^\.||g'
}

MascaraEquipIP ()
# Syntax as a function: $(MascaraEquipIP "$AdresaIP")
# Description: Dóna la màscara (com 255.255.255.0) que es correspon a la IP especificada de l'equip
# Nota: només funciona amb adreces IP actives a l'equip local
# Depends on functions: WhereProgram MaskBits
# Depends on software packages: grep, sed, iproute|net-tools
{
	local AdresaIP="$1"
	local Dades=''
	local DadaActual=''
	local IpRoute=''
	local IfConfig=''
	local IpRegexp=''
	local Value=''
	
	if [ "$AdresaIP" != "" ] ; then
		IpRegexp="$(printf '%s\n' "$AdresaIP" | sed -e 's|\.|\\.|g')"
		IpRoute="$(WhereProgram ip)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			Value="$("$IpRoute" addr 2>/dev/null | grep -e " ${IpRegexp}/" | sed -e "s|.* ${IpRegexp}/||g" | cut -f 2 -d '/' | cut -f 1 -d ' ')"
		fi
		if [ "$Value" != "" ] ; then
			Value="$(MaskBits "$Value" 4)"
		else
			IfConfig="$(WhereProgram ifconfig)"
			if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
			if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
			if [ "$IfConfig" != "" ] ; then
				Value="$(ifconfig -a 2>/dev/null | tr -s ':' ' ' | grep -e " ${IpRegexp} " | sed -e 's|.*mask||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
			fi
		fi
		if [ "$Value" = "255.255.255.255" ] ; then Value='' ; fi # Some venet in VPS return 32bits mask.
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IPv4ToDecimal ()
# Syntax as a function: $(IPv4ToDecimal "$IP")
# Description: Returns (stdout) the decimal numeric representation of IPv4 address (unsigned number)
# Depends on functions: EsIP
# Depends on software packages: bc
# Suggests software packages: awk/gawk
{
	local IP="$1"
	local Nr1=''
	local Nr2=''
	local Nr3=''
	local Nr4=''
	local Value=''

	if [ "$(EsIP "$IP")" = "1" ] ; then
		Nr1="$(printf '%s' "$IP" | cut -f 1 -d '.')"
		Nr2="$(printf '%s' "$IP" | cut -f 2 -d '.')"
		Nr3="$(printf '%s' "$IP" | cut -f 3 -d '.')"
		Nr4="$(printf '%s' "$IP" | cut -f 4 -d '.')"
		# Sometimes awk does not get stdin as expected, and can cache & repeat data
#		Value="$(printf '%s' "$IP" | awk -F '.' '{printf "%d\n", ($1 * 2^24) + ($2 * 2^16) + ($3 * 2^8) + $4}')"
		Value="$(printf '%s\n' "($Nr1 * 2^24) + ($Nr2 * 2^16) + ($Nr3 * 2^8) + $Nr4" | bc)"
		printf '%s\n' "$Value"
	fi
}

IPv4ToBinary ()
# Syntax as a function: $(IPv4ToBinary "$IP")
# Description: Returns (stdout) the binary representation of IPv4 address (unsigned numbers)
# Depends on functions: IPv4ToDecimal
# Depends on software packages: bc
{
	local IP="$1"
	local Value=''

	Value="$(IPv4ToDecimal "$IP")"
	if [ "$Value" != "" ] ; then
		Value="$(echo "obase=2;$Value" | bc)"
		if [ "$Value" != "" ] ; then
			Value="$(printf '%32s' "$Value" | tr ' ' '0')"
			printf '%s\n' "$Value"
		fi
	fi
}

BinaryToIPv4 ()
# Syntax as a function: $(BinaryToIPv4 "$BinaryDigits")
# Description: Returns (stdout) the IPv4 address of a 32-digit binary representation
# Note: Specified string MUST be 32 characters long.
# Depends on functions: (none)
# Depends on software packages: bc
{
	local BinaryDigits="$1"
	local Nr1=''
	local Nr2=''
	local Nr3=''
	local Nr4=''
	local Value=''

	if [ ${#BinaryDigits} -eq 32 ] && [ "$(printf '%s' "$BinaryDigits" | tr -s '1' '0')" = "0" ] ; then
		Nr1="$(printf '%s' "$BinaryDigits" | cut -c 1-8)"
		Nr1="$(echo "obase=10;ibase=2;${Nr1}" | bc)"
		Value="$Nr1"
		Nr2="$(printf '%s' "$BinaryDigits" | cut -c 9-16)"
		Nr2="$(echo "obase=10;ibase=2;${Nr2}" | bc)"
		Value="${Value}.${Nr2}"
		Nr3="$(printf '%s' "$BinaryDigits" | cut -c 17-24)"
		Nr3="$(echo "obase=10;ibase=2;${Nr3}" | bc)"
		Value="${Value}.${Nr3}"
		Nr4="$(printf '%s' "$BinaryDigits" | cut -c 25-32)"
		Nr4="$(echo "obase=10;ibase=2;${Nr4}" | bc)"
		Value="${Value}.${Nr4}"
		printf '%s\n' "$Value"
	fi
}

IPv4NetworkAddress ()
# Syntax as a function: $(IPv4NetworkAddress "$CIDR")
# Description: Returns (stdout) the base address of CIDR specification
# Example: 192.168.1.150/24 -> 192.168.1.0
# Depends on functions: Is_IntegerNr IPv4ToBinary BinaryToIPv4
# Depends on software packages: bc
{
	local CIDR="$1"
	local Address=''
	local Binary=''
	local Mask=''

	Address="$(printf '%s' "$CIDR" | cut -f 1 -d '/')"
	Mask="$(printf '%s' "$CIDR" | cut -f 2 -d '/')"
	if [ "$Address" != "$Mask" ] && [ "$Mask" != "" ] && Is_IntegerNr "$Mask" && [ $Mask -ge 0 ] && [ $Mask -le 32 ] ; then
		Binary="$(IPv4ToBinary "$Address")"
		if [ "$Binary" != "" ] ; then
			if [ $Mask -gt 0 ] ; then
				Binary="$(printf '%s' "$Binary" | cut -c 1-${Mask})"
			else
				Binary=''
			fi
			Binary="$(printf '%-32s' "$Binary" | tr ' ' '0')"
			BinaryToIPv4 "$Binary"
		fi
	fi
}

ParentPstree ()
# Alternative to pstree -p -s (syntax not available in 1990's or 2000's distributions)
# Depends on functions: Is_IntegerNr
# Depends on software packages: sed, procps
{
	local ChildPid="$1"
	local CurrentPid=''
	local Stat=''
	local StatRest=''
	local Value=''
	
	CurrentPid=$ChildPid
	while Is_IntegerNr "$CurrentPid" && [ "$CurrentPid" != "0" ] ; do
		Value="$CurrentPid $Value"
		Stat="$(cat /proc/${CurrentPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
		StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d ')' | sed -e 's|^ ||g')"
		CurrentPid="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ' | tail -n 1)"
#		CurrentPid=$(echo TrimAndSingle $(PsOutputValues ppid $CurrentPid | tail -n 1) | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')
		CurrentPid="$(echo TrimAndSingle $CurrentPid | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if ! Is_IntegerNr $CurrentPid ; then CurrentPid="" ; fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

PsOutputValues ()
# Syntax as a function: "$(PsOutputValues "$OutputColumns" "$FilterPid1 $FilterPid2 $FilterPid3...")"
# Description: Parser for ps command, that emulates -A and -o options for old versions without support for it. Only returns (stdout) asked output values.
# Expected parameters:
#	$1	Comma-separated list of keywords to compose columns. Following keywords are the only supported:
#		cmaj_flt,cmd,cmin_flt,comm,cstime,cutime,egid,euid,f,flag,flags,gid,maj_flt,min_flt,ni,nice,nlwp,pgid,pgrp,pid,ppid,pri,priority,rss,rssize,rsz,s,sess,session,sid,state,stime,T,tname,tpgid,tt,tty,uid,utime,vsize,vsz
#		+ specific "sort_pid" (sortable PID: 0001, 0002), uid_map (LXC base nr.), gid_map (LXC base nr.), rootfs (its container path)
#		Unexpected keywords are directly parsed to ps with modern syntax. Them return '?' in case of error.
#	$2	(optional) Space-separated list of PIDs to restrict to. If not provided, all PIDs will be selected.
# Notes:
#	- VERY SLOW! Better use directly "ps" if you don't need modern options.
#	- No headers are included
#	- Columns are separated by only one space character
#	- Every unknown value is returned as '?'
# Depends on functions: Is_IntegerNr ParentPstree
# Depends on software packages: grep sed
{
	local OutputColumns="$1"
	local FilterPids="$2"
	local CurValue=''
	local CurPid=''
	local CurColumn=''
	local CurLine=''
	local PidMaxLen=0
	local PidsToScan=''
	local ScanningPid=''
	local LastStatus=$?
	
	if ! Is_IntegerNr $FilterPids ; then
		FilterPids="$(ls -1 /proc/*/cmdline 2>/dev/null | grep -ve 'self' | cut -f 3 -d '/')"
	fi
	if [ "$OutputColumns" = "pid" ] ; then
		# To accelerate simple pids listing
		unset IFS ; for CurPid in $FilterPids ; do
			if [ -d /proc/$CurPid ] ; then printf '%s\n' "$CurPid" ; fi
		done
	else
		if [ "$(printf '%s\n' ",${OutputColumns}," | grep -e ',sort_pid,')" != "" ] ; then
			PidMaxLen=0
			unset IFS ; for CurPid in $FilterPids ; do
				if [ ${#CurPid} -gt $PidMaxLen ] ; then PidMaxLen=${#CurPid} ; fi
			done
		fi
		OutputColumns="$(printf '%s\n' "$OutputColumns" | tr -s ',' ' ')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| state | s |g' | sed -e 's| uid | euid |g' | sed -e 's| gid | egid |g' | sed -e 's| vsize | vsz |g' | sed -e 's| rssize | rss |g' | sed -e 's| rsz | rss |g' | sed -e 's| pgrp | pgid |g' | sed -e 's| session | sess |g')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| sid | sess |g' | sed -e 's| tname | tty |g' | sed -e 's| tt | tty |g' | sed -e 's| flags | f |g' | sed -e 's| flag | f |g' | sed -e 's| priority | pri |g' | sed -e 's| nice | ni |g' | sed -e 's| thcount | nlwp |g')"
		unset IFS ; for CurPid in $FilterPids ; do
			# A pid called "self" points to analyzer, such as "ls" or "cat". Discarding this.
			if Is_IntegerNr $CurPid ; then
				CurLine=''
				Stat="$(cat /proc/${CurPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
				StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d "${ParC}" | sed -e 's|^ ||g')"
				Status="$(cat /proc/${CurPid}/status 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g')"
				unset IFS ; for CurColumn in $OutputColumns ; do
					case "$CurColumn" in
						"sort_pid" ) CurValue="$(printf "%${PidMaxLen}s\n" "$CurPid" | sed -e 's| |0|g')" ;;
						"euid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Uid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"egid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Gid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"vsz" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmSize:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"rss" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmRSS:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"cmd" )
							CurValue="$(cat /proc/${CurPid}/cmdline 2>/dev/null | tr -s '\0' ' ' | tr -s ' ' | sed -e 's|$ ||g' | sed -e 's| $||g')"	# Command-line arguments come separated by null byles ('\0') and whole line ended too.
							if [ "$CurValue" = "" ] ; then	# comm
								CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")"
							fi
							;;
						"pid" )
							CurValue="$(printf '%s\n' "$Stat" | cut -f 1 -d "${ParO}" | cut -f 1 -d ' ')"
							if [ "$CurValue" = "" ] ; then
								CurValue=$CurPid
							fi
							;;
						"comm" ) CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")" ;;
						"s" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 1 -d ' ')" ;;
						"ppid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ')" ;;
						"pgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 3 -d ' ')" ;;
						"sess" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 4 -d ' ')" ;;
						"tpgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 6 -d ' ')" ;;
						"f" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 7 -d ' ')" ;;
						"min_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 8 -d ' ')" ;;
						"cmin_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 9 -d ' ')" ;;
						"maj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 10 -d ' ')" ;;
						"cmaj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 11 -d ' ')" ;;
						"utime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 12 -d ' ')" ;;
						"stime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 13 -d ' ')" ;;
						"cutime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 14 -d ' ')" ;;
						"cstime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 15 -d ' ')" ;;
						"pri" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 16 -d ' ')" ;;
						"ni" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')" ;;
						"nlwp" )
							CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')"	# W: Linux before 2.6 always returns "0"
							if [ "$CurValue" = "0" ] ; then
								CurValue="$(ps h p $CurPid -o $CurColumn 2>/dev/null)"
								if ! Is_IntegerNr $CurValue ; then CurValue='0' ; fi
							fi
							;;
						"T" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 19 -d ' ')" ;;	# Old starttime
#						"tty" ) CurValue="$(ps h p $CurPid -o tty 2>/dev/null)" ;;	# Old "tty" is not the same as modern one.
						"uid_map" )
							CurValue="$(cat /proc/${CurPid}/uid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"gid_map" )
							CurValue="$(cat /proc/${CurPid}/gid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"rootfs" )
							CurValue="$(cat /proc/${CurPid}/mountinfo 2>/dev/null)"
							if [ "$CurValue" != "" ] ; then
								CurValue="$(printf '%s' "$CurValue" | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
								if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi
							fi
							if [ "$CurValue" = "" ] ; then
								# It occurs on containerized defunct processes too
								PidsToScan="$(pstree -p $CurPid | tr ')' '\n' | grep -e '([0-9]' | sed -e 's|.*(||g')"
								unset IFS ; for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] ; then
								PidsToScan="$(ParentPstree $CurPid | tr ' ' '\n' | tac)"
								unset IFS ; for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] && [ $ScanningPid -ne 1 ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi  # Not in a container
							;;
						* )
							CurValue="$(ps h p $CurPid -o $CurColumn 2>&1 >/dev/null)"
							LastStatus=$?
							if [ $LastStatus -eq 0 ] && [ "$CurValue" = "" ] ; then
								# No error, No stderr
								CurValue="$(ps h p $CurPid -o $CurColumn)"
							else
								CurValue=''
							fi
							;;
					esac
					if [ "$CurValue" = "" ] ; then CurValue='?' ; fi
					CurLine="$CurLine $CurValue"
				done
				if [ -d "/proc/${CurPid}" ] ; then
					# Only if process has not disappeared while collecting data.
					printf '%s\n' "$CurLine" | sed -e 's|^ ||'
				fi
			fi
		done
	fi
}

#[/gesdonis]

##### SPECIFIC FUNCTIONS TO THIS SCRIPT #####

#[gesdonis]
Configuration_InitSystem ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
# This function must not write anything to disk. Mainly focused to detect InitSystem
{
	local ReadOnly=1
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then
		ReadOnly=1
		shift
	fi
	if [ "$1" = "pmc" ] ; then
		PackageManager_Call="$2"
		PackageManager_Mode="$3"
		PackageManager_ForVersion="$4"
	else
		PackageManager_Call=''
	fi
	if [ $(id -u) -eq 0 ] && [ "$(printf '%s\n' ":${PATH}:" | grep -e ':/sbin:')" = "" ] ; then
		# Some old distribution's root (ie RHL 7) legate $PATH from normal user when called by "su"
		printf '%s\n' "${sERROR}E: You have not /sbin in PATH environment variable, you may be using a normal user environment ${ParO}${LOGNAME}${ParC}${fRESET}" 1>&2
		LastStatus=61 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	InitSoftware=""
	if [ "$(command -v /bin/systemctl 2>/dev/null)$(command -v systemctl 2>/dev/null)" != "" ] ; then
		# [ "$(ls /run/systemd/ 2>/dev/null)" != "" ]	Condition could not work in a chroot
		InitSoftware="systemd"
	else
		if [ "$(command -v /sbin/initctl 2>/dev/null)$(command -v initctl 2>/dev/null)" != "" ] ; then
			# (All GNU operating systems with Upstart have replaced it by systemd later)
			# Used by default in Ubuntu from version 6.10 to version 14.10
			# Used by default in Fedora from version 9 to version 14
			# Used by default in RHEL 6 (Red Hat, CentOS, Scientific Linux, Oracle Linux)
			# Used by default in Maemo 5 and MeeGo
			InitSoftware="upstart"
		else
			if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
				# update-rc.d seems to be the LSB-compliant tool for System-V
				CurrentFile="$(command -v /sbin/runlevel 2>/dev/null)"
				if [ "$CurrentFile" = "" ] ; then CurrentFile="$(command -v runlevel 2>/dev/null)" ; fi
				if [ "$CurrentFile" != "" ] ; then
					if [ "$("$CurrentFile" --version 2>/dev/null | grep -ie upstart)" = "" ] ; then
						InitSoftware="systemv"
					else
						InitSoftware="upstart"
					fi
				fi
			else
				if [ -f /etc/inittab ] ; then
					# System-V
					InitSoftware="systemv"
				else
					InitSoftware=""
				fi
			fi
		fi
	fi
	ServiceEnabler=""
	InitProfilesDir=""
	InitProfilesDir_0=""
	ProgramExecutablePath=""
	InitExecutableParser=""
	if [ "$INIT_SCRIPT_InitCall" = "" ] ; then INIT_SCRIPT_InitCall=0 ; fi
	if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
		CallTree="$(ParentPstree $$)"
		if [ "$CallTree" != "" ] ; then
			CallTreeLevels="$(WordsNr () { printf '%s' $#; }; WordsNr $CallTree)"
			if [ $CallTreeLevels -le 2 ] ; then INIT_SCRIPT_InitCall=1 ; fi
		fi
	fi
	case "$InitSoftware" in
		"systemd" )
			ServiceEnabler="systemctl"
			if [ -d "/etc/systemd/system" ] ; then
				InitProfilesDir="/etc/systemd/system"
			fi
			if [ -d "/lib/systemd/system" ] ; then
				InitProfilesDir_0="/lib/systemd/system"
			fi
			if [ "$RootRequired" = "0" ] ; then
				ProgramExecutablePath="/bin/${ServiceName}"
			else
				ProgramExecutablePath="/sbin/${ServiceName}"
			fi
#			if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
				ProgramExecutablePath="/usr${ProgramExecutablePath}"
#			fi
			InitExecutableParser=""
			ServicePrefix=""
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$_SYSTEMCTL_SKIP_REDIRECT" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			;;
		"upstart" )
			if [ -d /etc/init ] ; then
				# Directory present in Ubuntu from version 9.10 to version 16.04 or more (with and without Upstart)
				ServiceEnabler="/etc/init" # upstart jobs are all enabled when profile is present at /etc/init/
				InitProfilesDir="/etc/init"
			else
				# Not all upstart versions had own /etc/init to place native job profiles
				if [ "$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)" != "" ] ; then
					ServiceEnabler="update-rc.d"
				else
					if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)" != "" ] ; then
						ServiceEnabler="insserv"
					else
						if [ "$(command -v /sbin/chkconfig 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
							ServiceEnabler="chkconfig"
						fi
					fi
				fi
			fi
			if [ -d "/etc/init.d" ] ; then
				InitExecutableParser="/etc/init.d/${ServiceName}"
				if [ "$RootRequired" = "0" ] ; then
					ProgramExecutablePath="/bin/${ServiceName}"
				else
					ProgramExecutablePath="/sbin/${ServiceName}"
				fi
#				if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
					ProgramExecutablePath="/usr${ProgramExecutablePath}"
#				fi
			else
				if [ -d /etc/rc.d/init.d ] ; then
					InitExecutableParser="/etc/rc.d/init.d/${ServiceName}"
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
				else
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
					InitExecutableParser=""
				fi
			fi
			ServicePrefix=""
			if [ ! -f "/etc/init/${ServiceName}.conf" ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
				if [ -x "/etc/init.d/${ServiceName}" ] || [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then
					# Let's use SystemV compatibility layer; it can be an upgraded environment from SystemV to Systemd
					if [ "$(command -v service 2>/dev/null)" != "" ] ; then
						ServicePrefix="service "
					else
						if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
							ServicePrefix="invoke-rc.d "
						fi
					fi
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$UPSTART_JOB" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			;;
		"systemv" )
			if [ "$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)" != "" ] ; then
				ServiceEnabler="update-rc.d"
			else
				if [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)" != "" ] ; then
					ServiceEnabler="insserv"
				else
					if [ "$(command -v /sbin/chkconfig 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] ; then
						ServiceEnabler="chkconfig"
					fi
				fi
			fi
			InitProfilesDir="" # System-V does not use job profile files
			if [ -d "/etc/init.d" ] ; then
				InitExecutableParser="/etc/init.d/${ServiceName}"
				if [ "$RootRequired" = "0" ] ; then
					ProgramExecutablePath="/bin/${ServiceName}"
				else
					ProgramExecutablePath="/sbin/${ServiceName}"
				fi
#				if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
					ProgramExecutablePath="/usr${ProgramExecutablePath}"
#				fi
			else
				if [ -d /etc/rc.d/init.d ] ; then
					InitExecutableParser="/etc/rc.d/init.d/${ServiceName}"
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
				else
					if [ "$RootRequired" = "0" ] ; then
						ProgramExecutablePath="/bin/${ServiceName}"
					else
						ProgramExecutablePath="/sbin/${ServiceName}"
					fi
#					if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
						ProgramExecutablePath="/usr${ProgramExecutablePath}"
#					fi
					InitExecutableParser=""
				fi
			fi
			# Must be useful for any action (install|--help|etc)
			if [ "$(command -v service 2>/dev/null)" != "" ] ; then
				ServicePrefix="service "
			else
				if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
					ServicePrefix="invoke-rc.d "
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "${RUNLEVEL}${runlevel}${INIT_VERSION}" != "" ] ; then
				INIT_SCRIPT_InitCall=1
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$PWD" = "/" ] && [ "$HOME" = "/root" ] ; then
				# E.g. Debian 3.1 needs this
				unset IFS ; for CurPid in $CallTree ; do
					if [ "$(ps $CurPid | grep -e ' service' -e '/service' -e ' invoke' -e '/invoke')" != "" ] ; then
						INIT_SCRIPT_InitCall=1
					fi
				done
			fi
			;;
		* )
			# Must be useful for any action (install|--help|etc)
			if [ "$(command -v service 2>/dev/null)" != "" ] ; then
				ServicePrefix="service "
			else
				if [ "$(command -v invoke-rc.d 2>/dev/null)" != "" ] ; then
					ServicePrefix="invoke-rc.d "
				fi
			fi
			if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
				if [ "${RUNLEVEL}${runlevel}${UPSTART_JOB}${UPSTART_EVENTS}${_SYSTEMCTL_SKIP_REDIRECT}${INIT_VERSION}" != "" ] || [ "$TERM" = "linux" ] ; then
					INIT_SCRIPT_InitCall=1
				fi
			fi
			;;
	esac
	InitToolForStartStop="$ServicePrefix"
	if [ "$InitToolForStartStop" = "" ] && [ "$InitSoftware" = "systemd" ] ; then InitToolForStartStop="systemctl" ; fi
	if [ "$InitToolForStartStop" = "" ] && [ "$InitSoftware" = "upstart" ] ; then InitToolForStartStop="initctl" ; fi
	RcLocal=""
	if [ -f /etc/rc.d/rc.local ] ; then RcLocal=/etc/rc.d/rc.local ; fi
	if [ -f /etc/rc.local ] ; then RcLocal=/etc/rc.local ; fi
	if [ "$ServiceEnabler" = "" ] && [ ! -d /etc/rc1.d ] && [ ! -d /etc/rc.d/rc1.d ] && [ -f "$RcLocal" ] ; then
		ServiceEnabler="$RcLocal"
	fi
	export INIT_SCRIPT_InitCall=$INIT_SCRIPT_InitCall
	return $StatusCode
}

SystemProgramIsEnabled ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
# Returns (exitcode) true if the main service is fully enabled to start on system boot. Otherwise false.
# Syntax (without brackets []):
#	if SystemProgramIsEnabled ; then printf '%s\n' "Yes." ; fi
{
	local RcBase=''
	local InRcLocal=0
	local ValueBoolean=1
	
	if [ "$(cat "$RcLocal" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	if [ "$(cat /etc/rc.d/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	if [ "$(cat /etc/rc.local 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | grep -E "^(${ProgramExecutablePath}|${ServiceName}) start")" != "" ] ; then InRcLocal=1 ; fi
	SystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')"
	if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^ENABLED=')" != "" ] ; then
		SystemvEnablementData="$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^ENABLED=' | tail -n 1)"
	else
		SystemvEnablementData="$(printf '%s\n' "$SystemvEnablementData" | grep -ie '^RUN=' | tail -n 1)"
	fi
	if [ $InRcLocal -eq 1 ] ; then
		# Init system independent way ~ hard way to invoke program
		ValueBoolean=0
		if [ "$SystemvEnablementData" != "" ] ; then
			# Manual filter present
			if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -iE '=(1|y)')" = "" ] ; then
				# Manual filter says something but "enabled"
				ValueBoolean=1
			fi
		fi
	else
		if [ "$SystemvEnablementData" != "" ] ; then
			# Manual filter present
			if [ "$(printf '%s\n' "$SystemvEnablementData" | grep -iE '=(1|y)')" != "" ] ; then
				# Manual filter says "enabled"
				ValueBoolean=0
			fi
		else
			# No manual filter; enabled depends only on init system
			ValueBoolean=0
		fi
		if [ $ValueBoolean -eq 0 ] ; then
			ValueBoolean=1
			case "$InitSoftware" in
				"systemd" )
					systemctl --quiet --system is-enabled "${ServiceName}.service" 2>/dev/null
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then ValueBoolean=0 ; fi
					if [ $ValueBoolean -eq 1 ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
						# It seems there is a SystemV compatibility layer; must check for upgraded environments from SystemV to Systemd
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						fi
					fi
					;;
				"upstart" )
					# Upstart considers it enabled when service profile file is present
					if [ "$InitProfilesDir" != "" ] ; then
						if [ -f "${InitProfilesDir}/${ServiceName}.conf" ] ; then ValueBoolean=0 ; fi
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}.conf" ] ; then ValueBoolean=0 ; fi
					fi
					if [ $ValueBoolean -eq 1 ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
						# It seems there is a SystemV compatibility layer; must check for upgraded environments from SystemV to Upspart
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						fi
					fi
					;;
				"systemv" )
					if [ "$ServiceEnabler" = "chkconfig" ] ; then
						if [ "$(chkconfig --list "$ServiceName" | grep -iE '(1|2|3|4|5):on')" ] ; then ValueBoolean=0 ; fi
					else
						RcBase=""
						if [ -d /etc/rc1.d ] ; then RcBase=/etc ; fi
						if [ -d /etc/rc.d/rc1.d ] ; then RcBase=/etc/rc.d ; fi
						if [ -d "$RcBase" ] ; then
							unset IFS ; for CurrentRc in 1 2 3 4 5 ; do
								if [ $ValueBoolean -ne 0 ] && [ -d "${RcBase}/rc${CurrentRc}.d" ] ; then
									IFS="$(printf '\n\b')" ; for CurrentLink in $(ls -1 "${RcBase}/rc${CurrentRc}.d"/ 2>/dev/null | grep -ie '^S') ; do unset IFS	#"
										if [ "$(ReadlinkF "${RcBase}/rc${CurrentRc}.d/${CurrentLink}" | grep -e "/${ServiceName}$")" != "" ] ; then ValueBoolean=0 ; fi	#"
									done
								fi
							done
						else
							if [ -x "/etc/init.d/${ServiceName}" ] ; then ValueBoolean=0 ; fi
						fi
					fi
					;;
			esac
		fi
	fi
	return $ValueBoolean
}

RecommendedInvocation ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: gesdonis
{
	local Action="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Object="$1"
	local ChildsObjects=""
	local SudoPrefix=''
	local SudoSuffix=''
	local Option="$Action"
	local Value=""
	
	if [ "$RootRequired" != "0" ] && [ $(id -u) -ne 0 ] ; then
		if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
			SudoPrefix="sudo "
		else
			SudoPrefix='su -c "'
			SudoSuffix='"'
		fi
	fi
	if [ "$ChildsPluralName" != "" ] && [ "$Object" != "" ] ; then
		ChildsObjects=" $@"
	fi
	case "$Option" in
		"start" ) Option="start-stop" ;;
		"stop" ) Option="start-stop" ;;
		"restart" ) Option="start-stop" ;;
		"reload" ) Option="start-stop" ;;
	esac
	if [ "$Object" = "" ] || [ "$ChildsPluralName" != "" ] ; then
		# Child treatment is allowed if childs are supported.
		if [ $(id -u) -eq 0 ] && SystemProgramIsEnabled ; then	# Pending to replace the use of SystemProgramIsEnabled() by a function like ServiceIsRegistered()
			case "$Option" in
				"enable" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} enable${ChildsObjects}${SudoSuffix}" ;;
				"disable" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} disable${ChildsObjects}${SudoSuffix}" ;;
				"start-stop" )
					if [ "$InitProfilesDir" != "" ] ; then
						case "$InitSoftware" in
							"systemd" )
								if [ "$Object" = "" ] ; then
#									Value="systemctl $Action $ServiceName"
									if [ -f "${InitProfilesDir}/${ServiceName}.service" ] ; then
										Value="systemctl $Action $ServiceName"
									else
										if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}.service" ] ; then
											Value="systemctl $Action $ServiceName"
										else
											Value="$ServiceName $Action"
										fi
									fi
								else
									if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
										Value="systemctl $Action \"${ServiceName}@${Object}\""
									else
										if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
											Value="systemctl $Action \"${ServiceName}@${Object}\""
										else
											Value="$ServiceName ${Action}${ChildsObjects}"
										fi
									fi
								fi
								;;
							"upstart" )
								if [ "$Object" = "" ] ; then
									if [ ! -f "/etc/init/${ServiceName}.conf" ] && [ "$(command -v /sbin/insserv 2>/dev/null)$(command -v insserv 2>/dev/null)$(command -v /usr/sbin/update-rc.d 2>/dev/null)$(command -v update-rc.d 2>/dev/null)$(command -v chkconfig 2>/dev/null)" != "" ] && [ "$(command -v /sbin/runlevel 2>/dev/null)$(command -v runlevel 2>/dev/null)" != "" ] ; then
										if [ -x "/etc/init.d/${ServiceName}" ] || [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then
											# Let's use SystemV compatibility layer; it can be an upgraded environment from SystemV to Systemd
											Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}"
										else
											Value="initctl $Action $ServiceName"
										fi
									else
										Value="initctl $Action $ServiceName"
									fi
								else
									Value="$ServiceName ${Action}${ChildsObjects}"
								fi
								;;
							* ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}" ;;
						esac
					else
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} ${Action}${ChildsObjects}${SudoSuffix}"
					fi
					;;
				"status" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} status${ChildsObjects}${SudoSuffix}" ;;
				"create" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} create${ChildsObjects}${SudoSuffix}" ;;
				"delete" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} delete${ChildsObjects}${SudoSuffix}" ;;
				"list" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} list${ChildsObjects}${SudoSuffix}" ;;
				"details" ) Value="${SudoPrefix}${ServicePrefix}${ServiceName} details${ChildsObjects}${SudoSuffix}" ;;
				"install" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} install${SudoSuffix}"
					fi
					;;
				"uninstall" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
					fi
					;;
				"purge" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} purge${SudoSuffix}"
					fi
					;;
			esac
		else
			case "$Option" in
				"enable" )
					if [ "$Object" = "" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} enable${ChildsObjects}${SudoSuffix}"
					else
						Value="${ServiceName} enable${ChildsObjects}"
					fi
					;;
				"disable" )
					if [ "$Object" = "" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} disable${ChildsObjects}${SudoSuffix}"
					else
						Value="${ServiceName} disable${ChildsObjects}"
					fi
					;;
				"start-stop" ) Value="${ServiceName} ${Action}${ChildsObjects}" ;;
				"status" ) Value="${ServiceName} status${ChildsObjects}" ;;
				"create" ) Value="${ServiceName} create${ChildsObjects}" ;;
				"delete" ) Value="${ServiceName} delete${ChildsObjects}" ;;
				"list" ) Value="${ServiceName} list${ChildsObjects}" ;;
				"details" ) Value="${ServiceName} details${ChildsObjects}" ;;
				"install" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} install${SudoSuffix}"
					fi
					;;
				"uninstall" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
					fi
					;;
				"purge" )
					if [ "$ProgramInstaller" = "1" ] ; then
						Value="${SudoPrefix}${ServicePrefix}${ServiceName} purge${SudoSuffix}"
					fi
					;;
			esac
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

FileReplacers ()
# Description: Returns (stdout) all replacer tags from a file, one per line. No repetitions.
{
	local TemplateOrContent="$1"
	
	if [ -f "$TemplateOrContent" ] ; then
		TemplateOrContent="$(cat "$TemplateOrContent")"
	fi
	printf '%s\n' "$TemplateOrContent" | cut -f 1 -d ';' | sed -e 's|{|\n{|g' -e 's|}|}\n|g' | grep -e '^{a:..*}$' -e '^{a,..*:..*}$' -e '^{f:..*}$' -e '^{h:..*}$' -e '^{x:..*}$' | sort -u
}

ReplacerValue ()
# Description: Returns (stdout) replaced value for a template tag.
#		{a:smtp.example.net}	<- Solved IP address to regenerate only when edited
#		{a:smtp.example.net:h}	<- Solved IP address to regenerate hourly
#		{a:smtp.example.net:d}	<- Solved IP address to regenerate daily
#		{a:smtp.example.net:w}	<- Solved IP address to regenerate weekly
#		{a:smtp.example.net:m}	<- Solved IP address to regenerate monthly
#		{a,1.1.1.1:smtp.example.net}	<- Solved IP address with specified NS
#		{a:/srv/named/fqdn.txt}	<- Solved IP address from a file contained name
#		{a:https://www.example.net/fqdn.html}	<- Solved IP address from HTTP downloaded name
#		{f:/srv/named/value.txt}	<- File content
#		{h:https://www.example.net/value.html}	<- HTTP content
#		{x:command}	<- Execution stdout
{
	local Replacer="$1"
	local Modificator=''
	local Pointer=''
	local Period=''
	local Value=''
	
	Modificator="$(printf '%s' "$Replacer" | cut -sf 2 -d ',' | cut -f 1 -d ':')"
	Pointer="$(printf '%s' "$Replacer" | cut -sf 2- -d ':' | sed -e 's|}$||' -e 's|:h$||g' -e 's|:d$||g' -e 's|:w$||g' -e 's|:m$||g')"
	Period="$(printf '%s' "$Replacer" | grep -e ':h}$' -e ':d}$' -e ':w}$' -e ':m}$' | sed -e 's|.*:||g' -e 's|}||g')"
	if [ "$(printf '%s' "$Replacer" | grep -e '^{a:' -e '^{a,')" ] ; then
		if [ "$(printf '%s' "$Pointer" | grep -e '/')" != "" ] ; then
			if [ "$(printf '%s' "$Pointer" | grep -e '.://.')" != "" ] && [ "$(printf '%s' "$Pointer" | grep -e '^/')" = "" ] ; then
				Pointer="$(HttpGetContent "$Pointer" $Modificator)"
			else
				Pointer="$(cat "$Pointer")"
			fi
		fi
		Value="$(NetnameDestinations "$Pointer" $Modificator)"
	else
		if [ "$(printf '%s' "$Replacer" | grep -e '^{f:')" ] ; then
			Value="$(cat "$Pointer")"
		else
			if [ "$(printf '%s' "$Replacer" | grep -e '^{h:')" ] ; then
				Value="$(HttpGetContent "$Pointer" $Modificator)"
			else
				if [ "$(printf '%s' "$Replacer" | grep -e '^{x:')" ] ; then
					Value="$(eval "$Pointer")"
				fi
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

FileContentReplaced ()
{
	local TemplateOrContent="$1"
	local Content=''
	local Replacers=''
	local CurReplacer=''
	local CurValue=''
	local ReplacedContent=''
	
	Content="$TemplateOrContent"
	if [ -f "$TemplateOrContent" ] ; then
		Content="$(cat "$TemplateOrContent")"
	fi
	Replacers="$(FileReplacers "$Content")"
	IFS="$(printf "\n\b")" ; for CurReplacer in $Replacers ; do unset IFS
		CurValue="$(ReplacerValue "$CurReplacer")"
		if [ $(printf '%s\n' "$CurValue"  | wc -l) -gt 1 ] ; then
			CurValue="$(echo TrimAndSingle $CurValue | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		fi
		Content="$(printf '%s' "$Content" | sed -e "s|${CurReplacer}|${CurValue}|g")"
	done
	if [ "$Content" != "" ] ; then printf '%s\n' "$Content" ; fi
}

CheckBindConfig ()
{
	local ServiceUser=''
	local LastStatus=0
	local StatusCode=0
	
	ServiceUser="$(cat /etc/default/named 2>/dev/null | grep -ie '^OPTIONS=' | cut -f 2 -d '"' | sed -e 's| -|\n-|g' | grep -e '^-u ' | cut -f 2 -d ' ')"
	if [ "$ServiceUser" != "" ] ; then
		runuser -u $ServiceUser -- named-checkconf /etc/bind/named.conf
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		named-checkconf /etc/bind/named.conf
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

NSFichero ()
# Sintaxis como función: $(NSFichero "$RutaFichero")
# Descripción: Devuelve (echo) una lista de valores de los registros NS encontrados en el fichero de zona $RutaFichero
# Notas:
#	- Si un valor terminaba con un punto, excluye el punto final.
#	- No devuelve valores repetidos
#	- Los valores se devuelven separados por espacios
{
	local RutaFichero="$1"
	local ValorActual=""
	local ContenidoRestante=""
	local Valores=""
	local LongCadena=0
	
	if [ "$RutaFichero" != "" ] ; then
		if [ -f "$RutaFichero" ] ; then
			ContenidoRestante="$(cat "$RutaFichero" | cut -f 1 -d ";")"
			ContenidoRestante="$(echo "$ContenidoRestante" | grep -E "[[:blank:]]IN[[:blank:]].*NS[[:blank:]]" | grep -E "[[:blank:]]IN.*[[:blank:]]NS[[:blank:]]")"
			while [ "$ContenidoRestante" != "" ] ; do
				ValorActual="$(ParaulaEspecial () { while [ "$1" != "" ] && [ "$1" != "IN" ] ; do shift ; done ; echo $3; }; ParaulaEspecial $ContenidoRestante)"
				if [ "$ValorActual" != "" ] ; then
					ContenidoRestante="$(echo "$ContenidoRestante" | grep -vE "[[:blank:]]$ValorActual(\.$|$|[[:blank:]])")"
					if [ "$(echo "$ValorActual" | grep -e "\.$")" != "" ] ; then
						LongCadena=${#ValorActual}
						if [ $LongCadena -gt 1 ] ; then
							LongCadena=$(($LongCadena - 1))
							ValorActual="$(echo "$ValorActual" | cut -c 1-$LongCadena)"
						else
							ValorActual=""
						fi
					fi
				fi
				if [ "$ValorActual" != "" ] ; then Valores="$Valores $ValorActual" ; fi
			done
		fi
	fi
	if [ "$Valores" != "" ] ; then
		echo $Valores  # Trim
	fi
}

FicherosConNS ()
# Sintaxis como función:	$(FicherosConNS "$Directorio" "$NSSeleccionado")
# Descripción:	Devuelve (echo) una lista de ficheros de zona que se encuentran en el $Directorio y tienen registros NS con valor $NSSeleccionado
# Notas:
#	- Devuelve los nombres sin ruta de directorio.
#	- Devuelve un nombre por línea.
{
	local Directorio="$1"
	local NSSeleccionado="$2"
	local ListaFicheros=""
	local FicheroActual=""
	local NSEncontrados=""
	
	if [ "$NSSeleccionado" != "" ] ; then
		if [ -d "$Directorio" ] ; then
			if [ "$(echo "$Directorio" | grep -e "/$")" = "" ] ; then
				Directorio="${Directorio}/"
			fi
			ListaFicheros="$(ls -1p "$Directorio" | grep -ve "/$")"
			IFS="$(printf "\n\b")" ; for FicheroActual in $ListaFicheros ; do unset IFS
				NSEncontrados="$(NSFichero "${Directorio}${FicheroActual}")"
				if [ "$(echo " $NSEncontrados " | grep -ie " $NSSeleccionado ")" != "" ] ; then
					echo "$FicheroActual"
				fi
			done
		fi
	fi
}

ReemplazarFicherosConNS ()
# Sintaxis:	ReemplazarFicherosConNS "$DirectorioOrigen" "$DirectorioDestino" "$NSSeleccionado" $ActualizarServicio
# Descripción:	Elimina todos los ficheros de destino que tienen el NS seleccionado, y luego mueve todos
#		los ficheros de origen a destino.
# Notas:
#	- El parámetro opcional $ActualizarDatos permite especificar:
#		- "." para que se actualicen los datos de todas las vistas
#		- El nombre de una vista para que se actualice dicha vista

{
	local DirectorioOrigen="$1"
	local DirectorioDestino="$2"
	local NSSeleccionado="$3"
	local ActualizarServicio="$4"
	local ListaAntiguos=""
	local ListaNuevos=""
	local FicheroActual=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NSSeleccionado" != "" ] ; then
		if [ -d "$DirectorioOrigen" ] ; then
			if [ "$(echo "$DirectorioOrigen" | grep -e "/$")" = "" ] ; then
				DirectorioOrigen="${DirectorioOrigen}/"
			fi
			if [ "$(echo "$DirectorioDestino" | grep -e "/$")" = "" ] ; then
				DirectorioDestino="${DirectorioDestino}/"
			fi
			ListaAntiguos="$(FicherosConNS "$DirectorioDestino" "$NSSeleccionado")"
			IFS="$(printf "\n\b")" ; for FicheroActual in $ListaAntiguos ; do unset IFS
				echo "Se elimina la antigua zona ${FicheroActual}"
				rm "${DirectorioDestino}${FicheroActual}"
			done
			ListaNuevos="$(ls -1p "$DirectorioOrigen" | grep -ve "/$")"
			IFS="$(printf "\n\b")" ; for FicheroActual in $ListaNuevos ; do unset IFS
				echo "Se coloca el nuevo fichero ${FicheroActual}"
				MkfilePP "${DirectorioDestino}${FicheroActual}" :bind ug=rwX,o=rX g+s
				cat "${DirectorioOrigen}${FicheroActual}" > "${DirectorioDestino}${FicheroActual}"
				rm "${DirectorioOrigen}${FicheroActual}"
			done
			case "$ActualizarServicio" in
				"" )	sleep 0	;;
				"." )	Service reload --nosoa	;;
				* )	Service reload --nosoa "$ActualizarServicio"	;;
			esac
		else
			echo "E: No se encontró el directorio de origen \"$DirectorioOrigen\"" 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "E: No se especificó un valor NS" 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

EncabezadoFicheroRegenerado ()
# Sintaxis:	EncabezadoFicheroRegenerado "$MarcaComentario"
# Parámetros esperados:
#	$1	Símbolo de comentario, como "#" o ";"
{
	local MarcaComentario="$1"
	echo "$MarcaComentario Este fichero se regenera con el asistente $ProgramName"
	echo "$MarcaComentario (si lo editas manualmente puedes perder los cambios)"
	echo "$MarcaComentario Ultima actualizacion $(date +'%F %T %Z')"
	echo ""
}

EliminarLineasFichero ()
# Sintaxis:	EliminarLineasFichero "$Fichero" "$Coincidencia1" "$Coincidencia2"
# Descripción:	Elimina del fichero de texto todas las líneas que contengan $Coincidencia1 y Coincidencia2
# Notas:	Coincidencia2 es opcional.
{
	local Fichero="$1"
	local Coincidencia1="$2"
	local Coincidencia2="$3"
	local FTemporal=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Fichero" != "" ] && [ "$Coincidencia1" != "" ] ; then
		if [ -f "$Fichero" ] ; then
			FTemporal="$(mktemp)"
			if [ "$Coincidencia2" = "" ] ; then
				cat "$Fichero" | grep -ive "$Coincidencia1" > "$FTemporal"
			else
				cat "$Fichero" | grep -ive "$Coincidencia1.*$Coincidencia2" | grep -ive "$Coincidencia2.*$Coincidencia1" > "$FTemporal"
			fi
			cat "$FTemporal" > "$Fichero"
			rm "$FTemporal"
		else
			echo "PROBLEMA(EliminarLineasFichero): No se encontró el fichero \"$Fichero\""
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "PROBLEMA(EliminarLineasFichero): No se especificó el fichero o el texto a seleccionar."
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

PreguntarVistaNoEspecificada ()
# Sintaxis:	PreguntarVistaNoEspecificada "$NombreVista" $Existente
# Descripción: Pide al usuario que escriba el nombre de una vista existente, y lo establece en la variable global $Respuesta
# Parámetros esperados:
#	$1	Nombre de la vista que se pudo suministrar. Sólo pregunta si está en blanco.
#	$2	"1" para dar error en caso que no exista la vista. "0" para dar error en caso que exista la vista.
{
	local NombreVista="$1"
	local Existente="$2"
	local VistasEncontradas=""
	local LastStatus=0
	local StatusCode=0
	
	Respuesta=""
	VistasEncontradas="$(ls -1p "${data_path}/views/" | grep -e "/$" | cut -f 1 -d "/")"
	if [ "$NombreVista" = "" ] ; then
		echo "- Estas son las vistas existentes:"
		echo "$VistasEncontradas"
		echo ""
		if [ "$Existente" = "0" ] ; then
			echo "Escribe el nombre para una nueva vista."
		else
			echo "Escribe el nombre exacto de una vista."
		fi
		read NombreVista
	fi
	if [ "$NombreVista" != "" ] ; then
		if [ "$Existente" = "1" ] && [ "$(echo "$VistasEncontradas" | grep -e "^${NombreVista}$")" = "" ] ; then
			echo "${sERROR}E: ${fBOLD}${fLOW}Vista${fRESET}${sERROR} \"${NombreVista}\" no encontrada.${fRESET}" 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$Existente" = "0" ] && [ "$(echo "$VistasEncontradas" | grep -e "^${NombreVista}$")" != "" ] ; then
			echo "E: Ya existe una vista de nombre \"${NombreVista}\"." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "E: No se especificó ningún nombre de vista." 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$NombreVista" != "" ] ; then
		Respuesta="$NombreVista"
	fi
	return $StatusCode
}

PreguntarZonaNoEspecificada ()
# Sintaxis:	PreguntarZonaNoEspecificada "$Vista" "$NombreZona" $Existente
# Parámetros esperados:
#	$1	Nombre de la vista a la que corresponda la zona.
#	$2	Nombre de la zona que se pudo suministrar. Sólo pregunta si está en blanco.
#	$3	"1" para dar error en caso que no exista la zona. "0" para dar error en caso que exista la zona.
# Descripción: Pide al usuario que escriba el nombre de una zona existente, y lo establece en la variable global $Respuesta
{
	local Vista="$1"
	local NombreZona="$2"
	local Existente="$3"
	local ZonasEncontradasM=""
	local ZonasEncontradasS=""
	local ZonasEncontradas=""
	local LastStatus=0
	local StatusCode=0
	
	Respuesta=""
	if [ -d "${data_path}/views/$Vista/master" ] && [ -d "${data_path}/views/$Vista/slave" ] && [ "$Vista" != "" ] ; then
		ZonasEncontradasM="$(ls -1p "${data_path}/views/$Vista/master/" | grep -ve "/$")"
		ZonasEncontradasS="$(ls -1p "${data_path}/views/$Vista/slave/" | grep -ve "/$")"
		ZonasEncontradas="$(echo "$ZonasEncontradasM" ; echo "$ZonasEncontradasS")"
		if [ "$NombreZona" = "" ] ; then
			echo "- Estas son las zonas existentes:"
			echo $ZonasEncontradas
			echo ""
			if [ "$Existente" = "0" ] ; then
				echo "Escribe el nombre de dominio para la zona."
			else
				echo "Escribe el nombre exacto de una zona."
			fi
			read NombreZona
		fi
		if [ "$NombreZona" != "" ] ; then
			NombreZona="$(printf '%s' "$NombreZona" | sed -e 's|\.$||')"
			if [ "$Existente" = "1" ] && [ "$(echo "$ZonasEncontradas" | grep -e "^${NombreZona}$")" = "" ] ; then
				echo "E: Zona no encontrada." 1>&2
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$Existente" = "0" ] && [ "$(echo "$ZonasEncontradas" | grep -e "^${NombreZona}$")" != "" ] ; then
				echo "E: Ya existe una zona con ese nombre." 1>&2
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			echo "E: No se especificó ningún nombre." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "E: No se encontraron los directorios de la vista \"$Vista\"." 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$NombreZona" != "" ] ; then
		Respuesta="$NombreZona"
	fi
	return $StatusCode
}

PreguntarZonaPtrNoEspecificada ()
# Sintaxis:	PreguntarZonaPtrNoEspecificada "$Vista" "$NombreZona" $Existente
# Parámetros esperados:
#	$1	Nombre de la vista a la que corresponda la zona.
#	$2	Nombre de la zona que se pudo suministrar. Sólo pregunta si está en blanco.
#	$3	"1" para dar error en caso que no exista la zona. "0" para dar error en caso que exista la zona.
# Descripción: Pide al usuario que escriba el nombre de una zona o IP-PTR existente, y establece la zona en la variable global $Respuesta
{
	local Vista="$1"
	local NombreZona="$2"
	local Existente="$3"
	local LaIP=''
	local LaIPR=''
	local NrActual=''
	local ZonasEncontradas=""
	local NombreZonaRegexp=''
	local LastStatus=0
	local StatusCode=0
	
	Respuesta=""
	if [ -d "${data_path}/views/$Vista/master" ] && [ -d "${data_path}/views/$Vista/slave" ] && [ "$Vista" != "" ] ; then
		ZonasEncontradas="$(Ptr_list | grep -ve '==' -ve '^$')"
		if [ "$NombreZona" = "" ] ; then
			printf '%s\n' "- Estas son las zonas con PTR existentes:"
			printf '%s\n' "$ZonasEncontradas"
			printf '%s\n' ""
			printf '%s' "Escribe el de la zona o direccion IP: "
			read NombreZona
		fi
		if [ "$NombreZona" != "" ] ; then
			if [ "$(printf '%s' "$NombreZona" | grep -ie 'in-addr\.arpa')" = "" ] ; then
				# Assumed to be an IP address
				LaIP="$NombreZona"
				for NrActual in $(printf '%s' "$LaIP" | tr '.' ' ') ; do
					if [ "$LaIPR" != "" ] ; then LaIPR=".${LaIPR}" ; fi
					LaIPR="${NrActual}${LaIPR}"
				done
				NombreZona="${LaIPR}.in-addr.arpa"
			fi
			NombreZona="$(printf '%s' "$NombreZona" | sed -e 's|\.$||')"
			NombreZonaRegexp="$(printf '%s\n' "$NombreZona" | sed -e 's|\.|\\.|g')"
			if [ "$Existente" = "1" ] && [ "$(printf '%s\n' "$ZonasEncontradas" | grep -e "^${NombreZonaRegexp} " -e "^${NombreZonaRegexp}\. ")" = "" ] && [ ! -f "${data_path}/views/${Vista}/master/$NombreZona" ] && [ ! -f "${data_path}/views/${Vista}/slave/$NombreZona" ] ; then
				printf '%s\n' "E: Zona $NombreZona no encontrada." 1>&2
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$Existente" = "0" ] && [ "$(printf '%s\n' "$ZonasEncontradas" | grep -e "^${NombreZonaRegexp} ")" != "" ] ; then
				printf '%s\n' "E: Ya existe una zona con el nombre $NombreZona" 1>&2
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			printf '%s\n' "E: No se especificó ningún nombre o IP." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: No se encontraron los directorios de la vista \"$Vista\"." 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$NombreZona" != "" ] ; then
		Respuesta="$NombreZona"
	fi
	return $StatusCode
}

ReferirZonasVista ()
# Sintaxis:	ReferirZonasVista "$NombreVista"
# Descripción:	Regenera el fichero ${data_path}/views/$NombreVista/view a partir de las plantillas y los ficheros de zona encontrados.
{
	local NombreVista="$1"
	local LastStatus=0
	local StatusCode=0
	local LasZonas=""
	local FZonaActual=""
	local NombreZonaActual=""
	local MasterActual=""
	
	echo "Regenerando vista \"$NombreVista\"..."
	MkdirPP "${data_path}/views/${NombreVista}/master" :bind ug=rwX,o=rX g+s
	MkdirPP "${data_path}/views/${NombreVista}/slave" :bind ug=rwX,o=rX g+s
	MkfilePP "${data_path}/views/${NombreVista}/view" :bind ug=rwX,o=rX
	EncabezadoFicheroRegenerado '#' > "${data_path}/views/${NombreVista}/view"
	if [ -f "${data_path}/views/${NombreVista}/view.header" ] && [ -f "${data_path}/views/${NombreVista}/view.footer" ] ; then
		cat "${data_path}/views/${NombreVista}/view.header" > "${data_path}/views/${NombreVista}/view"
		LasZonas="$(ls -1p "${data_path}/views/${NombreVista}/master/" | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for FZonaActual in $LasZonas ; do unset IFS
			NombreZonaActual="$FZonaActual"
			echo "	zone \"${NombreZonaActual}\" IN {" >> "${data_path}/views/${NombreVista}/view"
			echo "		type master;" >> "${data_path}/views/${NombreVista}/view"
			echo "		file \"${data_path}/views/${NombreVista}/master/$FZonaActual\";" >> "${data_path}/views/${NombreVista}/view"
			echo "	};" >> "${data_path}/views/${NombreVista}/view"
		done
		LasZonas="$(ls -1p "${data_path}/views/${NombreVista}/slave/" | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for FZonaActual in $LasZonas ; do unset IFS
			NombreZonaActual="$FZonaActual"
			MasterActual="$(NSFichero "${data_path}/views/${NombreVista}/slave/$FZonaActual")"
			if [ "$MasterActual" != "" ] ; then
				MasterActual="$(UnaParaula () { echo $1; }; UnaParaula $MasterActual)"
				MasterActual="$(dig +short $MasterActual | tail --lines=1)"
			fi
			if [ "$MasterActual" != "" ] ; then
				echo "	zone \"${NombreZonaActual}\" IN {" >> "${data_path}/views/${NombreVista}/view"
				echo "		type slave;" >> "${data_path}/views/${NombreVista}/view"
				echo "		masters {${MasterActual};};" >> "${data_path}/views/${NombreVista}/view"
				echo "		file \"${data_path}/views/${NombreVista}/slave/$FZonaActual\";" >> "${data_path}/views/${NombreVista}/view"
				echo "	};" >> "${data_path}/views/${NombreVista}/view"
			else
				echo "W: No se encontró en servidor NS de la zona \"NombreZonaActual\"." 1>&2
				echo "   Se ha excluido dicha zona de la carga de datos." 1>&2
				LastStatus=$1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			done
		cat "${data_path}/views/${NombreVista}/view.footer" >>  "${data_path}/views/${NombreVista}/view"
	else
		echo "E: No se encontro alguna plantilla para construir el fichero de vista." 1>&2
		echo "	${data_path}/views/${NombreVista}/view.header"
		echo "	${data_path}/views/${NombreVista}/view.footer"
		LastStatus=$1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

SoaFileSerial ()
# Description: Returns (stdout) serial number of SOA RR from a SOA dedicated file.
{
	local FilePath="$1"
	local CheckerFile=''
	local Value=''

	if [ -f "$FilePath" ] ; then
		if [ "$(ReadlinkF "${data_path}/soa-checker-link")" != "$FilePath" ] ; then
			rm -f "${data_path}/soa-checker-link"
			ln -s "$FilePath" "${data_path}/soa-checker-link"
		fi
		CheckerFile="${data_path}/soa-checker-zone"
		if [ ! -f "$CheckerFile" ] ; then
			MkfilePP "$CheckerFile" :bind ug=rwX,o=rX g+s
			echo '$ORIGIN example.net.' > "$CheckerFile"
			echo '$INCLUDE /srv/named/soa-checker-link' >> "$CheckerFile"
			echo '@ IN NS ns1.example.net.' >> "$CheckerFile"
			echo 'ns1.example.net. IN A 1.2.3.4' >> "$CheckerFile"
		fi
		Value="$(named-compilezone -i none -s relative -k ignore -o - example.net "$CheckerFile" 2>&1 | grep -e ' loaded serial ' | tr ' ' '\n' | tail -n 1)"
	fi
	if Is_IntegerNr $Value ; then echo $Value ; fi
}

SoaNextSerial ()
# Description: Returns (stdout) next serial number to apply to updated SOA RR, tracking last one from a SOA dedicated file.
{
	local FilePath="$1"
	local LastValue=''
	local LastDate=''
	local LastNumber=''
	local CurDate=''
	local NewNumber=''
	
	LastValue="$(SoaFileSerial "$FilePath")"
	LastDate="$(echo "$LastValue" | cut -c 1-8)"
	CurDate="$(date '+%Y%m%d')"
	if [ "$LastDate" = "$CurDate" ] ; then
		LastNumber="$(echo "$LastValue" | cut -c 9-)"
		while [ "$LastNumber" != "0" ] && [ "$(printf '%s' "$LastNumber" | grep -e '^0')" ] ; do
			LastNumber="$(printf '%s' "$LastNumber" | sed -e 's|^.||')"
		done
		NewNumber=$((LastNumber + 1))
		if [ $NewNumber -lt 10 ] ; then NewNumber="0${NewNumber}" ; fi
	else
		NewNumber='00'
	fi
	echo "${CurDate}${NewNumber}"
}

RegenerarSOA ()
# Descripción: Regenera el «Start of Authority Resource Record»
# https://en.wikipedia.org/wiki/SOA_record
# https://www.zytrax.com/books/dns/ch8/soa.html
# TO DO:
#	- Sería deseable que cada zona tenga su propio SOA con su número de serie, para que, con un sólo cambio no parezca que todas las zonas cambiaron.
#	- Usar el formato de número de serie recomendado YYYYMMDDnn pues así el número de cambio 00 a 99 sólo corresponde a la zona actualizada.
{
	local Cur_Authorityserver="$1"
	local Cur_Hostmaster=''
	local NumeroSerie=''
	local Filename=''
	local FilePath=''
	local LastSerial=''
	local ReplicateOldDefault=0
	
	if [ "$Cur_Authorityserver" = "" ] || [ "$Cur_Authorityserver" = "$default_authorityserver" ] ; then
		Cur_Authorityserver="$(echo TrimAndSingle $default_authorityserver | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		Cur_Authorityserver="$(printf '%s\n' "$Cur_Authorityserver" | cut -f 1 -d ' ')"
		ReplicateOldDefault=1
	fi
	Filename="$Cur_Authorityserver"
	if [ "$(printf '%s\n' "$Filename" | grep -e '.\.$')" != "" ] ; then
		Filename="$(printf '%s\n' "$Filename" | sed -e 's|\.$||g')"
	fi
	if [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.')" != "" ] && [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.$')" = "" ] && [ "$(EsIP "$default_authorityserver")" != "1" ] ; then
		Cur_Authorityserver="${Cur_Authorityserver}."
	fi
	FilePath="${data_path}/common-records/SOA/$Filename"
	# Sobre la utilidad y formato del SN vease https://www.zytrax.com/books/dns/ch8/soa.html
	# Usábamos yymmddHHM (AñoCorto-Mes-Dia-Hora-DecenaDelMinuto) que da hasta el año 2099
#	NumeroSerie="$(date +'%y%m%d%H')$(date +'%M' | cut -c 1)"
#	NumeroSerie="$(date '+%s')"
	NumeroSerie=$(SoaNextSerial "$FilePath")
	Cur_Hostmaster="$default_postmaster"
	if [ "$(printf '%s' "$Cur_Hostmaster" | grep -e '\.$')" = "" ] ; then
		Cur_Hostmaster="${Cur_Hostmaster}.$(echo $(printf '%s' "$Cur_Authorityserver" | tr '.' '\n' | tail -n 2) | tr ' ' '.')."
	fi
	MkfilePP "$FilePath" :bind ug=rwX,o=rX g+s
	EncabezadoFicheroRegenerado ";" > "$FilePath"
	echo "\$TTL $default_ttl	; Default time-to-live for zone" >> "$FilePath"
	echo "@		IN	SOA	$Cur_Authorityserver $Cur_Hostmaster (" >> "$FilePath"
	echo "				$NumeroSerie	; Serial number to a slave detects changes (32-bit natural)" >> "$FilePath"
	echo "				$default_ttl		; Time for slave refreshes" >> "$FilePath"
	# valor 'retry' del registro SOA recomendable según zonemaster.net: 3600
	echo "				1h		; Time for slave to retry in case of query failure" >> "$FilePath"
	echo "				4w		; Time for slave to expiry zone in case of query failure" >> "$FilePath"
	echo "				$default_ttl		; TTL for NXDOMAIN answers (time to live negative results)" >> "$FilePath"
	echo "				)" >> "$FilePath"
	if [ $ReplicateOldDefault -eq 1 ] ; then
		MkfilePP "${data_path}/common-soa" :bind ug=rwX,o=rX g+s
		echo "; This is only an automatic replica of $FilePath to not break INCLUDE references from old zone files." > "${data_path}/common-soa"
		echo "" >> "${data_path}/common-soa"
		cat "$FilePath" >> "${data_path}/common-soa"
	fi
}

SubdominioNombreInvertido ()
# Sintaxis como funcion: $(SubdominioNombreInvertido "$DominioOriginalEntero")
# "www.example.com" lo devuelve como "com.example.www"
{
	# Variables locales
	local QuedanPalabras="$1"
	local Valor=""
	
	if [ "$QuedanPalabras" != "" ] ; then
		QuedanPalabras="$(echo "$1" | tr -s "." " ")"
		while [ "$QuedanPalabras" != "" ] ; do
			if [ "$Valor" != "" ] ; then Valor=".$Valor" ; fi
			Valor="$(RetornaParaula () { echo $1; }; RetornaParaula $QuedanPalabras)$Valor"
			QuedanPalabras="$(RetornaParaula () { shift ; echo $*; }; RetornaParaula $QuedanPalabras)"
		done
	fi
	echo $Valor
}

DireccionZona ()
# Devuelve (echo) el valor de $ORIGIN para el fichero especificado
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
}

ZonaDireccion ()
# Devuelve (echo) la ruta de fichero que tiene el $ORIGIN especificado
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
}

Service_show ()
{
	echo "- Puede revisar la configuración del asistente en el fichero:"
	echo "$SystemConfigFile"
	echo "- Puede revisar las configuraciones generales de BIND en los ficheros de:"
	echo "${GesdonisConfigsPath}/"
	echo "- Todos los datos del servicio estan en el arbol de directorios:"
	echo "${data_path}/"
	echo "- Las incidencias son reportadas como \"named\" en:"
	echo "/var/log/daemon.log"
}

Service_ver ()
{
	Service_show "$@"
	return $?
}

Service_start ()
{
	local CurWait=0
	local StatusCode=0
	RegenerarSOA
	eval $(RecommendedInvocation start)
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	CurWait=3
	while [ $CurWait -gt 0 ] && [ "$(netstat -tulnp | tr -s '\t' ' ' | grep -e ':953 .* LISTEN ')" = "" ] ; do
		sleep 1
		CurWait=$((CurWait - 1))
	done
	rndc flush
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Service_iniciar ()
{
	Service_start "$@"
	return $?
}

Service_stop ()
{
	local StatusCode=0
	eval $(RecommendedInvocation stop)
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Service_parar ()
{
	Service_stop "$@"
	return $?
}

Service_restart ()
{
	local SlowRestart="$1"
	local LastStatus=0
	local StatusCode=0
	if [ "$SlowRestart" = "1" ] ; then
		eval $(RecommendedInvocation stop)
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		sleep 1
		eval $(RecommendedInvocation start)
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval $(RecommendedInvocation restart)
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Service_reiniciar ()
{
	Service_restart "$@"
	return $?
}

Service_flush ()
{
	local LastStatus=0
	local StatusCode=0

	rndc flush
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		echo "Cache DNS vaciada."
	fi
	return $StatusCode
}

Service_reload ()
# Syntax as a sentence: Service_reload [options]
# Notas:
#	- En cuanto a las zonas, sólo carga los cambios correctos;
#	  Si una zona ya estaba cargada y la nueva versión es incorrecta, queda la vieja cargada.
#	--nosoa evita que se regenere el common-soa, útil cuando se trata de un servidor espejo.
#	--novistas evita que se regeneren las referencias a las zonas en las vistas.
{
	local LasVistas=""
	local SlowRestart=0
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "--novistas" ] || [ "$2" = "--novistas" ] || [ "$1" = "--nosoa" ] || [ "$2" = "--nosoa" ] ; then
		SlowRestart=1
	fi
	if [ "$*" = "" ] ; then
		LasVistas="$(ls -1p "${data_path}/views/" | grep -e "/$" | cut -f 1 -d "/")"
		if [ "$LasVistas" != "" ] ; then
			Service reload $LasVistas
			return 0
		fi
	fi
	if [ "$1" != "--nosoa" ] && [ "$2" != "--nosoa" ] && [ "$3" != "--nosoa" ] ; then
		RegenerarSOA
	fi
	if [ "$*" != "" ] && [ "$1" != "--novistas" ] && [ "$2" != "--novistas" ] && [ "$3" != "--novistas" ] ; then
		for VistaActual in "$@" ; do
			if [ "$VistaActual" != "--nosoa" ] ; then
				ReferirZonasVista "$VistaActual"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	fi
	if [ $StatusCode -eq 0 ] ; then
#		if ! Is_Executable service ; then
#			invoke-rc.d bind9 reload
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		else
#			service bind9 reload
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		fi
# Esto del reload realmente no actualiza muchas cosas.
		Service restart $SlowRestart
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

FileInclusions ()

# IN DEVELOPMENT STAGE

# Syntax as a function: "$(FileInclusions $BaseFile)"
# Description: Returns (stdout) specified file and recursively included ones, one per line.
{
	local BaseFile="$1"
	echo "$BaseFile"
}

EditFile ()
# Syntax as a sentence: EditFile "$EditableFiles"
# NOTES:
#	- Supports either BIND9 files or template specification.
{
	local EditableFiles="$1"
	local FileToEdit=''
	local TemplateToEdit=''
	local FilesNr=0
	local FileNr=0
	local LastStatus=0
	local StatusCode=0
	
	FilesNr=$(printf '%s\n' "$EditableFiles" | wc -l)
	if [ $FilesNr -gt 1 ] ; then
		printf '%s\n' "Elija o inscriba el fichero a editar:"
		IFS="$(printf '\n\b')" ; for FileToEdit in $EditableFiles ; do unset IFS
			FileNr=$((FileNr + 1))
			printf '%s\n' "[${FileNr}] $FileToEdit"
		done
		read FileToEdit
		if Is_IntegerNr "$FileToEdit" && [ $FileToEdit -ge 1 ] && [ $FileToEdit -le $FilesNr ]  && [ "$(printf '%s' "$EditableFiles" | grep -e "^${FileToEdit}$")" = "" ] ; then
			FileToEdit="$(printf '%s\n' "$EditableFiles" | head -n $FileToEdit | tail -n 1 )"
		fi
	else
		FileToEdit="$EditableFiles"
	fi
	if [ -f "$FileToEdit" ] ; then
		if [ "$(printf '%s' "$FileToEdit" | grep -e '@tpl$')" != "" ] ; then
			TemplateToEdit="$FileToEdit"
			FileToEdit="$(printf '%s' | sed -e 's|@tpl$||')"
			if [ ! -f "$FileToEdit" ] ; then
				cp -a "$TemplateToEdit" "$FileToEdit"
				cat /dev/null > "$FileToEdit"
			fi
		else
			TemplateToEdit="${FileToEdit}@tpl"
			if [ ! -f "$TemplateToEdit" ] ; then cp -a "$FileToEdit" "$TemplateToEdit" ; fi
		fi
		echo "Se edita el fichero \"${TemplateToEdit}\""
		editor "$TemplateToEdit"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			FileContentReplaced "$TemplateToEdit" > "$FileToEdit"
		fi
	else
		printf '%s\n' "${sERROR}E: Fichero no encontrado:" 1>&2
		printf '%s\n' "${FileToEdit}${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CheckConfigsAndOrReload ()
# Syntax as a sentence: CheckConfigsAndOrReload [CheckBeforeReload] [ReloadServiceIfOkay] [MoreReloadOptions]
{
	local CheckBeforeReload="$1"
	local ReloadServiceIfOkay="$2"
	local MoreReloadOptions="$3"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CheckBeforeReload" != "n" ] && [ "$CheckBeforeReload" != "N" ] ; then
		printf '%s\n' "Se comprueba toda la configuracion..."
		CheckBindConfig
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ReloadServiceIfOkay" = "" ] ; then
			echo "¿Recargar los datos al servicio de BIND?"
		fi
		ReloadServiceIfOkay="$(RespostaLletra "$ReloadServiceIfOkay")"
		if [ "$ReloadServiceIfOkay" = "s" ] || [ "$ReloadServiceIfOkay" = "y" ] ; then
			Service reload $MoreReloadOptions
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "E: No se puede recargar la configuración hasta que se corrijan los errores en los ficheros." 1>&2
	fi
	return $StatusCode
}

EditConfigAndCheckReload ()
# Syntax as a sentence: EditConfigAndCheckReload "$EditableFiles" [CheckBeforeReload] [ReloadServiceIfOkay] [MoreReloadOptions]
{
	local EditableFiles="$1"
	local CheckBeforeReload="$2"
	local ReloadServiceIfOkay="$3"
	local MoreReloadOptions="$4"
	local LastStatus=0
	local StatusCode=0
	
	EditFile "$(FileInclusions "$EditableFiles")"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		CheckConfigsAndOrReload "$CheckBeforeReload" "$ReloadServiceIfOkay" "$MoreReloadOptions"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CheckZoneAndOrReload ()
# Syntax as a sentence: CheckZoneAndOrReload $ZoneName $Filename [CheckBeforeReload] [ReloadServiceIfOkay] [MoreReloadOptions]
{
	local ZoneName="$1"
	local Filename="$2"  # Full path
	local CheckBeforeReload="$3"
	local ReloadServiceIfOkay="$4"
	local MoreReloadOptions="$5"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CheckBeforeReload" != "n" ] && [ "$CheckBeforeReload" != "N" ] ; then
		printf '%s\n' "Se comprueba toda la configuracion..."
#		named-compilezone -i local -k fail -o /dev/null "${ZoneName}" "$Filename"
		named-compilezone -i full -k fail -M fail -o /dev/null "${ZoneName}" "$Filename"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ReloadServiceIfOkay" = "" ] ; then
			echo "¿Recargar los datos al servicio de BIND?"
		fi
		ReloadServiceIfOkay="$(RespostaLletra "$ReloadServiceIfOkay")"
		if [ "$ReloadServiceIfOkay" = "s" ] || [ "$ReloadServiceIfOkay" = "y" ] ; then
			Service reload $MoreReloadOptions
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		echo "E: No se puede recargar la configuración hasta que se corrijan los errores en los ficheros." 1>&2
	fi
	return $StatusCode
}

EditZoneAndCheckReload ()
# Syntax as a sentence: EditZoneAndCheckReload $ZoneName "$EditableFiles" [CheckBeforeReload] [ReloadServiceIfOkay] [MoreReloadOptions]
{
	local ZoneName="$1"
	local EditableFiles="$2"  # One full path per line. Check will be performed from first one.
	local CheckBeforeReload="$3"
	local ReloadServiceIfOkay="$4"
	local MoreReloadOptions="$5"
	local LastStatus=0
	local StatusCode=0
	
	EditFile "$(FileInclusions "$EditableFiles")"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		CheckZoneAndOrReload "$ZoneName" "$(printf '%s\n' "$EditableFiles" | head -n 1)" "$CheckBeforeReload" "$ReloadServiceIfOkay" "$MoreReloadOptions"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

View_show ()
{
	local LastStatus=0
	local StatusCode=0
	local VistaActual=""
	local LasZonas=""
	local ZonaActual=""
	local F1Vista=""
	local NrVista=0

	F1Vista="$(mktemp)"
	for VistaActual in "$@" ; do
		NrVista=$(($NrVista + 1))
		if [ $NrVista -gt 1 ] ; then echo "" ; fi
#		ClausulaEnFichero "/srv/named/views/$VistaActual/acl" "acl" "" "{" "}"
#		ClausulaEnFichero "/srv/named/views/$VistaActual/view" "view" "" "{" "}"
		echo "Zonas de la vista \"$VistaActual\" :"
		ls -1p "${data_path}/views/${VistaActual}/master/" | grep -ve "/$"
		ls -1p "${data_path}/views/${VistaActual}/slave/" | grep -ve "/$"
	done
	rm "$F1Vista"
	if [ "$*" = "" ] ; then
		echo "E: Hay que especificar la vista a informar" 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

View_ver ()
{
	View_show "$@"
	return $?
}

View_list ()
{
	local LastStatus=0
	local StatusCode=0
	local VistaActual=""
	local LasVistas=""

	ls -1p "${data_path}/views/" | grep -e "/$" | cut -f 1 -d "/"
	return $StatusCode
}

View_lista ()
{
	View_list "$@"
	return $?
}

View_duplicate ()
{
	local VistaOrigen="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local VistaDestino="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local IncluirZonas="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local EditarFicheros="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ActualizarServicio="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$VistaOrigen" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		VistaOrigen="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$VistaDestino" 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		VistaDestino="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$IncluirZonas" = "" ] ; then
			echo "¿Copiar tambien las zonas de la vista \"$VistaOrigen\"?"
		fi
		IncluirZonas="$(RespostaLletra "$IncluirZonas")"
		cp -ar "${data_path}/views/$VistaOrigen" "${data_path}/views/$VistaDestino"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$IncluirZonas" != "s" ] && [ "$IncluirZonas" != "y" ] ; then
			rm -fr "${data_path}/views/$VistaDestino/master"
			MkdirPP "${data_path}/views/$VistaDestino/master" :bind ug=rwX,o=rX g+s
			rm -fr "${data_path}/views/$VistaDestino/slave"
			MkdirPP "${data_path}/views/$VistaDestino/slave" :bind ug=rwX,o=rX g+s
			echo "Se ha copiado la vista sin zonas."
		else
			echo "Se ha copiado la vista con sus zonas."
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "" >> "${GesdonisConfigsPath}/named.conf.views"
			echo "include \"${data_path}/views/$VistaDestino/acl\";" >> "${GesdonisConfigsPath}/named.conf.views"
			echo "include \"${data_path}/views/$VistaDestino/view\";" >> "${GesdonisConfigsPath}/named.conf.views"
			echo "Al fichero \"${data_path}/views/$VistaDestino/acl\" hay que cambiarle el nombre del acl{} y diferenciarle la red."
			echo "En el fichero \"${data_path}/views/$VistaDestino/view\" hay que poner el nombre de nuevo ACL en match-clients{}."
			echo "${data_path}/views/$VistaDestino/view"
			if [ "$EditarFicheros" = "" ] ; then
				echo "¿Editar ahora la especificación de red ACL y la configuración de VIEW?"
			fi
			EditarFicheros="$(RespostaLletra "$EditarFicheros")"
			if [ "$EditarFicheros" = "s" ] || [ "$EditarFicheros" = "y" ] ; then
#				echo "Se edita el fichero \"${data_path}/views/$VistaDestino/acl\""
##				nano "${data_path}/views/$VistaDestino/acl"
#				editor "${data_path}/views/$VistaDestino/acl"
				EditFile "${data_path}/views/$VistaDestino/acl" n n
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				echo "Se edita el fichero-cabecera \"${data_path}/views/$VistaDestino/view.header\""
##				nano "${data_path}/views/$VistaDestino/view.header"
#				editor "${data_path}/views/$VistaDestino/view.header"
				EditFile "${data_path}/views/$VistaDestino/view.header" n n
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				ReferirZonasVista "$VistaDestino"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
#				echo "Se comprueba toda la configuracion..."
#				CheckBindConfig
				CheckConfigsAndOrReload y '' --novistas
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
#			if [ $StatusCode -eq 0 ] ; then
#				if [ "$ActualizarServicio" = "" ] ; then
#					echo "¿Recargar los datos al servicio de BIND?"
#				fi
#				ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
#				if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
#					Service reload --novistas
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#			else
#				echo "E: No se puede recargar la configuración hasta que se corrijan los errores en acl/view." 1>&2
#			fi
		fi
	fi
	return $StatusCode
}

View_duplicar ()
{
	View_duplicate "$@"
	return $?
}

View_delete ()
{
	local LaVista="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local Confirmacion="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ActualizarServicio="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Confirmacion" = "" ] ; then
			echo "¿Seguro que quiere eliminar todos los datos de \"$LaVista\"?"
		fi
		Confirmacion="$(RespostaLletra "$Confirmacion")"
		if [ "$Confirmacion" = "s" ] || [ "$Confirmacion" = "y" ] ; then
			EliminarLineasFichero "${GesdonisConfigsPath}/named.conf.views" "${data_path}/views/$LaVista/"
			rm -r "${data_path}/views/$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$ActualizarServicio" = "" ] ; then
					echo "¿Recargar los datos al servicio de BIND?"
				fi
				ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
				if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
					Service reload --novistas
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo "No se ha eliminado ninguna vista."
		fi
	fi
	return $StatusCode
}

View_eliminar ()
{
	View_delete "$@"
	return $?
}

View_edit_acl ()
{
	local LaVista="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ActualizarServicio="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
#		echo "Se edita el fichero \"${data_path}/views/$LaVista/acl\""
##		nano "${data_path}/views/$LaVista/acl"
#		editor "${data_path}/views/$LaVista/acl"
#		echo "Se comprueba toda la configuracion..."
#		CheckBindConfig
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		if [ $StatusCode -eq 0 ] ; then
#			if [ "$ActualizarServicio" = "" ] ; then
#				echo "¿Recargar los datos al servicio de BIND?"
#			fi
#			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
#			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
#				Service reload --novistas
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			fi
#		else
#			echo "E: No se puede recargar la configuración hasta que se corrijan los errores en ACL." 1>&2
#		fi
		EditConfigAndCheckReload "${data_path}/views/$LaVista/acl" y '' --novistas
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

View_edit_config ()
{
	local LaVista="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ActualizarServicio="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		echo "Cabecera de vista"
#		echo "Se edita el fichero-cabecera \"${data_path}/views/$LaVista/view\""
#		nano "${data_path}/views/$LaVista/view.header"
#		editor "${data_path}/views/$LaVista/view.header"
		EditFile "$(FileInclusions "${data_path}/views/$LaVista/view.header")" n n
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ReferirZonasVista "$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
#			echo "Se comprueba toda la configuracion..."
#			CheckBindConfig
			CheckConfigsAndOrReload y '' --novistas
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
#		if [ $StatusCode -eq 0 ] ; then
#			if [ "$ActualizarServicio" = "" ] ; then
#				echo "¿Recargar los datos al servicio de BIND?"
#			fi
#			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
#			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
#				Service reload --novistas
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			fi
#		else
#			echo "E: No se puede recargar la configuración hasta que se corrijan los errores en VIEW." 1>&2
#		fi
	fi
	return $StatusCode
}

View_edit_configuration ()
{
	View_edit_config "$@"
	return $?
}

View_edit_configuracion ()
{
	View_edit_config "$@"
	return $?
}

View_edit_view ()
{
	View_edit_config "$@"
	return $?
}

View_edit ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Acciones para vista editar:"
			echo ""
			echo "	vista edit acl [nombrevista]	(definir una red)"
			echo "	vista edit config [nombrevista]	(configurar una red)"
			;;
		* )
			View_edit_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

View_editar ()
{
	View_edit "$@"
	return $?
}

Ptr_show ()
{
	local LaVista="$1"
	local LaZona="$2"
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaPtrNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "${data_path}/views/$LaVista/master/$LaZona" ] ; then
			named-compilezone -i local -k warn -s full -o - ${LaZona} "${data_path}/views/$LaVista/master/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$"
		fi
		if [ -f "${data_path}/views/$LaVista/slave/$LaZona" ] ; then
			named-compilezone -i local -k warn -s full -o - ${LaZona} "${data_path}/views/$LaVista/slave/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$"
		fi
	fi
	return $StatusCode
}

Ptr_edit ()
{
	local LaVista="$1"
	local LaZona="$2"
	local ActualizarServicio="$3"
	local FZona=''
	local FPlantillaZona=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaPtrNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		if [ ! -f "$FZona" ] ; then FZona="${data_path}/views/$LaVista/slave/$LaZona" ; fi
#		FPlantillaZona="${FZona}@tpl"
#		if [ -f "$FPlantillaZona" ] ; then
#			echo "Se edita el fichero \"$FPlantillaZona\""
#			editor "$FPlantillaZona"
#			cat "$FPlantillaZona" > "$FZona"
#		else
#			echo "Se edita el fichero \"$FZona\""
##			nano "$FZona"
#			editor "$FZona"
#		fi
#		echo "Se comprueba la hoja..."
#		#named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$"
#		named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona"
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		if [ $LastStatus -eq 0 ] ; then
#			if [ "$ActualizarServicio" = "" ] ; then
#				echo "¿Recargar los datos al servicio de BIND?"
#			fi
#			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
#			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
#				ReferirZonasVista "$LaVista"
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				if [ $LastStatus -eq 0 ] ; then
#					Service reload --novistas
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#			fi
#		else
#			echo "W: No se puede recargar zona hasta que se corrijan los errores en la hoja." 1>&2
#		fi
		EditZoneAndCheckReload "$LaZona" "$FZona" y '' --novistas
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Ptr_create ()
{
	local LaVista="$1"
	local LaZona="$2"
	local EditarFicheros="$3"
	local ActualizarServicio="$4"
	local FZona=""
	local Cur_Authorityserver=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaPtrNoEspecificada "$LaVista" "$LaZona" 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	Cur_Authorityserver="$(echo TrimAndSingle $default_authorityserver | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	Cur_Authorityserver="$(printf '%s\n' "$Cur_Authorityserver" | cut -f 1 -d ' ')"
	if [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.')" != "" ] && [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.$')" = "" ] && [ "$(EsIP "$default_authorityserver")" != "1" ] ; then
		Cur_Authorityserver="${Cur_Authorityserver}."
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		MkfilePP "$FZona" :bind ug=rwX,o=rX g+s
		if [ "$(printf '%s' "$EditarFicheros" | tr -s '\t' ' ' | grep -e ' ')" != "" ] ; then NewRecord="$EditarFicheros" ; fi
		cat /dev/null > "$FZona"
		if [ "$NewRecord" = "" ] ; then
#			echo "\$TTL $default_ttl	; Default time-to-live for zone" >> "$FZona"
			echo "\$ORIGIN ${LaZona}.		; Base value for non FQDN names" >> "$FZona"
			echo "\$INCLUDE ${data_path}/common-soa	; Common authority server and serial number" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	NS	$Cur_Authorityserver	; Example (but required RR) to give authority name server. Cannot point to a CNAME record." >> "$FZona"
			echo ";@		IN	NS	dns.${LaZona}.	; Second example because for public domains are required at least 2 NS Resource Records" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	PTR	host.example.net.	; Example to use zone origin as IP." >> "$FZona"
			echo "" >> "$FZona"
			echo "" >> "$FZona"
			echo "" >> "$FZona"
			echo ";$LaZona		IN	PTR	host.example.net.	; Example to use explicit ARPA address as IP." >> "$FZona"
			echo ";1		IN	PTR	host1.example.net.	; Example .1 for a ${ParO}example${ParC} \"0.168.192.in-addr.arpa.\" zone to deploy one or more 192.168.1.x addresses" >> "$FZona"
			echo "" >> "$FZona"
			echo "; Semicolons \";\" disable rest of lines (as in examples)" >> "$FZona"
			echo "Se ha creado una hoja DNS con entradas RR de ejemplo:"
			echo "$FZona"
			
			if [ "$EditarFicheros" = "" ] ; then
				echo "Es importante cambiar el ejemplo por un valor productivo."
				echo "¿Editar ahora la nueva hoja de zona?"
			fi
			EditarFicheros="$(RespostaLletra "$EditarFicheros")"
			if [ "$EditarFicheros" = "s" ] || [ "$EditarFicheros" = "y" ] ; then
#				echo "Se edita el fichero \"$FZona\""
#				nano "$FZona"
#				editor "$FZona"
				EditFile "$FZona"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
#			echo "\$TTL $default_ttl				; Default time-to-live for zone" >> "$FZona"
			echo "\$ORIGIN ${LaZona}.		; Base value for non FQDN names" >> "$FZona"
			echo "\$INCLUDE ${data_path}/common-soa	; Common authority server and serial number" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	NS	$Cur_Authorityserver	; Required RR to give authority name server. Cannot point to a CNAME record." >> "$FZona"
			echo ";@		IN	NS	dns.${LaZona}.	; Second example because for public domains are required at least 2 NS Resource Records" >> "$FZona"
			echo "" >> "$FZona"
			echo "Se crea una hoja DNS $FZona con la entrada:"
			echo "$NewRecord" | tee -a "$FZona"
		fi
		if [ $LastStatus -eq 0 ] ; then
#			echo "Se comprueba la hoja..."
#			#named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$"
#			named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona"
			CheckZoneAndOrReload "$LaZona" "$FZona" y n
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			ReferirZonasVista "$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
#			if [ "$ActualizarServicio" = "" ] ; then
#				echo "¿Recargar los datos al servicio de BIND?"
#			fi
#			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
#			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
#				Service reload --novistas
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			fi
#		else
#			echo "W: No se puede cargar la nueva zona hasta que se corrijan los errores en la hoja." 1>&2
			CheckZoneAndOrReload "$LaZona" "$FZona" y '' --novistas
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Ptr_delete ()
{
	local LaVista="$1"
	local LaZona="$2"
	local Confirmacion="$3"
	local ActualizarServicio="$4"
	local FZona=''
	local FPlantillaZona=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaPtrNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		if [ ! -f "$FZona" ] ; then FZona="${data_path}/views/$LaVista/slave/$LaZona" ; fi
		FPlantillaZona="${FZona}@tpl"
		if [ "$Confirmacion" = "" ] ; then
			echo "¿Seguro que quiere eliminar la hoja de \"$LaZona\"?"
		fi
		Confirmacion="$(RespostaLletra "$Confirmacion")"
		if [ "$Confirmacion" = "s" ] || [ "$Confirmacion" = "y" ] ; then
			echo "Se borra el fichero \"$FZona\""
			rm "$FZona"
			if [ -f "$FPlantillaZona" ] ; then
				echo "Se borra el fichero \"$FPlantillaZona\""
				rm "$FPlantillaZona"
			fi
			ReferirZonasVista "$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$ActualizarServicio" = "" ] ; then
					echo "¿Recargar los datos al servicio de BIND?"
				fi
				ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
				if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
					Service reload --novistas
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

Ptr_list ()
{
	local LasVistas=""
	local VistaActual=""
	local NrVista=0
	local ZonasActuales=''
	local ZonaActual=""
	local PtrsZona=''
	local PtrActual=''
	local IpsZona=''
	local IpActualR=''
	local IpActual=''
	local NrActual=''
	local FqdnActual=''
	local LastStatus=0
	local StatusCode=0

	LasVistas="$(ls -1p "${data_path}/views/" | grep -e "/$" | cut -f 1 -d "/")"
	for VistaActual in $LasVistas ; do
		NrVista=$(($NrVista + 1))
		if [ $NrVista -gt 1 ] ; then echo "" ; fi
		echo "===== VISTA $VistaActual ====="
		ZonasActuales="$(ls -1p "${data_path}/views/${VistaActual}"/*/ | grep -ve "/" -ve '^$' 2>/dev/null)"
		IFS="$(printf '\n\b')" ; for ZonaActual in $ZonasActuales ; do unset IFS
			IpsZona=''
			if [ -f "${data_path}/views/${VistaActual}/master/$ZonaActual" ] ; then
				PtrsZona="$(named-compilezone -i local -k warn -s full -o - "$ZonaActual" "${data_path}/views/${VistaActual}/master/$ZonaActual" 2>/dev/null | tr -s '\t' ' ' | grep -ie ' IN PTR ')"
			else
				PtrsZona="$(named-compilezone -i local -k warn -s full -o - "$ZonaActual" "${data_path}/views/${VistaActual}/slave/$ZonaActual" 2>/dev/null | tr -s '\t' ' ' | grep -ie ' IN PTR ')"
			fi
			IFS="$(printf '\n\b')" ; for PtrActual in $PtrsZona ; do unset IFS
				IpActual=''
				IpActualR="$(printf '%s' "$PtrActual" | cut -f 1 -d ' ' | sed -e 's|.in-addr.arpa.*||')"
				for NrActual in $(printf '%s' "$IpActualR" | tr '.' ' ') ; do
					if [ "$IpActual" != "" ] ; then IpActual=".${IpActual}" ; fi
					IpActual="${NrActual}${IpActual}"
				done
				FqdnActual="$(printf '%s' "$PtrActual" | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||')"
				if [ "$IpsZona" != "" ] ; then IpsZona="$IpsZona " ; fi
				IpsZona="${IpsZona}${IpActual}"
			done
			if [ "$IpsZona" != "" ] ; then
				printf '%s\n' "$ZonaActual ${ParO}${IpsZona}${ParC}"
			fi
		done
	done
	return $StatusCode
}

Ptrs_list ()
{
	Ptr_list "$@"
	return $?
}

Ptrs_show ()
{
	Zones_show "$@"
	return $?
}

Zone_show ()
{
	local LaVista="$1"
	local LaZona="$2"
	local Options="$3"
	local LaZona_Regexp=''
	local LaZona_ComoRaiz=''
	local LaZona_ComoFinal=''
	local Report=''
	local CurLine=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "${data_path}/views/$LaVista/master/$LaZona" ] ; then
#			Report="$(named-compilezone -i local -k warn -M fail -s full -o - ${LaZona} "${data_path}/views/$LaVista/master/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$")"
#			Report="$(named-compilezone -i full -k warn -M fail -s relative -o - ${LaZona} "${data_path}/views/$LaVista/master/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$")"
			Report="$(named-compilezone -i full -k warn -M fail -s full -o - ${LaZona} "${data_path}/views/$LaVista/master/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$")"
		else
			if [ -f "${data_path}/views/$LaVista/slave/$LaZona" ] ; then
#				Report="$(named-compilezone -i local -k warn -s full -o - ${LaZona} "${data_path}/views/$LaVista/slave/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$"=)"
#				Report="$(named-compilezone -i full -k warn -M fail -s relative -o - ${LaZona} "${data_path}/views/$LaVista/slave/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$"=)"
				Report="$(named-compilezone -i full -k warn -M fail -s full -o - ${LaZona} "${data_path}/views/$LaVista/slave/$LaZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$" | grep -ive "^OK$"=)"
			fi
		fi
		if [ "$Report" != "" ] ; then
			if [ "$(printf '%s' " $Options " | grep -e ' --raw ')" ] ; then
				printf '%s\n' "$Report"
			else
				LaZona_ComoRaiz="@$(printf '%s' "$LaZona" | sed -e 's|.| |g')"
				LaZona_ComoFinal="$(printf '%s' "$LaZona" | sed -e 's|.| |g')  "
				LaZona_Regexp="$(printf '%s' "$LaZona" | sed -e 's|\.|\\.|g')"
				Report="$(printf '%s\n' "$Report" | sed -e "s|^${LaZona_Regexp}\.\t|${LaZona_ComoRaiz}\t|g" | sed -e "s|\.${LaZona_Regexp}\.\t|${LaZona_ComoFinal}\t|g" | sed -e "s|\.${LaZona_Regexp}\.$||g")"
				Report="$(printf '%s\n' "$Report" | sed -e "s| \.$| ${LaZona}.|g" | sed -e "s|\t\.$|\t${LaZona}.|g")"
				if [ "$(printf '%s' " $Options " | grep -e ' --portable ')" ] ; then
					Report="$(printf '%s\n' "$Report" | tr -s '\t ' | sed -e 's| \t|\t|g' -e 's|\t |\t|g')"
				fi
				IFS="$(printf '\n\b')" ; for CurLine in $Report ; do unset IFS
					if [ "$(printf '%s' "$CurLine" | grep -e '"')" = "" ] ; then
						printf '%s\n' "$CurLine" | cut -f 1 -d ';' | sed -e 's|\t$||g' -e 's| $||g'
					else
						if [ "$(printf '%s' "$CurLine" | grep -e '".*".*;')" != "" ] ; then
							printf '%s\n' "$CurLine" | cut -f 1-2 -d '"' | sed -e 's|$|"|'
						else
							printf '%s\n' "$CurLine"
						fi
					fi
				done
			fi
		fi
	fi
	return $StatusCode
}

Zone_export ()
{
	local LaVista="$1"
	local LaZona="$2"
	local Options="$3"
	
	Zone_show "$LaVista" "$LaZona" --portable
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

ViewZone_SOA ()
# Description: Returns (stdout) SOA RR for a zone in a view; query format.
{
	local LaVista="$1"
	local LaZona="$2"
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LaVista" = "" ] || [ "$LaZona" = "" ] ; then
		printf '%s\n' "${sERROR}E: Hay que especificar vista y zona para la funcion ViewZone_SOA().${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Value="$(Zone_show "$LaVista" "$LaZona")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Value="$(printf '%s' "$Value" | tr -s '\t' ' ' | grep -e '^@.* IN SOA ..' -e "^${LaZona}.* IN SOA .." | head -n 1)"
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
	return $StatusCode
}

Zone_ver ()
{
	Zone_show "$@"
	return $?
}

Zone_edit ()
{
	local LaVista="$1"
	local LaZona="$2"
	local ActualizarServicio="$3"
	local FZona=''
	local FPlantillaZona=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		if [ ! -f "$FZona" ] ; then FZona="${data_path}/views/$LaVista/slave/$LaZona" ; fi
#		FPlantillaZona="${FZona}@tpl"
#		if [ -f "$FPlantillaZona" ] ; then
#			echo "Se edita el fichero \"$FPlantillaZona\""
#			editor "$FPlantillaZona"
#			cat "$FPlantillaZona" > "$FZona"
#		else
#			echo "Se edita el fichero \"$FZona\""
##			nano "$FZona"
#			editor "$FZona"
#		fi
		EditFile "$(FileInclusions "$FZona")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
#			echo "Se comprueba la hoja..."
#			named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$"
#			named-compilezone -i full -k fail -M fail -o /dev/null ${LaZona} "$FZona"
			CheckZoneAndOrReload "$LaZona" "$FZona" y n
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			if [ "$ActualizarServicio" = "" ] ; then
				echo "¿Recargar los datos al servicio de BIND?"
			fi
			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
				ReferirZonasVista "$LaVista"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then
					Service reload --novistas --nosoa
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -eq 0 ] ; then
						SoaNameServer="$(ViewZone_SOA "$LaVista" "$LaZona" | sed -e 's|.* IN SOA ||g' | cut -f 1 -d ' ')"
						# If no SOA detected, it will regenerate default one anyway
						RegenerarSOA "$SoaNameServer"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						Service reload --novistas --nosoa
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		else
			echo "W: No se puede recargar zona hasta que se corrijan los errores en la hoja." 1>&2
		fi
	fi
	return $StatusCode
}

Zone_create ()
{
	local LaVista="$1"
	local LaZona="$2"
	local EditarFicheros="$3"
	local ActualizarServicio="$4"
	local NewRecord=''
	local FZona=""
	local Cur_Authorityserver=''
	
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaNoEspecificada "$LaVista" "$LaZona" 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	Cur_Authorityserver="$(echo TrimAndSingle $default_authorityserver | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	Cur_Authorityserver="$(printf '%s\n' "$Cur_Authorityserver" | cut -f 1 -d ' ')"
	if [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.')" != "" ] && [ "$(printf '%s\n' "$Cur_Authorityserver" | grep -e '\.$')" = "" ] && [ "$(EsIP "$default_authorityserver")" != "1" ] ; then
		Cur_Authorityserver="${Cur_Authorityserver}."
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		MkfilePP "$FZona" :bind ug=rwX,o=rX g+s
		if [ "$(printf '%s' "$EditarFicheros" | tr -s '\t' ' ' | grep -e ' ')" != "" ] ; then NewRecord="$EditarFicheros" ; fi
		if [ "$NewRecord" = "" ] ; then
#			echo "\$TTL $default_ttl				; Default time-to-live for zone" >> "$FZona"
			echo "\$ORIGIN ${LaZona}.		; Base value for non FQDN names" >> "$FZona"
			echo "\$INCLUDE ${data_path}/common-soa	; Common authority server and serial number" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	NS	$Cur_Authorityserver	; Example (but required RR) to give authority name server. Cannot point to a CNAME record." >> "$FZona"
			echo ";@		IN	NS	dns.${LaZona}.	; Second example because for public domains are required at least 2 NS Resource Records" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	MX 10	smtp							; Example for first MX (target should be type A). Preference (here 10) is a 16-bit natural. Cannot point to a CNAME record." >> "$FZona"
			echo "@		IN	MX 20	mta.example.net.					; Example for second MX for same user@${LaZona} destinations. Cannot point to a CNAME record." >> "$FZona"
			echo "@		IN	SPF	\"v=spf1 mx a:www.${LaZona} a:smtp.${LaZona}\"		; Example for SPF (SMTP authorized servers). -all ends testing and enables rejecting. ~all ends testing and only marks messages." >> "$FZona"
			echo "@		IN	SPF	\"v=spf1 include:example.com -all exp=spffail.${LaZona}\"	; Example to include SPF entries from another domain. RoundRobin changes this RR order." >> "$FZona"
			echo "spffail		IN	TXT	\"Not allowed to send mail for domain\"			; Message for SPF failure (exp parameter indicates this RR)." >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	TXT	\"Welcome to beautiful net\"				; Example for free text." >> "$FZona"
			echo "" >> "$FZona"
			echo "dns.${LaZona}.		IN	A	60.60.60.1	; Example with full name (same as simple \"dns\" alias) but dot-terminated" >> "$FZona"
			echo "www		IN	A	80.80.80.81		; A example for www.${LaZona}" >> "$FZona"
			echo "www		IN	A	80.80.80.82		; Example for RoundRobin" >> "$FZona"
			echo "	IN		A	80.80.80.83		; Next RoundRobin example for same www" >> "$FZona"
			echo "smtp	90m	IN	A	40.40.40.1		; Example with specific TTL (TTL in seconds is 32-bit integer: 0-2147483647)" >> "$FZona"
			echo "" >> "$FZona"
			echo "pop3		IN	CNAME	smtp			; Example of alias to local name (target should be type A)" >> "$FZona"
			echo "imap		IN	CNAME	mail.example.net.	; Example of alias to external name" >> "$FZona"
			echo "" >> "$FZona"
			echo "" >> "$FZona"
			echo "\$ORIGIN desktops.${LaZona}.				; Example of new base value to define subdomain zone separately in the same file (inherites \$TTL, SOA, NS)" >> "$FZona"
			echo "joan		IN	A	20.20.20.1		; Example for joan.desktops.${LaZona}" >> "$FZona"
			echo "@		IN	A	20.20.20.100		; Example for desktops.${LaZona}" >> "$FZona"
			echo "" >> "$FZona"
			echo "\$ORIGIN servers.${LaZona}.				; Another example for subdomain zone" >> "$FZona"
# NS independentes no me funcionan.
#			echo "servers.${LaZona}.		IN	NS	dns.servers.${LaZona}.	; Example for independent authority name server" >> "$FZona"
#			echo "servers.${LaZona}.		IN	NS	$Cur_Authorityserver	; Second NS RR" >> "$FZona"
#			echo "dns		IN	A	60.60.60.2		; Example for dns.servers.${LaZona}" >> "$FZona"
			echo "files		IN	A	60.60.60.3		; Example for files.servers.${LaZona}" >> "$FZona"
			echo "" >> "$FZona"
			echo "; Is recommended to comment examples with a beginning semicolon \";\"" >> "$FZona"
			echo "Se ha creado una hoja DNS con entradas RR de ejemplo:"
			echo "$FZona"
			
			if [ "$EditarFicheros" = "" ] ; then
				echo "Es importante cambiar los ejemplos por entradas productivas."
				echo "¿Editar ahora la nueva hoja de zona?"
			fi
			EditarFicheros="$(RespostaLletra "$EditarFicheros")"
			if [ "$EditarFicheros" = "s" ] || [ "$EditarFicheros" = "y" ] ; then
				echo "Se edita el fichero \"$FZona\""
#				nano "$FZona"
#				editor "$FZona"
				EditFile "$FZona"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
#			echo "\$TTL $default_ttl				; Default time-to-live for zone" >> "$FZona"
			echo "\$ORIGIN ${LaZona}.		; Base value for non FQDN names" >> "$FZona"
			echo "\$INCLUDE ${data_path}/common-soa	; Common authority server and serial number" >> "$FZona"
			echo "" >> "$FZona"
			echo "@		IN	NS	$Cur_Authorityserver	; Required RR to give authority name server. Cannot point to a CNAME record." >> "$FZona"
			echo ";@		IN	NS	dns.${LaZona}.	; Second example because for public domains are required at least 2 NS Resource Records" >> "$FZona"
			echo "" >> "$FZona"
			echo "Se crea una hoja DNS $FZona con la entrada:"
			echo "$NewRecord" | tee -a "$FZona"
		fi
		if [ $LastStatus -eq 0 ] ; then
#			echo "Se comprueba la hoja..."
#			named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona" 2>&1 | grep -ive "^zone.* loaded serial " | grep -ive "^dump zone to .*done$"
#			named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona"
			CheckZoneAndOrReload "$LaZona" "$FZona" y n
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			ReferirZonasVista "$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			if [ "$ActualizarServicio" = "" ] ; then
				echo "¿Recargar los datos al servicio de BIND?"
			fi
			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
				Service reload --novistas --nosoa
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then
					SoaNameServer="$(ViewZone_SOA "$LaVista" "$LaZona" | sed -e 's|.* IN SOA ||g' | cut -f 1 -d ' ')"
					# If no SOA detected, it will regenerate default one anyway
					RegenerarSOA "$SoaNameServer"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Service reload --novistas --nosoa
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo "W: No se puede cargar la nueva zona hasta que se corrijan los errores en la hoja." 1>&2
		fi
	fi
	return $StatusCode
}

Zone_crear ()
{
	Zone_create "$@"
	return $?
}

Zone_clone ()
{
	local LaVistaOrigen="$1"
	local LaZonaOrigen="$2"
	local LaVistaDestino="$3"
	local LaZonaDestino="$4"
	local EditarFicheros="$5"
	local ActualizarServicio="$6"
	local SourceOrigin=''
	local TargetOrigin=''
	local NewRecord=''
	local FZona=""
	local Cur_Authorityserver=''
	
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$LaVistaOrigen" = "" ] || [ "$LaZonaOrigen" = "" ] || [ "$LaVistaDestino" = "" ] || [ "$LaZonaDestino" = "" ] ; then
			printf '%s\n' "${sERROR}E: Hay que especificar vista de origen, zona de origen, vista de destino y zona de destino.${fRESET}" 1>&2
			LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -f "${data_path}/views/$LaVistaOrigen/master/$LaZonaOrigen" ] ; then
		printf '%s\n' "${sERROR}E: Vista/zona de origen no encontrada.${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -d "${data_path}/views/$LaVistaDestino/master" ] ; then
		printf '%s\n' "${sERROR}E: Vista de destino no encontrada.${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ -f "${data_path}/views/$LaVistaDestino/master/$LaZonaDestino" ] ; then
		printf '%s\n' "${sERROR}E: Zona de destino ya existente.${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVistaDestino/master/$LaZonaDestino"
		cp -a "${data_path}/views/$LaVistaOrigen/master/${LaZonaOrigen}" "${FZona}"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ -f "${data_path}/views/$LaVistaOrigen/master/${LaZonaOrigen}@tpl" ] ; then
			cp -a "${data_path}/views/$LaVistaOrigen/master/${LaZonaOrigen}@tpl" "${FZona}@tpl"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		SourceOrigin="$(cat "${data_path}/views/$LaVistaOrigen/master/$LaZonaOrigen" | tr -s '\t' ' ' | grep -ie '^$ORIGIN ..' | head -n 1 | cut -f 2 -d ' ' | sed -e 's|\.$||g')"
		if [ "$SourceOrigin" != "" ] ; then
#			printf '%s\n' "\$ORIGIN ${LaZonaDestino}.		; Base value for non FQDN names" > "$FZona"
#			cat "${data_path}/views/$LaVistaOrigen/master/$LaZonaOrigen" | grep -ive "^\$ORIGIN ${SourceOrigin}\." >> "$FZona"
			sed -i -e "s|\$ORIGIN.*${SourceOrigin}\.|\$ORIGIN ${LaZonaDestino}.|gi" "${FZona}"
			if [ -f "${FZona}@tpl" ] ; then
				sed -i -e "s|\$ORIGIN.*${SourceOrigin}\.|\$ORIGIN ${LaZonaDestino}.|gi" "${FZona}@tpl"
			fi
		fi
		if [ "$EditarFicheros" = "" ] ; then
			echo "Es bueno revisar las entradas."
			echo "¿Editar ahora la nueva hoja de zona?"
		fi
		EditarFicheros="$(RespostaLletra "$EditarFicheros")"
		if [ "$EditarFicheros" = "s" ] || [ "$EditarFicheros" = "y" ] ; then
#			echo "Se edita el fichero \"$FZona\""
#			editor "$FZona"
			EditFile "$FZona"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
#			echo "Se comprueba la hoja..."
#			named-compilezone -i local -k fail -o /dev/null ${LaZonaDestino} "$FZona"
			CheckZoneAndOrReload "$LaZonaDestino" "$FZona" y n
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			ReferirZonasVista "$LaVistaDestino"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			if [ "$ActualizarServicio" = "" ] ; then
				echo "¿Recargar los datos al servicio de BIND?"
			fi
			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
				Service reload --novistas --nosoa
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then
					SoaNameServer="$(ViewZone_SOA "$LaVistaDestino" "$LaZonaDestino" | sed -e 's|.* IN SOA ||g' | cut -f 1 -d ' ')"
					# If no SOA detected, it will regenerate default one anyway
					RegenerarSOA "$SoaNameServer"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Service reload --novistas --nosoa
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo "W: No se puede cargar la nueva zona hasta que se corrijan los errores en la hoja." 1>&2
		fi
	fi
	return $StatusCode
}

Zone_insert ()
{
	local LaVista="$1"
	local LaZona="$2"
	local NewRecord="$3"
	local ActualizarServicio="$4"
	local FZona=""
	local EntryPart1=''
	local EntryPart2=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$NewRecord" = "" ] ; then
			printf '%s\n' "Escribe de forma compacta la nueva entrada DNS simple en la raiz (@)"
			printf '%s\n' "Ejemplo 1: @ IN A 192.168.1.23"
			printf '%s\n' "Ejemplo 2: @ IN MX 10 smtp.example.net."
			printf '%s\n' "Ejemplo 3: imap IN CNAME mail.example.net."
			read NewRecord
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		echo "Se añade esta entrada a la hoja DNS $FZona"
		echo "$NewRecord" | tee -a "$FZona"
		echo "Se comprueba la hoja..."
		named-compilezone -i local -k fail -o /dev/null ${LaZona} "$FZona"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -eq 0 ] ; then
			if [ "$ActualizarServicio" = "" ] ; then
				echo "¿Recargar los datos al servicio de BIND?"
			fi
			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
				Service reload --novistas --nosoa
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then
					SoaNameServer="$(ViewZone_SOA "$LaVista" "$LaZona" | sed -e 's|.* IN SOA ||g' | cut -f 1 -d ' ')"
					# If no SOA detected, it will regenerate default one anyway
					RegenerarSOA "$SoaNameServer"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Service reload --novistas --nosoa
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo "W: No se puede cargar la nueva zona hasta que se corrijan los errores en la hoja." 1>&2
		fi
	fi
	return $StatusCode
}

Zone_delete ()
{
	local LaVista="$1"
	local LaZona="$2"
	local Confirmacion="$3"
	local ActualizarServicio="$4"
	local FZona=''
	local FPlantillaZona=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		PreguntarVistaNoEspecificada "$LaVista" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaVista="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		PreguntarZonaNoEspecificada "$LaVista" "$LaZona" 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LaZona="$Respuesta"
	fi
	if [ $StatusCode -eq 0 ] ; then
		FZona="${data_path}/views/$LaVista/master/$LaZona"
		if [ ! -f "$FZona" ] ; then FZona="${data_path}/views/$LaVista/slave/$LaZona" ; fi
		FPlantillaZona="${FZona}@tpl"
		if [ "$Confirmacion" = "" ] ; then
			echo "¿Seguro que quiere eliminar la hoja de \"$LaZona\"?"
		fi
		Confirmacion="$(RespostaLletra "$Confirmacion")"
		if [ "$Confirmacion" = "s" ] || [ "$Confirmacion" = "y" ] ; then
			SoaNameServer="$(ViewZone_SOA "$LaVista" "$LaZona" | sed -e 's|.* IN SOA ||g' | cut -f 1 -d ' ')"
			echo "Se borra el fichero \"$FZona\""
			rm "$FZona"
			if [ -f "$FPlantillaZona" ] ; then
				echo "Se borra el fichero \"$FPlantillaZona\""
				rm "$FPlantillaZona"
			fi
			ReferirZonasVista "$LaVista"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$ActualizarServicio" = "" ] ; then
					echo "¿Recargar los datos al servicio de BIND?"
				fi
				ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
				if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
					Service reload --novistas --nosoa
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -eq 0 ] ; then
						# If no SOA detected, it will regenerate default one anyway
						RegenerarSOA "$SoaNameServer"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						Service reload --novistas --nosoa
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "${sGOODNORMAL}Hecho.${fRESET}" 1>&2
			fi
		fi
	fi
	return $StatusCode
}

Zone_eliminar ()
{
	Zone_delete "$@"
	return $?
}

Zone_editar ()
{
	Zone_edit "$@"
	return $?
}

Zones_show ()
{
	local StatusCode=0
	local VistaActual=""
	local ZonaActual=""
	local LasVistas=""
	local NrVista=0

	LasVistas="$(ls -1p "${data_path}/views/" | grep -e "/$" | cut -f 1 -d "/")"
	for VistaActual in $LasVistas ; do
		NrVista=$(($NrVista + 1))
		if [ $NrVista -gt 1 ] ; then echo "" ; fi
		echo "===== VISTA $VistaActual ====="
		for ZonaActual in "$@" ; do
			echo ""
			if [ -e "${data_path}/views/$VistaActual/slave/${ZonaActual}" ] || [ -e "${data_path}/views/$VistaActual/master/${ZonaActual}" ] ; then
				echo ";; ZONA $ZonaActual"
				echo ""
				Zone_show "$VistaActual" "$ZonaActual"
			fi
		done
	done
	return $StatusCode
}

Zones_ver ()
{
	Zones_show "$@"
	return $?
}

Address_check ()
{
	local LastStatus=0
	local StatusCode=0
	local NrPruebas=0
	local CurResultTxt=''
	
	for DireccionActual in "$@" ; do
		printf '%s' "$DireccionActual : "
		if [ "$(EsIP "$DireccionActual")" = "1" ] ; then
			CurResultTxt="$(dig +short @localhost -x $DireccionActual 2>&1)"
		else
			CurResultTxt="$(dig +short @localhost $DireccionActual 2>&1)"
		fi
		CurResultTxt="$(echo aaa$CurResultTxt | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
		printf '%s\n' "$CurResultTxt"
		NrPruebas=$(($NrPruebas + 1))
	done
	if [ $NrPruebas -eq 0 ] ; then
		echo "E: No se ha especificado ninguna direccion a comprobar" 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Address_analizar ()
{
	Address_check "$@"
	return $?
}

Service ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Acciones para service:"
			echo ""
			echo "	service show		(parametros generales)"
			echo "	service start	(iniciar BIND)"
			echo "	service stop		(parar BIND)"
			echo "	service flush	(vaciar memoria cache DNS)"
			echo "	service reload	(recargar configuracion y datos)"
			echo "	service restore	(borrar todo y dejar como el primer dia)"
#			echo "	service install	(Instalar BIND9)"
#			echo "	service uninstall	(Desinstalar BIND9)"
			;;
		* )
			ServiceName="bind9"
			Configuration_InitSystem
			Service_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

View_updateforwarders ()
{
	local CurrentView="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CurLine=''
	local BlankLineMask=''
	local NewContent=''
	local CurIP=''
	local ForwardersOpen=''
	local ForwardersClosed=''
	local BeginLine=''
	local EntriesLines=''
	local EndLine=''
	local DoneIPs=''
	local DoneIPsNr=0
	local LastStatus=0
	local StatusCode=0
	
	if [ -f "${data_path}/views/$CurrentView/view.header" ] && [ -f "${data_path}/views/$CurrentView/view" ] ; then
		if [ "$(EsIP $(OneWord () { printf '%s' $1; }; OneWord $*))" = "1" ] || [ "$(EsIP $(OneWord () { printf '%s' $2; }; OneWord $*))" = "1" ] ; then
			printf '%s\n' "Processing header-file \"${data_path}/views/$CurrentView/view.header\""
			BlankLineMask="$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')BLANK$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')blank$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')"
			IFS="$(printf '\n\b')" ; for CurLine in $(cat "${data_path}/views/$CurrentView/view.header" | sed -e "s|^$|${BlankLineMask}|g") ; do unset IFS
				if [ "$CurLine" = "$BlankLineMask" ] ; then CurLine="" ; fi
				if [ "$(printf '%s\n' "$CurLine" | grep -e 'forwarders.*{')" != "" ] ; then ForwardersOpen="$CurLine" ; fi
				if [ "$ForwardersOpen" != "" ] && [ "$ForwardersClosed" = "" ] ; then
					if [ "$(printf '%s\n' "$CurLine" | grep -e 'forwarders.*{')" != "" ] ; then
						# Open found: let's write whole clause from zero
						CurLine="$(printf '%s\n' "$CurLine" | sed -e 's|forwarders.*{|forwarders {\n|')"
						BeginLine="$(printf '%s\n' "$CurLine" | head -n 1)"
						printf '%s\n' "$BeginLine"
						NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$BeginLine")"
						CurLine="$(printf '%s\n' "$CurLine" | awk 'NR >= 2')"
						for CurIP in $* ; do
							if [ "$(EsIP "$CurIP")" = "1" ] ; then
								if [ "$(printf '%s\n' " $DoneIPs " | grep -e " $CurIP ")" = "" ] ; then
									DoneIPsNr=$(($DoneIPsNr + 1))
								fi
								DoneIPs="${DoneIPs} $CurIP"
								CurIP="$(printf '%s\n' "$CurIP" | sed -e 's|^|\t\t|')"
								printf '%s\n' "$CurIP"
								NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "${CurIP};")"
							fi
						done
						if [ "$(printf '%s\n' "$CurLine" | grep -e '}')" != "" ] ; then
							CurLine="$(printf '%s\n' "$CurLine" | sed -e 's|.*}|}\n|')"
							EndLine="$(printf '%s\n' "$CurLine" | head -n 1)"
							ForwardersClosed="$EndLine"
							printf '%s\n' "$EndLine"
							NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$EndLine")"
							CurLine="$(printf '%s\n' "$CurLine" | awk 'NR >= 2')"
							if [ "$CurLine" != "" ] ; then
								NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$CurLine")"
							fi
						fi
					else
						if [ "$(printf '%s\n' "$CurLine" | grep -e '}')" != "" ] ; then
							if [ "$(printf '%s\n' "$CurLine" | tr -s '\t' ' ' | grep -e '^ }')" != "" ] ; then
								# Respect clean indentation
								CurLine="$(printf '%s\n' "$CurLine" | sed -e 's|}|}\n|')"
							else
								CurLine="$(printf '%s\n' "$CurLine" | sed -e 's|.*}|}\n|')"
							fi
							EndLine="$(printf '%s\n' "$CurLine" | head -n 1)"
							ForwardersClosed="$EndLine"
							printf '%s\n' "$EndLine"
							NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$EndLine")"
							CurLine="$(printf '%s\n' "$CurLine" | awk 'NR >= 2')"
							if [ "$CurLine" != "" ] ; then
								NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$CurLine")"
							fi
						fi
					fi
				else
					NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$CurLine")"
				fi
			done
			if [ $DoneIPsNr -gt 0 ] ; then
				printf '%s' "$NewContent" > "${data_path}/views/$CurrentView/view.header"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					printf '%s\n' "$DoneIPsNr unique forwarders registered."
				fi
			else
				if [ "$ForwardersOpen" != "" ] ; then
					printf '%s\n' "E: No valid IPv4 addresses to register." 1>&2
					LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "E: forwarders{} clause not found in ${data_path}/views/$CurrentView/view.header" 1>&2
					LastStatus=60 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			printf '%s\n' "E: DNS IP addresses not specified." 1>&2
			printf '%s\n' "   Following view specification, DNS forwarders are expected." 1>&2
			LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			ReferirZonasVista "$CurrentView"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "Not regenerating view \"$CurrentView\""
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "Se comprueba toda la configuracion..."
			CheckBindConfig
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "Reloading files to BIND9 service."
			Service reload --novistas
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			echo "NOT reloading files to BIND9 service."
		fi
	else
		if [ "$CurrentView" != "" ] ; then
			printf '%s\n' "E: View \"$CurrentView\" files not found at ${data_path}/views" 1>&2
			printf '%s\n' "   updateforwarders must be followed by existing listening net specification ${ParO}view${ParC}" 1>&2
			printf '%s\n' "   Next to this, DNS forwarders." 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "E: No view and forwarders specified." 1>&2
			printf '%s\n' "   updateforwarders must be followed by existing listening net specification ${ParO}view${ParC}" 1>&2
			printf '%s\n' "   Next to this, DNS forwarders." 1>&2
			LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

View ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Acciones para view:"
			echo ""
			echo "	view list			     (lista de redes específicas)"
			echo "	view show [nombrevista]		     (lista de zonas de una red)"
			echo "	view duplicate			     (crear una red copiando otra)"
			echo "	view delete [nombrevista]	     (borrar una red específica)"
			echo "	view edit acl [nombrevista]	     (definir una red)"
			echo "	view edit config [nombrevista]	     (configurar una red)"
			echo "	updateforwarders [nombrevista] IP... (establecer nuevos referentes DNS)"
			;;
		* )
			View_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Domain ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Acciones para domain:"
			echo ""
			echo "	domain show [nombredominio]	(lista de zonas)"
			;;
		* )
			Domain_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Ptr ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for ptr:"
			echo ""
			echo "	ptr list"
			echo "	ptr show [nombrevista] [IP]	(mostrar hoja RDNS en una vista)"
			echo "	ptr create [nombrevista] [IP]	(añadir una hoja-plantilla a una vista)"
			echo "	ptr delete [nombrevista] [IP]	(borrar una hoja de una vista)"
#			echo "	ptr duplicate"
			echo "	ptr edit [nombrevista] [IP]	(configurar manualmente una hoja)"
			echo "	file edit [fichero]             (editar fichero con plantilla aplicable)"
			;;
		* )
			Ptr_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Ptrs ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for ptrs:"
			echo ""
			echo "	ptrs show [nombreptr]	(misma hoja DNS en cada vista)"
			;;
		* )
			Ptr_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Zone ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for zone:"
			echo ""
			echo "	zones show [nombrezona]                      (misma hoja DNS en cada vista)"
			echo "	zone show [nombrevista] [nombrezona]	     (mostrar hoja DNS compilada)"
			echo "	zone show [nombrevista] [nombrezona] --raw   (mostrar hoja DNS sin compactar)"
			echo "	zone export [nombrevista] [nombrezona]       (mostrar hoja DNS mejor para copiar)"
			echo "	zone create [nombrevista] [nombrezona]       (añadir una hoja-plantilla a una vista)"
			echo "	zone clone [vista1] [zona2] [vista2] [zona2] (copiar una zona en otra como nueva)"
			echo "	zone insert [nombrevista] [nombrezona]       (agregar una entrada DNS a una hoja)"
			echo "	zone delete [nombrevista] [nombrezona]       (borrar una hoja de una vista)"
#			echo "	zone duplicate                               (crear una hoja copiando otra)"
			echo "	zone edit [nombrevista] [nombrezona]         (configurar manualmente una hoja)"
			echo "	file edit [fichero]                          (editar fichero con plantilla aplicable)"
			;;
		* )
			Zone_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Zones ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for zones:"
			echo ""
			echo "	zones show [nombrezona]				(misma hoja DNS en cada vista)"
			echo "	zone show [nombrevista] [nombrezona]	(mostrar hoja DNS en una vista)"
			echo "	zone create [nombrevista] [nombrezona]	(añadir una hoja-plantilla a una vista)"
			echo "	zone delete [nombrevista] [nombrezona]	(borrar una hoja de una vista)"
#			echo "	zone duplicate				(crear una hoja copiando otra)"
			echo "	zone edit [nombrevista] [nombrezona]	(configurar manualmente una hoja)"
			;;
		* )
			Zones_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

File_edit ()
{
	local BaseFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ -f "$BaseFile" ] ; then
		EditFile "$(FileInclusions "$BaseFile")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		printf '%s\n' "${sERROR}E: Fichero no encontrado:" 1>&2
		printf '%s\n' "${BaseFile}${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		CheckConfigsAndOrReload y '' --novistas
#		CheckConfigsAndOrReload y ''
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

File ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for file:"
			echo ""
			echo "	file edit [fichero]  (editar directamente fichero con plantilla aplicable)"
			;;
		* )
			File_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Entry ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for entry:"
			echo ""
			echo "	entry show		(detalles de una entrada DNS)"
			echo "	entry create		(añadir una entrada DNS)"
			echo "	entry delete	(borrar una entrada DNS)"
			;;
		* )
			Entry_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Address ()
{
	local ActionHere="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$ActionHere" in
		"--help" | "" )
			echo "Actions for address:"
			echo ""
			echo "	adress check	(Analyze and detail)"
			;;
		* )
			Address_$ActionHere "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}
#[/gesdonis]

##### TEMPLATE FUNCTIONS TO CUSTOMIZE #####

Install_precp_Pre ()
# Example: Install_precp_Pre /cdrom/program
# Will be run after uninstalling previous version (all before installing new program files).
# Useful for old programs migration/uninstall before doing anything about this one.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_precp_Post ()
# Example: Install_precp_Post /cdrom/program
# Will be run after uninstalling previous version and before copying new program files.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_postcp_More ()
# Example: Install_postcp_More /cdrom/service
# Will be run once program files are already installed.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

#[gesdonis]
	if Is_Executable gestionardns.sh ; then
		rm "$(WhereProgram gestionardns.sh)"
	fi
	if Is_Executable gesdonis.sh ; then
		rm "$(WhereProgram gesdonis.sh)"
	fi
#[/gesdonis]
	return $StatusCode
}

Uninstall_predel_More ()
# Will be run after stopping service and before program files removal.
{
	sleep 0
}

Uninstall_Postdeletion ()
# Will be run after program files removal.
{
	sleep 0
}

Uninstall_purge_MorePre ()
# Will be run after Uninstall_Postdeletion and just before removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Uninstall_purge_MorePost ()
# Will be run just after removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Configuration_Saved_Pre ()
# Preliminar configurations at beginning of Configuration(). Useful to migrate old configurations and data before assuming them for production.
{
	local ReadOnly=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
}

Configuration_Saved_Post ()
{
	local ReadOnly=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
#[gesdonis]
	local LocalNetworks=""
	local MascaraLocal=""
	local FQDN=""
	local ValorActual=""
	local HuboCambios=""
	local CurWait=0
	local LastStatus=0
	local StatusCode=0

	GesdonisConfigsPath="/etc/bind/gesdonis"
	if [ -d /etc/bind/gestionardns ] ; then GesdonisConfigsPath="/etc/bind/gestionardns" ; fi
	if [ $ReadOnly -eq 0 ] ; then
		MkdirPP "$GesdonisConfigsPath" :bind ug=rwX,o=rX g+s
		MkdirPP "${data_path}/views" :bind ug=rwX,o=rX g+s
		if [ -f /etc/bind/gestionardns/gestionardns.conf ] ; then
			if [ ! -f "$SystemConfigFile" ] ; then
				cp -a /etc/bind/gestionardns/gestionardns.conf "$SystemConfigFile"
			else
				printf '%s\n' "" >> "$SystemConfigFile"
				printf '%s\n' "# Old configurations from /etc/bind/gestionardns/gestionardns.conf" >> "$SystemConfigFile"
				printf '%s\n' "" >> "$SystemConfigFile"
				cat /etc/bind/gestionardns/gestionardns.conf >> "$SystemConfigFile"
			fi
			mv /etc/bind/gestionardns/gestionardns.conf /etc/bind/gestionardns/gestionardns.conf.bak
		fi
	fi

#	if [ ! -f "$SystemConfigFile" ] ; then
#		echo "# Asistente de administracion de BIND9 (DNS)" >> "$SystemConfigFile"
#		echo "# Copyright (GNU GPL) Narcis Garcia" >> "$SystemConfigFile"
#		echo "# Parametros de funcionamiento del programa $ProgramName" >> "$SystemConfigFile"
#		echo "" >> "$SystemConfigFile"
#		HuboCambios="1"
#	fi
#	
	data_path="$(GetOrSetIniVarValue "$SystemConfigFile" data_path '' "/srv/named" = '' "\n# data_path no puede contener espacios, porque es usado en directivas \$INCLUDE sin comillas." $ReadOnly)"
	if [ "$data_path" != "/" ] ; then data_path="$(printf '%s\n' "$data_path" | sed -e 's|/$||g')" ; fi
	PropiaIP="$(PrimeraIPEquip)"
	PropiaIP_Regexp="$(printf '%s\n' "$PropiaIP" | sed -e 's|\.|\\.|g')"
	if [ "$PropiaIP" != "" ] ; then
		MascaraLocal="$(MascaraEquipIP "$PropiaIP")"
		if [ "$MascaraLocal" != "" ] && [ "$MascaraLocal" != "255.255.255.255" ] ; then
#			LocalNetworks="$(IPBaseXarxa "$PropiaIP" "$MascaraLocal")/$(BitsMascara "$MascaraLocal")"
			LocalNetworks="$(BitsMascara "$MascaraLocal")"
			LocalNetworks="$(IPv4NetworkAddress "${PropiaIP}/${LocalNetworks}")/${LocalNetworks}"
		fi
		if [ "$LocalNetworks" = "" ] && [ "$(printf '%s\n' "$PropiaIP" | grep -e '^192\.168\.' -e '^172\.16\.' -e '^10\.')" != "" ] ; then
			# RFC 1918
			LocalNetworks="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"
		fi
	fi
	local_nets="$(GetOrSetIniVarValue "$SystemConfigFile" local_nets '' "\"127.0.0.0/8 $LocalNetworks\"" = '' "\n" $ReadOnly)"
	if [ "$MascaraLocal" = "255.255.255.255" ] && [ "$(echo " $local_nets " | grep -e "/32 ")" != "" ] && [ local_nets = "127.0.0.0/8 $LocalNetworks" ] && [ $ReadOnly -eq 0 ] ; then
		# Comprobación después de cargar la configuración de local_nets, que puede estar corregida.
		echo "La máscara de la red local ($MascaraLocal) no parece haberse detectado bien."
		echo "Por favor, escribe la máscara correcta."
		read MascaraLocal
#		LocalNetworks="$(IPBaseXarxa "$PropiaIP" "$MascaraLocal")/$(BitsMascara "$MascaraLocal")"
		LocalNetworks="$(BitsMascara "$MascaraLocal")"
		LocalNetworks="$(IPv4NetworkAddress "${PropiaIP}/${LocalNetworks}")/${LocalNetworks}"
		local_nets="127.0.0.0/8 $LocalNetworks"
		if [ $ReadOnly -eq 0 ] ; then
			SetIniVarValue "$SystemConfigFile" local_nets '' "\"$local_nets\"" = "# CIDR definitions of networks to not consider as WAN/internet. Space separated list."
			echo "Se actualizó El parámetro local_nets de $SystemConfigFile"
		fi
	fi
	
	if [ $ReadOnly -eq 0 ] ; then
		# Action only useful to write data
		default_forwarders="$(IniVarValue "$SystemConfigFile" default_forwarders '' '' "" = '')"
		if [ "$default_forwarders" = "" ] ; then
			printf '%s\n' "Getting Tier 2 forwarders from opennicproject.org"
			default_forwarders="$(HttpGetContent "https://api.opennicproject.org/geoip/?ns&ipv=4&res=5&adm=2&pct=95&bl&wl" 2>/dev/null)"
			default_forwarders="$(printf '%s\n' "$default_forwarders" | tr -s '\t' ' ' | grep -ie '^nameserver .' | cut -f 2 -d ' ')"
			default_forwarders="$(echo TrimAndSingle $default_forwarders | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		fi
	fi
	default_forwarders="$(GetOrSetIniVarValue "$SystemConfigFile" default_forwarders '' "\"$default_forwarders\"" = '' "\n# Sure that all forwarders response NXDOMAIN to this command: host -t SOA local. x.x.x.x" $ReadOnly)"
	recursion_local="$(GetOrSetIniVarValue "$SystemConfigFile" recursion_local '' "yes" = '' "\n$PreComment" $ReadOnly)"
	recursion_nolocal="$(GetOrSetIniVarValue "$SystemConfigFile" recursion_nolocal '' "no" = '' "\n$PreComment" $ReadOnly)"
	# valor 'refresh' del registro SOA recomendable según zonemaster.net: 14400
	default_ttl="$(GetOrSetIniVarValue "$SystemConfigFile" default_ttl '' "\"4h\"" = '' "\n# Recommended 1d or more by RFC 1912; see https://www.zytrax.com/books/dns/apa/ttl.html" $ReadOnly)"
	
	FQDN="$(hostname -f | grep -e '\..*\.')"
	if [ "$FQDN" = "" ] ; then
		FQDN="$(cat /etc/hosts | tr -s '\t' ' ' | grep -e "^${PropiaIP_Regexp} ")"
		FQDN="$(OneWord () { echo $2; }; OneWord $FQDN)"
	fi
	if [ "$FQDN" = "" ] ; then
		FQDN="$(cat /etc/hosts | tr -s '\t' ' ' | grep -e "^127\." | grep -e ' .* ')"
		FQDN="$(OneWord () { echo $2; }; OneWord $FQDN)"
	fi
	default_authorityserver="$(GetOrSetIniVarValue "$SystemConfigFile" default_authorityserver '' "\"$FQDN\"" = '' "\n# Default SOA, without final dot." $ReadOnly)"
	if [ "$(echo "$default_authorityserver" | grep -e ".*\..*")" = "" ] && [ $ReadOnly -eq 0 ] ; then
		echo "No pudo detectarse el nombre publico de este servidor ($default_authorityserver)."
		echo "Se necesita como servidor DNS generico para todas las zonas."
		echo "Por favor, escribe el nombre completo (FQDN) del SOA predeterminado."
		read FQDN
		default_authorityserver="$FQDN"
		if [ "$default_authorityserver" != "." ] ; then default_authorityserver="$(printf '%s\n' "$default_authorityserver" | sed -e 's|\.$||g')" ; fi
		echo "- Establecido como SOA predeterminado $default_authorityserver"
		SetIniVarValue "$SystemConfigFile" default_authorityserver '' "\"$default_authorityserver\"" = "# Default SOA for new zone sheets."
	fi
	if [ "$(EsIP "$default_authorityserver")" != "1" ] && [ "$(echo "$default_authorityserver" | grep -e "\.$")" = "" ] ; then
		# Si no es una IP, lo hacemos terminar en punto
		default_authorityserver="${default_authorityserver}."
	fi
	default_postmaster="$(GetOrSetIniVarValue "$SystemConfigFile" default_postmaster '' "\"postmaster\"" = '' "\n# Correo de contacto predeterminado al responsable de las zonas. Sin dominio sera para el dominio de cada zona." $ReadOnly)"
	if [ "$(echo "$default_postmaster" | grep -e ".*@.*")" ] ; then
		# El formato esperado para un correo regular como postmaster@example.net es "postmaster.example.net."
		default_postmaster="$(echo "$default_postmaster" | tr -s "@" ".")."
	fi
	
	# No se relevará la configuración de raíz en caso que "named.conf" contenga la 
	# palabra "gestionardns" o bién ya exista named.conf.original
	if [ -f /etc/bind/named.conf ] ; then
		if [ "$(cat /etc/bind/named.conf | grep -ie "gestionardns" -ie "gesdonis")" = "" ] && [ $ReadOnly -eq 0 ] ; then
			echo ""
			echo "Parece la primera vez que se usa este asistente."
			echo "Los datos DNS ahora encontrados en BIND se descartarán."
			echo "¿Sustituir ya el fichero /etc/bind/named.conf para el control del asistente?"
			ValorActual="$(RespostaLletra)"
			if [ "$ValorActual" = "s" ] || [ "$ValorActual" = "y" ] ; then
				if [ -f /etc/bind/named.conf.original ] && [ ! -f /etc/bind/named.conf.gesdonis ] ; then
					# Old backup names
					mv /etc/bind/named.conf.original /etc/bind/named.conf.gesdonis
				fi
				if [ ! -f /etc/bind/named.conf.gesdonis ] ; then
					mv /etc/bind/named.conf /etc/bind/named.conf.gesdonis
				else
					rm /etc/bind/named.conf
				fi
				if [ -f /etc/bind/named.conf.local.original ] && [ ! -f /etc/bind/named.conf.local.gesdonis ] ; then
					# Old backup names
					mv /etc/bind/named.conf.local.original /etc/bind/named.conf.local.gesdonis
				fi
				if [ -f /etc/bind/named.conf.local ] && [ ! -f /etc/bind/named.conf.local.gesdonis ] ; then
					mv /etc/bind/named.conf.local /etc/bind/named.conf.local.gesdonis
				fi
				if [ -f /etc/bind/named.conf.options.original ] && [ ! -f /etc/bind/named.conf.options.gesdonis ] ; then
					# Old backup names
					mv /etc/bind/named.conf.options.original /etc/bind/named.conf.options.gesdonis
				fi
				if [ -f /etc/bind/named.conf.options ] && [ ! -f /etc/bind/named.conf.options.gesdonis ] ; then
					mv /etc/bind/named.conf.options /etc/bind/named.conf.options.gesdonis
				fi
				if [ -d "${data_path}.original" ] && [ ! -d "${data_path}.gesdonis" ] ; then
					# Old backup names
					mv "${data_path}.original" "${data_path}.gesdonis"
				fi
				if [ -d "${data_path}" ] && [ ! -d "${data_path}.gesdonis" ] ; then
					# Esto debe hacerse antes de generar ningun fichero
					mv "${data_path}" "${data_path}.gesdonis"
				fi
				HuboCambios="1"
			fi
		fi
	fi
	if [ ! -f "${GesdonisConfigsPath}/named.conf.logging" ] && [ $ReadOnly -eq 0 ] ; then
		MkdirPP "/var/log/named" :bind ug=rwX,o=rX g+s
		echo "# Configuracion inicial generada por $ProgramName" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "# En este fichero solo debe haber la clausula logging." >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "# Los errores son reportados como \"named\" en /var/log/daemon.log" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "logging {" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "	channel bind-log {" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "		file \"/var/log/named/bind.log\" versions 3 size 30m;" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "		severity warning;  # critical, error, warning, notice, info, debug, dynamic." >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "		print-time yes;" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "		print-severity yes;" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "		};" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "	category lame-servers { bind-log; };" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "	#category queries { bind-log; };" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "	category default { bind-log; };" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "	category config { bind-log; };" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "};" >> "${GesdonisConfigsPath}/named.conf.logging"
		echo "- Se ha configurado como bitacora el fichero /var/log/named/lame-servers.log"
		echo "  Parametros revisables en ${GesdonisConfigsPath}/named.conf.logging"
		HuboCambios="1"
	fi
	if [ ! -f "${GesdonisConfigsPath}/named.conf.options" ] && [ $ReadOnly -eq 0 ] ; then
		MkdirPP "/var/cache/bind" :bind ug=rwX,o=rX g+s
		chmod g+rwX /var/cache/bind
		echo "# Configuracion inicial generada por $ProgramName" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "# En este fichero solo debe haber la clausula options." >> "${GesdonisConfigsPath}/named.conf.options"
		echo "" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "options {" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	directory \"/var/cache/bind\";" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	auth-nxdomain no;    # conform to RFC1035" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	listen-on { any; };" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	listen-on-v6 { any; };" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	allow-query { any; };" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	allow-query-on {any;};" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	rrset-order {order cyclic;};    # Habilita el reordenamiento de las respuestas para registros coincidentes (Round Robin)" >> "${GesdonisConfigsPath}/named.conf.options"
#		echo "	version \"999.999.999-P9\";       # Devuelve este texto si el cliente pide la version.bind" >> "${GesdonisConfigsPath}/named.conf.options"
#		echo "	version \"not currently available\";       # Devuelve este texto si el cliente pide la version.bind" >> "${GesdonisConfigsPath}/named.conf.options"
		# Checker www.zonemaster.net does not show any notice when version.bind is returned empty.
		echo "	version \"\";       # Devuelve este texto si el cliente pide la version.bind" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "	allow-transfer {\"none\";};	# Predeterminado no permitir la transferencia de zonas a un servidor esclavo." >> "${GesdonisConfigsPath}/named.conf.options"
		echo "#	allow-transfer { 80.80.80.1;};	# Ejemplo para permitir a un servidor esclavo transferirse cualquier zona. Parametro que tambien puede estar en clausulas \"zone\"" >> "${GesdonisConfigsPath}/named.conf.options"
#		echo "	dnssec-enable yes;" >> "${GesdonisConfigsPath}/named.conf.options"  OBSOLETE AS OF bind 9.16
		echo "	dnssec-validation yes;" >> "${GesdonisConfigsPath}/named.conf.options"
#		echo "	dnssec-lookaside auto;" >> "${GesdonisConfigsPath}/named.conf.options"  OBSOLETE AS OF bind 9.16
		echo "};" >> "${GesdonisConfigsPath}/named.conf.options"
		echo "- Se ha configurado toda interfaz y todo origen para consultas DNS."
		echo "  Parametros revisables en ${GesdonisConfigsPath}/named.conf.options"
		HuboCambios="1"
	fi
	if [ $ReadOnly -eq 0 ] ; then
		# This before generating named.conf.controls
		if [ ! -f /etc/bind/rndc.key ] || [ ! -f "${GesdonisConfigsPath}/named.conf.controls" ] || [ "$(cat /etc/bind/named.conf 2>/dev/null | grep -e '/etc/bind/rndc.key')" = "" ] ; then
			printf '%s\n' "- Generating new secret at /etc/bind/rndc.key to authenticate remote rndc or nsupdate"
#			rndc-confgen | grep -ve '^#' | sed -e 's|953|53|g' > /etc/bind/rndc.key
			rndc-confgen | grep -ve '^#' | sed -e '/}/q' > /etc/bind/rndc.key
#			rndc-confgen -a
			HuboCambios="1"
		fi
	fi
	if [ ! -f "${GesdonisConfigsPath}/named.conf.controls" ] && [ $ReadOnly -eq 0 ] ; then
		echo "# Configuracion inicial generada por $ProgramName" >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "# En este fichero solo debe haber la clausula controls." >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "" >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "controls {	# Configuraciones para la gestion remota mediante el programa rndc" >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "	inet 127.0.0.1 allow { 127.0.0.1; } keys { \"rndc-key\"; };" >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "};" >> "${GesdonisConfigsPath}/named.conf.controls"
		echo "- Se ha configurado que con rndc se pueda administrar el servicio desde localhost."
		echo "  Parametros revisables en ${GesdonisConfigsPath}/named.conf.controls"
		HuboCambios="1"
	fi
	if [ ! -f /etc/bind/named.conf ] && [ $ReadOnly -eq 0 ] ; then
		echo "# Configuracion inicial generada por $ProgramName" >> "/etc/bind/named.conf"
		echo "" >> "/etc/bind/named.conf"
		echo "include \"${GesdonisConfigsPath}/named.conf.logging\";" >> "/etc/bind/named.conf"
		echo "include \"${GesdonisConfigsPath}/named.conf.options\";" >> "/etc/bind/named.conf"
		echo "include \"/etc/bind/rndc.key\";" >> "/etc/bind/named.conf"
		echo "include \"${GesdonisConfigsPath}/named.conf.controls\";" >> "/etc/bind/named.conf"
		echo "include \"${GesdonisConfigsPath}/named.conf.views\";" >> "/etc/bind/named.conf"
		HuboCambios="1"
	fi
	if [ "$(cat /etc/bind/named.conf 2>/dev/null | grep -e '/etc/bind/rndc.key')" = "" ] && [ $ReadOnly -eq 0 ] ; then
		# Workarounds to also update old setups without key dependency problems
		if [ "$(cat /etc/bind/named.conf 2>/dev/null | grep -e '\.controls' -e 'controls {')" != "" ] ; then
			ValorActual="$(cat /etc/bind/named.conf)"
			echo "include \"/etc/bind/rndc.key\";" > "/etc/bind/named.conf"
			printf '%s\n' "$ValorActual" >> "/etc/bind/named.conf"
		else
			echo "include \"/etc/bind/rndc.key\";" >> "/etc/bind/named.conf"
		fi
		HuboCambios="1"
	fi
	if [ ! -f "${GesdonisConfigsPath}/named.conf.views" ] && [ $ReadOnly -eq 0 ] ; then
		EncabezadoFicheroRegenerado "#" > "${GesdonisConfigsPath}/named.conf.views"
		echo "# Las ACL integradas \"localhost\" y \"localnets\" no funcionan bien en un contenedor OpenVZ (venet)" >> "${GesdonisConfigsPath}/named.conf.views"
		
		echo "include \"${data_path}/views/local-nets/acl\";" >> "${GesdonisConfigsPath}/named.conf.views"
		MkfilePP "${data_path}/views/local-nets/acl" :bind ug=rwX,o=rX g+s
		echo "# En este fichero solo debe haber clausulas acl o inclusiones de ficheros." >> "${data_path}/views/local-nets/acl"
		echo "# Las ACL integradas \"localhost\" y \"localnets\" no funcionan bien en un contenedor OpenVZ (venet)" >> "${data_path}/views/local-nets/acl"
		echo "" >> "${data_path}/views/local-nets/acl"
		echo "acl \"acl_local-nets\" {" >> "${data_path}/views/local-nets/acl"
		for ValorActual in $local_nets ; do
			echo "	${ValorActual};" >> "${data_path}/views/local-nets/acl"
		done
		echo "};" >> "${data_path}/views/local-nets/acl"
		
		echo "include \"${data_path}/views/nolocal-nets/acl\";" >> "${GesdonisConfigsPath}/named.conf.views"
		MkfilePP "${data_path}/views/nolocal-nets/acl" :bind ug=rwX,o=rX g+s
		echo "# En este fichero solo debe haber clausulas acl o inclusiones de ficheros." >> "${data_path}/views/nolocal-nets/acl"
		echo "# Las ACL integradas \"localhost\" y \"localnets\" no funcionan bien en un contenedor OpenVZ (venet)" >> "${data_path}/views/nolocal-nets/acl"
		echo "" >> "${data_path}/views/nolocal-nets/acl"
		echo "acl \"acl_nolocal-nets\" { !acl_local-nets; any; };" >> "${data_path}/views/nolocal-nets/acl"
		
		echo "" >> "${GesdonisConfigsPath}/named.conf.views"
		
		echo "include \"${data_path}/views/local-nets/view\";" >> "${GesdonisConfigsPath}/named.conf.views"
		MkfilePP "${data_path}/views/local-nets/view.header" :bind ug=rwX,o=rX g+s
		echo "view \"local-nets\" {" >> "${data_path}/views/local-nets/view.header"
		echo "	match-clients { acl_local-nets; };  # Prestar este servicio/vista a clientes de las redes locales" >> "${data_path}/views/local-nets/view.header"
		echo "	recursion yes;  # El uso recursivo de la cache del servidor (zonas ajenas)." >> "${data_path}/views/local-nets/view.header"
		echo "	prefetch 10 30;	# Repetir consultas de recursion [X] segundos antes de vencer su TTL pero solo registros que llegaron con TTL de al menos [Y] segundos. Util para atender a clientes con cache en lugar de que esperen repeticiones de consultas." >> "${data_path}/views/local-nets/view.header"
		echo "	stale-cache-enable yes;	# Conservar datos caducados en cache or si fallan los intentos de consulta a los servidores de referencia."
		echo "	stale-answer-client-timeout 1;	# Segundos de espera a un servidor antes de atender al cliente con datos caducados"
		echo "	forwarders {" >> "${data_path}/views/local-nets/view.header"
		for ValorActual in $default_forwarders ; do
			echo "		${ValorActual};" >> "${data_path}/views/local-nets/view.header"
		done
		echo "	};" >> "${data_path}/views/local-nets/view.header"
		if [ -f "/etc/bind/named.conf.default-zones" ] ; then
			echo "	include \"/etc/bind/named.conf.default-zones\";" >> "${data_path}/views/local-nets/view.header"
		fi
		MkfilePP "${data_path}/views/local-nets/view.footer" :bind ug=rwX,o=rX g+s
		echo "};" >> "${data_path}/views/local-nets/view.footer"
		
		echo "include \"${data_path}/views/nolocal-nets/view\";" >> "${GesdonisConfigsPath}/named.conf.views"
		MkfilePP "${data_path}/views/nolocal-nets/view.header" :bind ug=rwX,o=rX g+s
		echo "view \"nolocal-nets\" {" >> "${data_path}/views/nolocal-nets/view.header"
		echo "	match-clients { acl_nolocal-nets; };	# Prestar este servicio/vista a cualquier cliente excepto de las redes locales" >> "${data_path}/views/nolocal-nets/view.header"
		echo "	recursion no;	# El uso recursivo de la cache del servidor (zonas ajenas)." >> "${data_path}/views/nolocal-nets/view.header"
		echo "	forwarders {" >> "${data_path}/views/nolocal-nets/view.header"
		for ValorActual in $default_forwarders ; do
			echo "		${ValorActual};" >> "${data_path}/views/nolocal-nets/view.header"
		done
		echo "	};" >> "${data_path}/views/nolocal-nets/view.header"
		echo "	sortlist {      # Round Robin" >> "${data_path}/views/nolocal-nets/view.header"
		echo "		{10.2/16; };" >> "${data_path}/views/nolocal-nets/view.header"
		echo "	};" >> "${data_path}/views/nolocal-nets/view.header"
		MkfilePP "${data_path}/views/nolocal-nets/view.footer" :bind ug=rwX,o=rX g+s
		echo "};" >> "${data_path}/views/nolocal-nets/view.footer"
		
		ReferirZonasVista "local-nets"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ReferirZonasVista "nolocal-nets"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		
		echo "- Se han configurado $local_nets como redes locales"
		echo "  Parametros revisables en ${data_path}/views/local-nets/acl"
		HuboCambios="1"
	fi

	if [ "$HuboCambios" = "1" ] && [ $StatusCode -eq 0 ] ; then
		Service reload --novistas
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CurWait=4
		while [ $CurWait -gt 0 ] && [ "$(netstat -tulnp | tr -s '\t' ' ' | grep -e ':953 .* LISTEN ')" = "" ] ; do
			sleep 1
			CurWait=$((CurWait - 1))
		done
		Service flush
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		echo "Configuración básica revisada."
		echo ""
	fi
	return $StatusCode
#[/gesdonis]
}

ProgramHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local InstallerActions=''
#[gesdonis]
	local SpecificActions='service|view|zone|zones|address'
#[/gesdonis]
	local BaseName=''
	
	BaseName="$(printf '%s\n' "$MeExecutable" | tr -s '/' '\n' | tail -n 1)"
	printf '%s\n' "$(ScriptHeaderValue Description) ${ParO}${ProgramName}${ParC} $(ScriptHeaderValue Version)"
	ScriptHeaderValue Homepage
	printf '%s\n' ""
	if [ $(MeInstalled) -eq 1 ] || [ "$InstalledExpected" = "0" ] ; then
		if [ "$RootRequired" != "0" ] && [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			if [ $(MeInstalled) -eq 1 ] ; then
				InstallerActions="|uninstall|purge"
			else
				InstallerActions="|install"
			fi
		fi
		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {${SpecificActions}${InstallerActions}}${SudoSuffix}"
#[gesdonis]
		printf '%s\n' "       ${SudoPrefix}${ProgramName} [object] [action] [parameters]"
		printf '%s\n' ""
		printf '%s\n' "Objects:"
		printf '%s\n' "	service   ${ParO}BIND9${ParC}"
		printf '%s\n' "	view      ${ParO}listening net${ParC}"
#		printf '%s\n' "	domain    ${ParO}name with zones${ParC}"
		printf '%s\n' "	zone      ${ParO}entries and subdomains sheet${ParC}"
		printf '%s\n' "	zones     ${ParO}sheets set${ParC}"
#		printf '%s\n' "	entry     ${ParO}DNS record${ParC}"
		printf '%s\n' "	ptr       ${ParO}RDNS zones${ParC}"
		printf '%s\n' "	address   ${ParO}FQDN or IP${ParC}"
#[/gesdonis]
		printf '%s\n' ""
	else
		if [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			printf '%s\n' "To install as a system program:"
			printf '%s\n' "${SudoPrefix}${BaseName} install${SudoSuffix}"
			printf '%s\n' ""
			printf '%s\n' "To remove the program:"
			printf '%s\n' "${SudoPrefix}${ProgramName} uninstall${SudoSuffix}"
		else
			printf '%s\n' "${sWARN}W: Program not installed. Use your package manager to install ${ProgramName}.${fRESET}"
		fi
	fi
}


##### MAIN SCRIPT #####

LastStatus=0 ; StatusCode=0
if [ $(id -u) -eq 0 ] && [ "$USER" != "root" ] && [ "$USER" != "" ] && [ "$(env | grep -ie LXC -ie '^container=')" != "" ] ; then
	# Patch to not legate some LXC unprivileged variables on lxc-attach/calls
	export USER=root ; export HOME=/root ; export LOGNAME=root
	if [ -f /etc/default/locale ] ; then
		IFS="$(printf '\n\b')" ; for CurVar in $(cat /etc/default/locale | cut -f 1 -d '#' | grep -e '=') ; do unset IFS
			eval export $CurVar
		done
	fi
fi
MeCallFile="$0"
PreviousDir="$(pwd)"
MeDir="$(Dirname "$MeCallFile")"
cd "$MeDir"
MeDir="$(pwd)"
cd "$PreviousDir"
MeCallFile="${MeDir}/$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1)"
if [ "$LogLevel" = "" ] ; then LogLevel=3 ; fi	# A nested call can export LogLevel
LogProgram 4 '$' "$0" "$@"
#cd / # avoid blocking mount points from being unmounted - It's a problem to deal with user specified relative paths

Action="$1"
if [ $# -gt 0 ] ; then shift ; fi
Action="$(Lowercase "$Action")"
case "$Action" in
	"remove" ) Action="uninstall" ; ActionMode="remove" ;;
	"purge" ) Action="uninstall" ; ActionMode="purge" ;;
	"-h" ) Action="--help" ;;
	"-V" ) Action="--version" ;;
#[gesdonis]
	"direccion" ) Action="address" ;;
	"registro" ) Action="entry" ;;
	"record" ) Action="entry" ;;
	"zonas" ) Action="zones" ;;
	"zona" ) Action="zone" ;;
	"zonavista" ) Action="zone" ;;
	"dominio" ) Action="domain" ;;
	"vista" ) Action="view" ;;
	"servicio" ) Action="service" ;;
#[/gesdonis]
esac
case "$Action" in
	"install" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				LackDependencies="$(DependenciasFaltan "$DependsOnSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sERROR}E: Following software must be installed before this dependent program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
					LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				LackDependencies="$(DependenciasFaltan "$RecommendedSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sWARN}W: Following software is also recommended for this program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Install actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Installing $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "precp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script.
				Install_precp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "cp" ] || [ "$PackageManager_Call" = "whole" ] ; then
#				printf '%s\n' "Installing $ProgramName"
				Install_cp "$MeExecutable" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postcp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Install_postcp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "For more options, run: ${ProgramName} --help"
				fi
			fi
		fi
		;;
	"uninstall" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$ActionMode" = "remove" ] ; then
			if [ "$ProgramInstaller" = "1" ] || [ "$PackageManager_Call" != "" ] ; then
				printf '%s\n' "${sWARN}W: To uninstall $ProgramName you must use the action \"uninstall\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Uninstall actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Uninstalling $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "predel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_predel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "predel" ] ; then
					cp "$MeExecutable" "/var/tmp/${ProgramName}.uninstall.tmp"
					cp "$MeExecutable" "${DirTempX}/${ProgramName}.uninstall.tmp"	# Compatibility with old package script: postrm
					chmod u=rx,g=r,o= "/var/tmp/${ProgramName}.uninstall.tmp"
					chmod u=rx,g=r,o= "${DirTempX}/${ProgramName}.uninstall.tmp"
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "del" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_del
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postdel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_postdel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$ActionMode" = "purge" ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "purge" ] ; then
				Uninstall_purge
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		;;
	"debug" )
#		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		export LogLevel=4
		"$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"--version" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "${ProgramName} $(ScriptHeaderValue Version)"
		ScriptHeaderValue Copyright
		ScriptHeaderValue Homepage
		;;
	"--help" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[gesdonis]
	"service" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Service "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"view" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		View "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"domain" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Domain "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"zone" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Zone "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"zones" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Zones "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"ptr" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Ptr "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"ptrs" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Ptrs "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"file" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		File "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"entry" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Entry "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"address" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Address "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"updateforwarders" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		View_updateforwarders "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/gesdonis]
	"" )
		printf '%s\n' "${sERROR}E: No action specified.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	* )
		printf '%s\n' "${sERROR}E: Syntax error; Unknown action ${Action}${fRESET}" 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
esac

cd "$PreviousDir" 2>/dev/null
if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
exit $StatusCode
