#!/bin/sh
### BEGIN INIT INFO
# Provides:	durruter
# Required-Start:	$network
# Should-Start:	$syslog
# Required-Stop:	$network
# Should-Stop:	$syslog
# Default-Start:	2 3 4 5
# Default-Stop:	0 1 6
# Short-Description:	NAT/PAT routing helper
# Description:		Wizard to configure NAT/PAT router based in iptables
# X-Interactive: false
### END INIT INFO
# RHL fields:
# chkconfig: 2345 80 30
# description: NAT/PAT routing helper

# Service version: 2.5.12
# Copyright: (GNU GPL) 2012-2025 Narcis Garcia

# 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/

# Init script template version: 2.6.3
# Copyright: (GNU GPL) 2014-2023 Narcis Garcia
# Compatible with init systems: System-V since y.2000, upstart, systemd

# Service development at programacio/projectes_publics/durruter/durruter
# Software releases can be downloaded from: https://...


# Program tested with distributions: Debian/Ubuntu, CentOS
# Estructura principal en directorios de configuración:
#	dispositivo/servicio/...
# Dispositivos especiales:
#	to-wan (NIC predeterminada que sale a la pasarela por 0.0.0.0)
#	* (válido para cualquier dispositivo; exprésamente sin especificar dispositivo)
#	any (sinónimo de *). Se guarda como _any - Esta palabra está reservada para sólo el guardado.
# IPs especiales:
#	* (válido para cualquier IP; exprésamente sin especificar IP)
#	any (sinónimo de *). Se guarda como _any - Esta palabra está reservada para sólo el guardado.
# Protocolos especiales:
#	* (válido para cualquier protocolo; exprésamente sin especificar protocolo)
#	any (sinónimo de *). Se guarda como _any - Esta palabra está reservada para sólo el guardado.
# Comodines:
#	default (para la IP que corresponda a un dispositivo, o la NIC predeterminada para WAN)
#	all (para seleccionar todos los elementos existentes)
# Programillos «ejecutores»: if-up_fast.sh if-down_fast.sh
# Estructura de datos por servicio:
#	$dispositivo/nat/red-ip-origen/wan-ip.txt
#	$dispositivo/nat/red-ip-origen/lan-mask.txt
#	$dispositivo/nat/red-ip-origen/id.txt
#	$dispositivo/nat/red-ip-origen/description.txt
#	$dispositivo/nat/red-ip-origen/if-up_fast.sh
#	$dispositivo/nat/red-ip-origen/if-down_fast.sh
#	$dispositivo/nat/if-up_fast.sh
#	$dispositivo/nat/if-down_fast.sh
#	$dispositivo/if-up_fast.sh
#	$dispositivo/if-down_fast.sh
#	Ejemplo: eth1/nat/192.168.1.0/description.txt
#
#	$dispositivo/dnat/puerto/protocolo/id.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/input-ip.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/reception-ports.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/destination-ip.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/destination-ports.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/description.txt	<- One line per input IP case.
#	$dispositivo/dnat/puerto/protocolo/if-up_fast.sh
#	$dispositivo/dnat/puerto/protocolo/if-down_fast.sh
#	$dispositivo/dnat/puerto/if-up_fast.sh
#	$dispositivo/dnat/puerto/if-down_fast.sh
#	$dispositivo/dnat/if-up_fast.sh
#	$dispositivo/dnat/if-down_fast.sh
#	$dispositivo/if-up_fast.sh
#	$dispositivo/if-down_fast.sh
#	Ejemplo: eth0/dnat/80/tcp/description.txt
#
# Ejemplo manual para agregar una entrada DNAT en iptables restringiengo una IP de origen:
# iptables --table nat --append PREROUTING --destination $InputIp --source $SourceIp --protocol $Protocol --dport $Port --in-interface $RDev --jump DNAT --to-destination ${DestinationIp}:${Port}
#
# Cada if-up_fast.sh y if-down_fast.sh contienen las instrucciones para iptables, sin llamadas a ningún otro script.
# El if-up_fast.sh de un directorio superior contiene una copia de las líneas ^iptables de los inferiores, sin llamar scripts.
# A los programillos ejecutores siempre hay que pasarles el nombre del dispositivo como primer parámetro, y la IP-WAN como segundo.
# [POR CONSIDERAR] if-up_strict.sh determina el dispositiu aixecat i executa durruter.sh enable per cada entrada
#
# Documentation references:
# https://phoenixnap.com/kb/iptables-tutorial-linux-firewall
# https://www.systutorials.com/port-forwarding-using-iptables/
# http://www.netfilter.org/documentation/
# http://wiki.openvz.org/Using_NAT_for_container_with_private_IPs
# Log/debug:
#	https://fabianlee.org/2019/06/05/ubuntu-debug-iptables-by-inserting-a-log-rule/
#	(logs to /var/log/kern.log )
#	Example to insert+append log rules at beginning+end of each table / ruleclass:
#		iptables -t nat -I PREROUTING -j LOG --log-prefix "PREROUTING1:" --log-level 7
#		iptables -t nat -A PREROUTING -j LOG --log-prefix "PREROUTING9:" --log-level 7
#		iptables -t nat -I POSTROUTING -j LOG --log-prefix "POSTROUTING1:" --log-level 7
#		iptables -t nat -A POSTROUTING -j LOG --log-prefix "POSTROUTING9:" --log-level 7
#		iptables -I FORWARD -j LOG --log-prefix "FORWARD1:" --log-level 7
#		iptables -A FORWARD -j LOG --log-prefix "FORWARD9:" --log-level 7
#	Delete previous log rules:
#		iptables -t nat -D PREROUTING -j LOG --log-prefix "PREROUTING1:" --log-level 7
#		iptables -t nat -D PREROUTING -j LOG --log-prefix "PREROUTING9:" --log-level 7
#		iptables -t nat -D POSTROUTING -j LOG --log-prefix "POSTROUTING1:" --log-level 7
#		iptables -t nat -D POSTROUTING -j LOG --log-prefix "POSTROUTING9:" --log-level 7
#		iptables -D FORWARD -j LOG --log-prefix "FORWARD1:" --log-level 7
#		iptables -D FORWARD -j LOG --log-prefix "FORWARD9:" --log-level 7
# MASQUERADE is an iptables target that can be used instead of SNAT target (source NAT) when external ip of the inet interface is not known at the moment of writing the rule (when server gets external ip dynamically).
#	http://www.billauer.co.il/ipmasq-html.html
#	https://www.oreilly.com/openbook/linag2/book/ch11.html
# iptables first packaged with Debian as of OS version 3.0 woody (iptables v1.2)
# Friendly alternatives:
#	(flexible) https://www.shorewall.org/
#	(simple) https://help.ubuntu.com/community/UFW
# iptables comprehensive table of D/NAT rules:
# sudo iptables -t nat -L -vn --line-numbers


# Service development ToDo:
#	- "dnat export" exporta por duplicado las entradas guardadas para «to-wan» y/o «default» que también están activas con NIC/IP explícita.
#	- "dnat export" debería implementar la opción --maxportable para convertir a «to-wan» y «default» lo que coincida con la NIC-WAN y la única IP de NIC respectivamente.
#	- Al eliminar entradas DNAT, no parecen actualizarse los disparadores if-up_fast.sh ni if-down_fast.sh
#	  Habría que hacerlos modulares, de manera que un if-up_fast.sh ejecute todo lo que haya en if-up_fast.d/
#	- Cada entrada/salida debería poderse vincular a un evento distinto: Levantamiento de NIC, inicio de contenedor, conexión VPN... Y que haya opción «pre» y «post»
#	- "dnat create" no detecta que una entrada así ya existía:
#	  YES     ens3f0      tcp             122  *            172.16.11.2:     SFTP SO
#	- RebuildDnatLoaders() no trata completamente los ficheros multilínea; por ejemplo destination-ip.txt cuando se ha vaciado la primera línea.
#	- Para que funcione una entrada DNAT de WAN hacia un destino WAN, debe ejecutarse una de estas:
#	  iptables -t nat -A POSTROUTING -d $IpDestinoPropio/32 -j MASQUERADE
#	  iptables -t nat -A POSTROUTING -d $IpDestinoPropio/32 -o $InterfazSalida -j MASQUERADE
#	- Comprobar que "service autostart" ya está arreglado en todos los escenarios, como Debian 10 de OVH-KVM {denia}
#	- Es posible hacer traducción de la IP solicitada desde LAN:
#	  iptables -t nat -A PREROUTING -d 10.2.0.1 -j DNAT --to-destination 192.115.106.31
#	  (en el ejemplo, quien a pida 10.2.0.1 le será redirigida la petición a 192.115.106.31) - típico para DNS sin servidor propio.
#	- Desde Debian 10 (create/remove): iptables: Bad rule (does a matching rule exist in that chain?)
#	- Con "durruter dnat remove all all $Port all" no queda /etc/durruter/*/dnat/$Port limpio
#	- Comprobar que con "durruter dnat remove all all all all" quede /etc/durruter/*/dnat limpio
#	- Actualizando de la versión 2.2.6 a 2.2.7 , deshabilita el servicio (ifup.d/ifdown.d) al desinstalar y no lo vuelve a habilitar al instalar.
#	- Al desaparecer un nombre de NIC que se estaba usando, con cualquier gestion da el mensaje: Device "eth2" does not exist.
#	  Ello ocurre al quedar datos como /etc/durruter/eth2/dnat/*
#	  Dicha desaparición puede deberse a cambios en el hardware, en la nomenclatura del entorno operativo, o en el alta/baja de NICs virtuales.
#	  Al hacer "durruter dnat list" deberian listarse todas las entradas aunque sean de NICs inexistentes.
#	- Cada dispositivo (como /etc/durruter/eth0) deberia contemplar "if-down_extra.sh" y "if-up_extra.sh" para agregar instrucciones iptables manualmente.
#	- Servicio independiente «ipupdown» que, al cambiar cualquier IP, ejecute run-parts a /etc/ipupdown/actions.d (para no depender de /etc/network/if-up.d)
#	  Monitorizando el resultando de:
#	  $ Cidr="$(ip addr show dev $CurDev | grep -e 'inet ' | sed -e 's|.*inet |inet |g' | cut -f 2 -d ' ')"
#	  $ Link="$(ip link show dev $CurDev | grep -e ' state ' | sed -e 's|.* state ||g' | cut -f 1 -d ' ')"
#	  $ Link="$(Lowercase "$Link")"
#	  El evento debe dispararse al cambiar cualquiera de los valores obtenidos en una interfaz, ejecutando:
#	  Comando $Interface $Type $Event $Ip $IsGateway
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 link up
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 link down
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 ip add 192.168.1.2/24 192.168.1.1
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 ip add 192.168.1.2/24
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 ip del 192.168.1.2/24
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 default add 192.168.1.1
#	  Ejemplo: /etc/ipupdown/actions.d/MyScript eth0 default del 192.168.1.1
#	- Soportar netplan.io y systemd-networkd https://wiki.ubuntu.com/BionicBeaver/ReleaseNotes#New_since_16.04_LTS
#	- [v2.2.6] sudo durruter dnat remove
#	  Escriba el dispositivo de red atendido como entrada
#	  WAN device
#	  (enp0s25 enp48s0 lanbr0 lo venet0):
#	  (para eliminar entradas de todos los dispositivos, escriba: all )
#	  all
#	  Escriba el protocolo de comunicacion atendido
#	  (por ejemplo: tcp )
#	  (para eliminar entradas de todos los protocolos, escriba: all )
#	  all
#	  Escriba el puerto atendido en la entrada. Puede especificar uno o un rango.
#	  Public port / Input port
#	  (0-65535)
#	  (para eliminar entradas de todos los numeros de puerto, escriba: all )
#	  53
#  >>>>all: error fetching interface information: Device not found<<<<< ¿ARREGLAT amb ListaDispositivosRed()?
#	  Escriba la direccion IP publica atendida de entrada
#	  (para eliminar entradas para todas las IP publicas, escriba: all )
#	  all
#	  iptables: No chain/target/match by that name.
#	  Entrada DNAT tcp/53 deshabilitada para enp0s25/192.168.1.3
#	  Entrada DNAT tcp/53 eliminada para enp0s25/192.168.1.3
#	- [v2.2.6] sudo durruter dnat create (utilitzant l'assistent): . tcp 53 . 172.16.81.61 . "DNS" <- El comentari potser no queda desat
#	- [v2.2.6] sudo durruter dnat list <- A "equilibri" alguns comentaris queden mal formats (a causa de l'ús de guions entre espais)
#	- Acabar de repassar que "all" se suporta bé a totes les funcions de DNAT per representar que no s'especifica la IP d'entrada.
#	- Paràmetre ForceInit per evitar confiar en d'ifupdown
#	- Permetre amb el filtre: durruter nat enable all eth1 (és necessari per vpn/scripts/up-down.sh)
#	- Quan es faci servir init, TheScriptUp haurà de ser el mateix que TheScriptDown, però (ProgramilloArranqueParada) amb paràmetre «up» o «down»
#	- Apart de "default" implementar també l'especificació d'IP "all" per DNAT, per tal de no especificar --destination i que per totes les IP es faci servir la mateixa ruta.
#	- Comprovar totes les versions possibles de Ubuntu, CentOS i altres
#	- (Almenos hasta durruter 2.1) "service networking stop" emite:
#	  Deconfiguring network interfaces...to-wan: error fetching interface information: Device not found
#	- En algunos servidores, durruter dnat list & durruter nat list muestran entradas duplicadas.
#	- Cuando se habilita una DNAT para una NIC que no tiene IP:
#	  .iptables v1.4.14: Bad IP address ""
#	  Try `iptables -h' or 'iptables --help' for more information.
#	- Al intentar borrar entradas por un dispositivo que ya no existe (las elimina pero muestra el mensaje):
#	  E: No se encuentra el dispositivo eth1
#	   ¿ARREGLAT amb ListaDispositivosRed()?
#	- Al borrar la (última?) entrada DNAT para un dispositivo, todavía queda su registro SNAT en iptables.
#	- Funcionalidad para cambiar un valor masivamente (por ejemplo cambiar de eth0 a eth3, una IP o un puerto)
#	- Gestionar bloqueo de puertos para NAT http://www.cyberciti.biz/faq/iptables-block-port/
#	- Aprender el funcionamiento de wondershaper y fireqos para implementar la moderación de ancho de banda
#	- PROBLEMA POSIBLE: Hay que revisar si se elimina correctamente el contenido de /etc/durruter/to-wan al eliminar la ultima salida NAT
#	- Anglificar todos los parametros, como mutda.sh
#	- Sustituir grep por sed en donde se pueda (mayor portabilidad)
#	- Revisar emulación DMZ (DNAT predeterminado): Comprobar si, por el orden de iptables, tienen prioridad las entradas más concretas.
#	  Si no es así, habrá que establecer DMZ usando negaciones «!» de iptables.
#	  https://thejimmahknows.com/linux-network-firewall-with-iptables-and-dmz/
#	  http://www.cyberciti.biz/faq/linux-demilitarized-zone-howto/
#	- Investigar si es posible establecer NAT que distinga el puerto, por ejemplo para 
#	  encaminar las peticiones del HTTP/S por un dispositivo diferente.
#	- Dar funciones de cálculo de IP y máscaras al usuario (servicio «calculadora»)
#	- Probar en más sistemas operativos: Android, ddWRT, openWRT, Fedora, OpenSuse, Debian antiguas, Slackware, Gentoo, etc.
#	  Ya probado: Debian 6, CentOS 6
#	- Implementar la clave «local-nets» para NAT (todas las redes excepto la de 0.0.0.0). Los datos salen de la instrucción:
#		netstat -rn | grep -ve ' !' | grep -ve ' 255.255.255.255 ' | grep -ve '^0.0.0.0 '
#		(y entonces hay que descartar la red WAN)
#		* Habrá que reaccionar al evento ifup de una interfaz que puede tener una nueva IP de red local. Con ello,
#		  buscar entonces NATs registradas para local-nets de dispositivos que estén activos.
#	- Preveer la existencia de negaciones «!» en iptables
#	- Preveer IPv6
#	- Many iptables configurations drop IPSEC packets! To pass IPSEC, use:
#	  'iptables -A xxx -p 50 -j ACCEPT' and 'iptables -A xxx -p 51 -j ACCEPT' 
#	  http://lartc.org/lartc.html#LARTC.IPSEC.INTRO
#	- Ebtables is used to set up, maintain, and inspect the tables of Ethernet frame rules in the Linux kernel. It is analogous to iptables, but operates at the MAC layer rather than the IP layer.
#	- Add "public" keyword to refer detected IP in internet with PublicIP()
#	- DNAT: Establecer lógica de superposición de RDev como una NIC explícita sobre «to-wan» cuando coinciden; para establecer la explícita primero hay que deshabilitar la de to-wan.
#	- DNAT: Establecer lógica de superposición de InputIp como una IP explícita sobre «default» cuando coinciden; para establecer la explícita primero hay que deshabilitar la de default.
#	- Different test for iproute v.20051007 (At Ubuntu 6.10 syntax "ip address show" isn't supported)
#	- durruter d/nat import Fichero : Debería admitir ruta relativa.
#	- Admitir '*' para representar todas las IP de entrada, al crear para DNAT, y en tal caso no especificar IP en la entrada iptables.
#	- Desarrollar sintaxis nat|dnat enable/$Comment disable/$Comment remove/$Comment para actuar sobre entradas identificadas por el comentario.
#	- Acción «edit/$Comment» para reemplazar entradas NAT/DNAT identificadas por el comentario.
#	- Implementar posibilidad de nombres en lugar de IPs, de manera que, al levantar una entrada NAT/DNAT, se resuelva el nombre para establecer la IP.
#	- acción "dnat dest <port>" para reportar a qué destino van las entradas por un número de puerto.
#	- Implementar list/enable/disable/remove por una cadena de la descripción#
#	  durruter nat remove all "Texto"
#	- Preparar compatibilidad con nftables (Netfilter)
#	- Unban4Fail y ListBans4Fail deben poderse basar en iproute en lugar de fail2ban, para hacerlo genérico.
#	- Implementar: durruter via add-net-gw 172.16.10.105/32 100.64.13.77 Nombre
#	  para forzar tráfico hacia un tunel o lo que sea, que serán estos comandos:
#		(inscribir Nombre en /etc/iproute2/rt_tables)
#		ip rule add from 172.16.10.105/32 table Nombre
#		ip route add default via 100.64.13.77 dev SuNicDeSalida table Nombre
#	- "durruter dnat export" muestra entradas activas duplicadas:
#		Cuando la IP atendida está registrada como «default», que se distingue de la IP atendida activa en iptables
#		Cuando los puertos de destino están registrados como «=», que se distingue de la falta de puerts («*») especificados en iptables


##### PARAMETERS ABOUT SERVICE NATURE #####

# ServiceName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ServiceName="durruter"
# DependsOnSoftware: Space separated list of required programs. Each item's syntaxes: executable/package ExecutableOption1:ExecutableOption2/PackageOption1|PackageOption2...
DependsOnSoftware="cut/coreutils|debianutils grep sed perl/perl-base bc pstree/psmisc start-stop-daemon:daemonize:screen/dpkg|daemonize|screen iptables:iptables-legacy/iptables iptables-save:iptables-legacy-save/iptables iptables-restore:iptables-legacy-restore/iptables ifconfig/net-tools ip/iproute netstat/net-tools find/findutils run-parts/debianutils"
RecommendedSoftware="geoiplookup/geoip-bin geoiplookup/geoip-database wget:curl/wget|curl"
SuggestedSoftware=""
SystemConfigDir="/etc/${ServiceName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# DisabledAtFirst: If installer will disable the loading as a system service at first installation (1=Yes 0=No)
DisabledAtFirst=1
# DaemonizeOnStart: If main program will fork in a background process on start and return control inmediately (1), or will run in the foreground and return control when completed (0)
DaemonizeOnStart=0
# MainControllerStays: If main program remains running until is ordered to stop (1), or ends itself when one-shot task is completed (0)
MainControllerStays=0
# StopStartOnUpgrade: If old service process is to be stopped when upgrading program (and after started again if it was running)
StopStartOnUpgrade=0
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=1
# GentleStopTimeoutS: Maximum seconds to wait daemons at stop signal, before forcing kill processes. Intended for childs and {main x ChildsNr}
GentleStopTimeoutS=30
# TimeoutStartSec: When starting main program, if it doesn't complete ond/or fork, after this number of seconds systemd/upstart will shut down the service. -1 for infinite (recommended on MainControllerStays==0)
TimeoutStartSec=-1
# TimeoutStopSec: When stopping service, if it doesn't complete, after this number of seconds systemd will kill main process. -1 for infinite
TimeoutStopSec=4
# Leave empty to not treat child objects
ChildSingularName=""
# Leave empty to not treat child objects. Plural name has implies profiles directory names.
ChildsPluralName=""
# ChildProfileExtension: Leave empty to assume profiles in subdirectories instead of files
ChildProfileExtension="conf"
# DaemonizeChilds: If child objects are background processes (1) or are to run serialized tasks in the foreground (0)
DaemonizeChilds=1
# ServiceDocumentation: A space-separated list of URIs as http:// https:// file: info: man:
ServiceDocumentation="https://www.actiu.net/durruter/"
# Configuration default value for _LoadWithCalm
Child_LoadWithCalm_Default=-1
# Configuration default value for _MoreLoadsInterval
Childs_MoreLoadsInterval_Default=0
# If "status" action shows (1) processes and memory state or not (0). If not, only customized Status_More and ProcessStatus_More functions will be called.
StatusProcecessShow=0

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

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
	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)"
	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 7)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"
	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)"
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fUNDERL="$(tput smul)"
	fUNDERLx="$(tput rmul)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${cfWHITE}${cbDGRAY}${fBOLD}$(tput bel)"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
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=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
fi

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"
# 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
			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
			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
}

Is_Executable ()
# Syntax as a function: Is_Executable $Command
# Description: Returns (exitcode 0) TRUE if specified command's argument is an existing 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
}

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
}

EliminarTemporalSeguro ()
# Sintaxis como instrucción: EliminarTemporalSeguro "$RutaFichero"
# Descripción:
#	Borra el fichero o directorio especificado, y los directorios de su ruta que no estén vacíos.
#	Por ejemplo, de "/run/shm/usuario/Rutinas/20120821-091624.qBG8.ext"
#	borra 20120821-091624.qBG8.ext , Rutinas y usuario.
# Nota: El borrado de fichero es irrecuperable
# Parámetros esperados:
#	$1	Ruta de un fichero existente
# Depends on functions: Dirname
{
	local RutaFichero="$1"
	local DirActual=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$RutaFichero" != "" ] ; then
		if [ -e "$RutaFichero" ] ; then
			cat /dev/null > "$RutaFichero"
			rm -r "$RutaFichero"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		DirActual="$(Dirname "$RutaFichero")"
#		while [ "$DirActual" != "" ] && [ "$DirActual" != "/" ] && [ "$(basename "$DirActual")" != "tmp" ] ; do	Problems with a path begun with "-" in old basename versions
		while [ "$DirActual" != "" ] && [ "$DirActual" != "/" ] && [ "$(printf '%s\n' "$DirActual" | tr -s '/' '\n' | tail -n 1)" != "tmp" ] ; do
			if [ -d "$DirActual" ] ; then
				rmdir --ignore-fail-on-non-empty "$DirActual"
			fi
			DirActual="$(Dirname "$DirActual")"
		done
	else
		printf '%s\n' "PROBLEMA (EliminarTemporalSeguro): Fichero no especificado." 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes: 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
}

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
				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
}

Uppercase ()
# Description: Converts specified string to upper 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 '[:lower:]' '[:upper:]'
		else
			if Is_Executable awk ; then
				printf '%s\n' "$OriginalString" | awk '{print toupper($0)}'
			else
				if Is_Executable sed && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:lower:]]?)/\U\1/g'
				fi
			fi
		fi
	fi
}

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
{
	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="$(date '+%Y-%m-%dT%T%z')"
		fi
		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' "E: Log file not specified" 1>&2
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

LogProgram ()
# Syntax as a sentence: LogProgram $ThisLevel $Message
# Description: Writes message to configured log file, depending on configured LogLevel vs ThisLevel 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.
# Depends on functions: Is_IntegerNr Dirname LogToFile
# Depends on software packages: perl-base sed
# Supports environment variables: LogLevel MainLog MainControllerLog INIT_SCRIPT_InitCall MaxLogLines
{
	local ThisLevel="$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 [ $ThisLevel -ge 0 ] ; then
		DatetimeStamp=1
	fi
	ThisLevel="$(printf '%s\n' "$ThisLevel" | sed -e 's|^-||g')"
	if Is_IntegerNr "$LogLevel" ; then ProgramLogLevel=$LogLevel ; fi
	if [ "$*" != "" ] && [ $ProgramLogLevel -ge $ThisLevel ] && [ $ThisLevel -gt 0 ] ; then
		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 [ $ThisLevel -eq 3 ] ; then
			printf '%s\n' "$AskedText"
		else
			if [ $ThisLevel -eq 2 ] ; then
				printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
			else
				if [ $ThisLevel -eq 1 ] ; then
					printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
				else
					printf '%s\n' "$AskedText" 1>&2
				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
			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
}

NumeroResumit ()
# Syntax as a function: $(NumeroResumit $Numero $Estil)
# Retorna el número humanament llegible, segons $Estil:
# 	"2" :	Amb factors de 1024 (10,5b; 10,5k; 10,5m; 10,5g)
# 	"2L" :	Amb factors de 1024 i notació llarga (10,5B; 10,5KiB; 10,5MiB; 10,5GiB)
# 	"1" :	Amb factors de 1000 (10,5B; 10,5K; 10,5M; 10,5G) (SI)
# 	"." :	Utilitzar el separador decimal "." en comptes del predeterminat ","
#	Si s'afegeix un espai, se separa el número de la notació amb un espai:
#		"2 "   10,5 g
#		"2L "  9,5 MiB
#		"1 "   8300,5 B
#	Si s'afegeix una "A", aleshores s'alineen les dades:
#		"A2"     10,5g
#		"A2L"     9,5MiB
#		"A1"   8300,5B
#	Si s'afegeix un "0", aleshores no s'expressen decimals (només l'enter arrodonit a la baixa):
#		"02"  10g
#		"02L" 9MiB
#		"01"  8300B
#	Tot es pot combinar:
#		"A02L "     9 MiB
#		"A01L "  8300 B
#		"2L."     9.5MiB
# Nota: el $Numero s'espera en bytes (octets)
# ToDo:
#	- Try to not depend on bc
# Depends on functions: (none)
# Depends on software packages: grep, bc
{
	local Numero=$1
	local Estil="$2"
	local LlindarResum=2000
	local DecimalSeparator=''
	local CurLocale=''
	local EnterVisible=''
	local DecimalVisible=''
	local Sufix=''
	local Factor=0
	
	if [ "$(printf '%s' "$Estil" | grep "\.")" != "" ] ; then DecimalSeparator="." ; fi
	if [ "$(printf '%s' "$Estil" | grep ",")" != "" ] ; then DecimalSeparator="," ; fi
	if [ "$(printf '%s' "$Estil" | grep "'")" != "" ] ; then DecimalSeparator="'" ; fi	#"
	if [ "$DecimalSeparator" = "" ] ; then
		DecimalSeparator=','
		for CurLocale in "$LC_NUMERIC" "$LC_ALL" "$LANG" "$LANGUAGE" ; do
			CurLanguage="$(printf '%s' "$LC_NUMERIC" | cut -f 1 -d '_' | cut -f 1 -d '-')"
			if [ "$CurLanguage" = "en" ] || [ "$CurLanguage" = "EN" ] ; then
				DecimalSeparator='.'
			fi
		done
	fi
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then
		Factor=1024
	else
		if [ "$(printf '%s' "$Estil" | grep "1")" != "" ] ; then
			Factor=1000
		else
			return 1
		fi
	fi
	EnterVisible="$Numero"
	Sufix="B"
	# Resum
	if [ $Numero -ge $LlindarResum ] || [ $(printf '%s\n' "$Numero" | sed -e 's|.....$||g') -ge $LlindarResum ] ; then
		EnterVisible="$(echo "$Numero / $Factor" | bc)"
		DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
		Numero=$(echo "$Numero / $Factor" | bc)
		Sufix="K"
		if [ $Numero -ge $LlindarResum ] ; then
			EnterVisible="$(echo "$Numero / $Factor" | bc)"
			DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
			Numero=$(echo "$Numero / $Factor" | bc)
			Sufix="M"
			if [ $Numero -ge $LlindarResum ] ; then
				EnterVisible="$(echo "$Numero / $Factor" | bc)"
				DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
				Numero=$(echo "$Numero / $Factor" | bc)
				Sufix="G"
				if [ $Numero -ge $LlindarResum ] ; then
					EnterVisible="$(echo "$Numero / $Factor" | bc)"
					DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
					Numero=$(echo "$Numero / $Factor" | bc)
					Sufix="T"
					if [ $Numero -ge $LlindarResum ] ; then
						EnterVisible="$(echo "$Numero / $Factor" | bc)"
						DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
						Numero=$(echo "$Numero / $Factor" | bc)
						Sufix="P"
						if [ $Numero -ge $LlindarResum ] ; then
							EnterVisible="$(echo "$Numero / $Factor" | bc)"
							DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
							Numero=$(echo "$Numero / $Factor" | bc)
							Sufix="E"
							if [ $Numero -ge $LlindarResum ] ; then
								EnterVisible="$(echo "$Numero / $Factor" | bc)"
								DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
								Numero=$(echo "$Numero / $Factor" | bc)
								Sufix="Z"
								if [ $Numero -ge $LlindarResum ] ; then
									EnterVisible="$(echo "$Numero / $Factor" | bc)"
									DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
									Numero=$(echo "$Numero / $Factor" | bc)
									Sufix="Y"
								fi
							fi
						fi
					fi
				fi
			fi
		fi
	fi
	# Decimal invisible
	if [ "$(printf '%s' "$Estil" | grep "0")" != "" ] ; then
		DecimalVisible=''
	else
		if [ "$DecimalVisible" = "" ] ; then DecimalVisible="0" ; fi
		DecimalVisible="$DecimalSeparator$DecimalVisible"
	fi
	# Notació llarga
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then  # factor binari, sufix en minuscula
		case "$Sufix" in
			"B" )	Sufix="b"	;;
			"K" )	Sufix="k"	;;
			"M" )	Sufix="m"	;;
			"G" )	Sufix="g"	;;
			"T" )	Sufix="t"	;;
			"P" )	Sufix="p"	;;
			"E" )	Sufix="e"	;;
			"Z" )	Sufix="z"	;;
			"Y" )	Sufix="y"	;;
		esac
		if [ "$(printf '%s' "$Estil" | grep "L")" != "" ] ; then
			case "$Sufix" in
				"b" )	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
						Sufix="B  "	# Amb aliniació
					else
						Sufix="B"
					fi
					;;
				"k" )	Sufix="KiB"	;;
				"m" )	Sufix="MiB"	;;
				"g" )	Sufix="GiB"	;;
				"t" )	Sufix="TiB"	;;
				"p" )	Sufix="PiB"	;;
				"e" )	Sufix="EiB"	;;
				"z" )	Sufix="ZiB"	;;
				"y" )	Sufix="YiB"	;;
			esac
		fi
	fi
	# Espai
	if [ "$(printf '%s' "$Estil" | grep -e ' ')" != "" ] ; then
		Sufix=" $Sufix"
	fi
	# Aliniació
	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
		while [ ${#EnterVisible} -lt 4 ] ; do
			EnterVisible=" $EnterVisible"
		done
	fi
	printf '%s\n' "$EnterVisible$DecimalVisible$Sufix"
}

DependenciasFaltan ()
# Sintaxis como función: $(DependenciasFaltan "$ListaDependencias")
# 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:
#	- 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=''
	
	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=''
		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
}

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' >> "$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' >> "$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' >> "$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' >> "$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
	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
#	$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
	return $StatusCode
}

LoadVarsValues ()
# Syntax as a sentence: LoadVarsValues $FileOrContent $VariablesNames $VarsRequired $DefaultsFileOrContent
# Descripcion: Runs 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.
#	$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)
#	$3	(optional or empty) Variable names to filter (case sensitive, space separated). If not specified, all file variables will be loaded.
#	$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
		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
				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
}

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)
#		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:
#	- No headers are included
#	- Columns are separated by only one space character
#	- Every unknown value is returned as '?'
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep sed
{
	local OutputColumns="$1"
	local FilterPids="$2"
	local CurValue=''
	local CurPid=''
	local CurColumn=''
	local CurLine=''
	local PidMaxLen=0
	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
		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
			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')"
		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')"
				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.
						* )
							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
}

ParentPstree ()
# Alternative to pstree -p -s (syntax not available in 1990's or 2000's distributions)
# Depends on functions: Is_IntegerNr PsOutputValues
# Depends on software packages: sed, procps
{
	local ChildPid="$1"
	local CurrentPid=''
	local Value=''
	
	CurrentPid=$ChildPid
	while Is_IntegerNr "$CurrentPid" && [ "$CurrentPid" != "0" ] ; do
		Value="$CurrentPid $Value"
		CurrentPid=$(echo TrimAndSingle $(PsOutputValues ppid $CurrentPid | tail -n 1) | 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
}

PercentLoad ()
# Returns the load average last 1|5|15 minutes x 100 / CPU cores
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep, sed, perl-base
{
	local Minutes="$1"
	local MinutesColumn=0
	local CpuCoresNr=0
	local Value=''
	
	case "$Minutes" in
		1 ) MinutesColumn=1 ;;
		5 ) MinutesColumn=2 ;;
		15 ) MinutesColumn=3 ;;
	esac
	if [ $MinutesColumn -ne 0 ] ; then
		CpuCoresNr=$(ArgumentsNumber () { printf '%s' $#; }; ArgumentsNumber $(grep "processor" /proc/cpuinfo | cut -f 2 -d ':'))
		if ! Is_IntegerNr "$CpuCoresNr" ; then CpuCoresNr=1 ; fi
		Value="$(echo TrimAndSingle $(env LANG=en w | head -n 1 | perl -pe 's|load.*:|\n|g' | tail -n 1) | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f $MinutesColumn -d ' ' | sed -e 's|,||g' | sed -e 's|\.||g')"
		while [ "$(printf '%s\n' "$Value" | grep -e '^0')" != "" ] && [ "$Value" != "0" ] ; do
			Value="$(printf '%s\n' "$Value" | cut -c 2-)"
		done
		if Is_IntegerNr "$Value" ; then
			printf '%s\n' "$((Value / CpuCoresNr))"
		fi
	fi
}

WaitForCalm ()
# Syntax as a sentence: WaitForCalm $MaxPercent [$StopFile] [$MaxSeconds] [$PrecisionSeconds]
# Description: Sleeps until load average is descending and under specified percent.
# Expected parameters:
#	$1	Average load expecting to be lower (x100)
#	$2	(optional) Signal file to end unconditionally when it's present.
#	$3	(optional) Maximum seconds number to wait. If it's specified and $MaxPercent is not reached down, after this seconds wait will end.
#	$4	(optional) Number of seconds for waiting check period. Default is 1
#	
# Depends on functions: PercentLoad Is_IntegerNr
# Depends on software packages: (none)
{
	local MaxPercent="$1"
	local StopFile="$2"
	local MaxSeconds="$3"
	local PrecisionSeconds="$4"
	local WaitFractionS=0
	local CurSecondsLeft=0
	local Load1=''
	local LastLoad1=0
	local OldLoad1=0
	local Load5=''
	local Max5=''
	local Calm=''
	local TotalSeconds=0
	
	if ! Is_IntegerNr "$MaxSeconds" || [ $MaxSeconds -le 0 ] ; then MaxSeconds=999999999999999999 ; fi
	if ! Is_IntegerNr "$PrecisionSeconds" ; then PrecisionSeconds=1 ; fi
	WaitFractionS=$((PrecisionSeconds / 2))
	if [ $WaitFractionS -lt 1 ] ; then WaitFractionS=1 ; fi
	if [ $WaitFractionS -gt 2 ] ; then WaitFractionS=2 ; fi
	Max5=$MaxPercent
	if [ $Max5 -lt 100 ] ; then Max5=100 ; fi
	Load1=$(PercentLoad 1)
	Load5=$(PercentLoad 5)
	if [ $Load1 -le $Load5 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
		Calm=1
	fi
	if [ $Load1 -le $((MaxPercent / 2)) ] && [ $Load5 -le $((Max5 / 2)) ] ; then
		Calm=1
	fi
	while [ "$Calm" != "1" ] && [ $TotalSeconds -lt $MaxSeconds ] && [ ! -f "$StopFile" ] ; do
		CurSecondsLeft=$PrecisionSeconds
		while [ $CurSecondsLeft -gt 0 ] && [ ! -f "$StopFile" ] ; do
			sleep $WaitFractionS
			CurSecondsLeft=$((CurSecondsLeft - WaitFractionS))
			TotalSeconds=$((TotalSeconds + WaitFractionS))
		done
		Load1=$(PercentLoad 1)
		Load5=$(PercentLoad 5)
		if [ $Load1 -le $LastLoad1 ] &&  [ $LastLoad1 -le $OldLoad1 ] &&  [ $Load1 -lt $OldLoad1 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
			# If load is currently descending and indicators (1,5) aren't over MaxPercent, calm assumed.
			Calm=1
		fi
		if [ $Load1 -le $Load5 ] && [ $Load1 -le $MaxPercent ] && [ $Load5 -lt $Max5 ] ; then
			# If load is generally descending and indicators (1,5) aren't over MaxPercent, calm assumed.
			Calm=1
		fi
		if [ $Load1 -le $((MaxPercent / 2)) ] && [ $Load5 -le $((Max5 / 2)) ] ; then
			# If none of indicators (1,5,15) are over half MaxPercent, calm assumed too.
			if [ $(PercentLoad 15) -le $((Max5 / 2)) ] ; then
				Calm=1
			fi
		fi
		if [ $Load1 -le 0 ] ; then
			Calm=1
		fi
		LastLoad1=$Load1
		OldLoad1=$LastLoad1
	done
}

PidfileStatus ()
# Only returns exit code about status
# Exit codes same as start-stop-daemon:
#	0: Program is running
#	1: Program is not running and the pid file exists
#	3: Program is not running
#	4: Unable to determine program status
# Depends on functions: Is_Executable Is_IntegerNr
# Depends on software packages: grep, sed, pstree/psmisc
{
	local PidFile="$1"
	local Value=''
	local PID=''
	local SSD="start-stop-daemon"

	if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
	if [ "$("$SSD" --help 2>&1 | grep -e '--status')" != "" ] ; then
		# Some old versions haven't status parameter.  #'
		"$SSD" --pidfile "$PidFile" --status
		Value=$?
	else
		if [ -f "$PidFile" ] ; then
			PID=$(cat "$PidFile" | sed -e 's| ||g')
			if Is_IntegerNr $PID ; then
				if [ "$(pstree $PID)" != "" ] ; then
					Value=0
				else
					Value=1
				fi
			else
				Value=4
			fi
		else
			Value=3
		fi
	fi
	return $Value
}

ExistingPIDs ()
# Description: From PIDs specified, returns (stdout) the existing ones as processes.
# Depends on functions: (none)
# Depends on software packages: sed, pstree/psmisc
{
	local CurrentPid=''
	local Value=''
	
	for CurrentPid in $* ; do
		if [ "$(pstree $CurrentPid)" != "" ] ; then
			Value="$Value $CurrentPid"
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

KillPid ()
# Description: Scales signals and walks PID subtree to force a termination.
# Note: Specified PID must exist to find subtree.
# Depends on functions: ExistingPIDs Is_IntegerNr
# Depends on software packages: grep, pstree/psmisc
{
	local Pid=$1
	local TimeoutS=$2
	local PidTree=''
	local CurrentPid=''
	local TimeoutCount=0
	local TempValue=''
	local OldSleep=''
	local MultipleCount=2
	local FractionSecond=0.5
	local LastStatus=0
	local StatusCode=0

	PidTree="$(env COLUMNS=999 pstree -p $Pid)"
	if [ "$PidTree" != "" ] ; then
		# Sed upto v3 doesn't support newlines as replacement  #'
		#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
		TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
		PidTree=''
		for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
		done
		NextPidTree=''
		for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -15 $CurrentPid 2>/dev/null
			fi
		done
		TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
		# Sleep upto v2 (and maybe -v4) only support integers
		OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
		if [ "$OldSleep" != "" ] ; then
			MultipleCount=1
			FractionSecond=1
		fi
		TimeoutCount=$((TimeoutCount * MultipleCount))
		while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
			sleep $FractionSecond
			TimeoutCount=$((TimeoutCount - 1))
		done
		PidTree="$(ExistingPIDs $PidTree)"
		for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -9 $CurrentPid 2>/dev/null
			fi
		done
		if [ "$PidTree" != "" ] ; then
			TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutS - TimeoutCount)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutCount * MultipleCount))
			while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep $FractionSecond
				TimeoutCount=$((TimeoutCount - 1))
			done
			if [ "$(ExistingPIDs $PidTree)" != "" ] ; then
				LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

StopPidFile ()
# Description: Stops daemon and walks whole PID subtree to kill signals.
# Depends on functions: Is_Executable Is_IntegerNr PidfileStatus ExistingPIDs KillPid
# Depends on software packages: pstree/psmisc
{
	local PidFile="$1"
	local TimeoutEachTry=$2
	local TimeoutCount=0
	local MainPid=''
	local PidTree=''
	local CurrentPid=''
	local TempValue=''
	local LastStatus=0
	local SSD="start-stop-daemon"
	local LastStatus=0
	local StatusCode=0

	if [ -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		if [ "$TimeoutEachTry" = "" ] ; then TimeoutEachTry=1 ; fi
		MainPid="$(cat "$PidFile" | sed -e 's| ||g')"
		PidTree="$(env COLUMNS=999 pstree -p $MainPid)"
		if [ "$PidTree" != "" ] ; then
			# Sed upto v3 doesn't support newlines as replacement  #'
			#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
			TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
			PidTree=''
			for CurrentPid in $TempValue ; do
				if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
			done
			if Is_Executable "$SSD" ; then
				PidfileStatus "$PidFile"
				LastStatus=$?
				"$SSD" --stop --pidfile "$PidFile"
				LastStatus=$?
				if [ $LastStatus -ne 1 ] && [ $LastStatus -ne 3 ] ; then
					# May be running before; we can pass error status result.
					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			TimeoutCount=$TimeoutEachTry
			while [ "$(pstree -p $MainPid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep 1
				TimeoutCount=$((TimeoutCount - 1))
			done
			PidTree="$(ExistingPIDs $PidTree)"
			for CurrentPid in $PidTree ; do
				# This walk is necessary because MainPid can be already killed, but not all the tree.
				KillPid $CurrentPid $TimeoutEachTry
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
		if [ "$(ExistingPIDs $PidTree)" = "" ] ; then
			if [ -f "$PidFile" ] ; then
				rm "$PidFile"
			fi
		else
			LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi

	return $StatusCode
}

DaemonizeCommand ()
# Syntax as a sentence: DaemonizeCommand "$PidFile" "$ExecutablePath" $parameters
# Description: 
# Expected parameters:
#	$1	File where to register the forked process PID, useful to control its execution
#	$2	Program pathname as expected by --startas parameter for start-stop-daemon, but necessary in any mode for execution  #do
#	(rest)	Parameters to pass to the executable
# Notes:
#	- Forks with any of start-stop-daemon[Debian] or daemonize[RHEL] or screen
#	- If no daemonize software is available, ampersand method will be used, BUT: Never call DaemonizeCommand inside a $(subshell) call because the $(subshell) only ends when childs end.
#	- An environment variable can be set: DaemonizeWith=screen to force use of "screen" if available.
# PENDENT:
#	- Preveure ús de ampersand
# Depends on functions: Is_Executable Is_IntegerNr Dirname PsOutputValues
# Depends on software packages: grep, sed, dpkg>=1.6|daemonize|screen, pstree/psmisc
{
	local PidFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ExecutablePath="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local PidFileDir=''
	local Pid=''
	local SSD="start-stop-daemon"
	local Regexp1=''
	local Regexp2=''
	local ProcessName=''
	local LastStatus=0
	local StatusCode=0

	Pid="$(cat "$PidFile" 2>/dev/null)"
	if Is_IntegerNr $Pid ; then
		if [ "$(pstree $Pid)" = "" ] ; then
			# Dead PID
			rm -f "$PidFile"
		fi
	else
		# Bad $PidFile or not exist
		rm -f "$PidFile"
	fi
	if [ "$DaemonizeWith" != "" ] && ! Is_Executable "$DaemonizeWith" ; then DaemonizeWith='' ; fi
	if [ ! -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		PidFileDir="$(Dirname "$PidFile")"
		if [ ! -d "$PidFileDir" ] ; then
			mkdir -p "$PidFileDir"
			chmod u=rwX,g=rX,o= "$PidFileDir"
		fi
		if Is_Executable "$SSD" && [ "$("$SSD" --help 2>&1 | grep -e '--background')" != "" ] && [ "$("$SSD" --help 2>&1 | grep -e '--make-pidfile')" != "" ] && [ "$DaemonizeWith" = "" ] ; then
			# Without --background option, start-stop-daemon doesn't fork.  #'
			# Without --make-pidfile, PID is difficult to locate (a sub-script should be created).
			"$SSD" --start --pidfile "$PidFile" --make-pidfile --background --startas "$ExecutablePath" -- "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if Is_Executable daemonize && [ "$DaemonizeWith" = "" ] ; then
				daemonize -p "$PidFile" "$ExecutablePath" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if Is_Executable screen ; then
					ProcessName="$(printf '%s' "$PidFile" | tr -s '/' '\n' | tail -n 1 | cut -f 1 -d '.')"
					if [ "$ProcessName" != "" ] ; then ProcessName="${ProcessName}-" ; fi
					ProcessName="${ProcessName}$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')"
					screen -d -m -S "$ProcessName" "$ExecutablePath" "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					Pid="$(echo Trim$(screen -list | sed -e 's|	| |g' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^Trim||g' | sed -e 's|^ ||g' | sed -e 's| ||g')"
					Pid="$(echo TrimAndSingle $(screen -list | tr -s '\t' ' ' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | sed -e 's| ||g')"
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				else
#					printf '%s\n' "${sERROR}E: No program to launch command in the background ${ParO}start-stop-daemon/daemonize/screen${ParC}${fRESET}" 1>&2
#					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					"$ExecutablePath" "$@" &
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Pid=$!
					if ! Is_IntegerNr $Pid ; then
						# Careful for quotes in arguments
						if [ $# -gt 0 ] ; then
							Regexp1="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
							if [ $# -gt 1 ] ; then
								Regexp2="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}.* .*${Regexp2}" | tail -n 1 | cut -f 1 -d ' ')"
							else
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}" | tail -n 1 | cut -f 1 -d ' ')"
							fi
						else
							Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath" | tail -n 1 | cut -f 1 -d ' ')"
						fi
					fi
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Cannot launch a new process over an existing pidfile ${ParO}${PidFile}${ParC} with running PID ${Pid}${fRESET}" 1>&2
		LastStatus=113 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

TimedExecution ()
# Syntax as a function: $(TimedExecution $TimeoutSeconds $OpportunitiesNr "$StopFile" $command)
# Description: Executes a command with a hard timeout. Returns (stdout) execution's output.  #'
# Expected parameters:
#	$1	Number of seconds to wait the command completes (pased this time, process is killed)
#	$2	Maximum number of executions to try getting a clean exit of the command
#	$3	(optional or empty) File that will be a force signal to terminate inmediately, when exists (will not be removed).
#	(rest)	Command to execute
# Depends on functions: Is_Executable Is_IntegerNr DaemonizeCommand StopPidFile PidfileStatus
# Depends on software packages: grep, dpkg|daemonize
{
	local TimeoutSeconds=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local OpportunitiesNr=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local StopFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local TimedTempDir=''
	local RemainingCount=0
	local RemainingOpportunities=0
	local SuccessTry=0
	local CurrentPid=''
	local PidFile=''
	local ReposeTime=0
	local CurrentParameter=''
	local ParameterNr=-1
#	local MultipleCount=2
#	local FractionSecond=0.5
	local StatusCode_Tmp=0
	local Indentation1='	'
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if [ $# -le 0 ] ; then
		printf '%s\n' "${sERROR}E: No command specified for TimedExecution.${fRESET}" 1>&2
		LastStatus=86 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_IntegerNr "$TimeoutSeconds" || ! Is_IntegerNr "$OpportunitiesNr" ; then
		printf '%s\n' "${sERROR}E: Bad TimeoutSeconds or OpportunitiesNr for TimedExecution.${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		TimedTempDir="${DirTempX}/TimedExecution.$$"
		rm -fr "$TimedTempDir"
		mkdir "$TimedTempDir"
		chmod u=rwX,g=rX,o= "$TimedTempDir"
		printf '%s\n' '#!/bin/sh' > "${TimedTempDir}/logger.sh"
		chmod u=rwx,go= "${TimedTempDir}/logger.sh" # Closed permissions to secure passwords there
		if ! Is_Executable timeout ; then
			Indentation1=''
		fi
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'if [ "$1" != "timeout" ] ; then' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' "	timeout --signal=9 --kill-after=0 $TimeoutSeconds \"\$0\" timeout" >> "${TimedTempDir}/logger.sh"
			printf '%s\n' '	StatusCode=$?' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' 'else' >> "${TimedTempDir}/logger.sh"
		fi
		for CurrentParameter in "$@" ; do
			ParameterNr=$((ParameterNr + 1))
			if [ $ParameterNr -gt 0 ] ; then
				printf '%s' ' ' >> "${TimedTempDir}/logger.sh"
			else
				printf '%s' "$Indentation1" >> "${TimedTempDir}/logger.sh"
			fi
			if [ "$CurrentParameter" = "" ] ; then
				printf '%s' "''" >> "${TimedTempDir}/logger.sh"
			else
				if [ "$(printf '%s\n' "$CurrentParameter" | grep -e ' ')" != "" ] ; then
					printf '%s' "\"${CurrentParameter}\"" >> "${TimedTempDir}/logger.sh"
				else
					printf '%s' "$CurrentParameter" >> "${TimedTempDir}/logger.sh"
				fi
			fi
		done
		printf '%s\n' " > \"${TimedTempDir}/log.txt\" 2>&1" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}StatusCode=\$?" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}printf '%s\n' \$StatusCode > \"${TimedTempDir}/StatusCode.num\"" >> "${TimedTempDir}/logger.sh"
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'fi' >> "${TimedTempDir}/logger.sh"
		fi
		printf '%s\n' 'exit $StatusCode' >> "${TimedTempDir}/logger.sh"
		RemainingOpportunities=$OpportunitiesNr
		while [ $RemainingOpportunities -gt 0 ] && [ $SuccessTry -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
			PidFile="${TimedTempDir}/logger.pid"
			DaemonizeCommand "$PidFile" "${TimedTempDir}/logger.sh"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			# Sleep upto v2 (and maybe -v4) only support integers
#			OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
#			if [ "$OldSleep" != "" ] ; then
#				MultipleCount=1
#				FractionSecond=1
#			fi
#			RemainingCount=$((TimeoutSeconds * MultipleCount))
			RemainingCount=$TimeoutSeconds
			while [ $RemainingCount -gt 0 ] && [ $StatusCode -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
#				sleep $FractionSecond
				sleep 1
				RemainingCount=$((RemainingCount - 1))
			done
			if [ -f "$PidFile" ] ; then
				PidfileStatus "$PidFile"
				StatusCode_Tmp=$?
				if [ $StatusCode_Tmp -eq 0 ] ; then
					# 0: Program is running
					StatusCode_Tmp=109
				else
					StatusCode_Tmp=0
				fi
				StopPidFile "$PidFile" $TimeoutSeconds
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then LastStatus=$StatusCode_Tmp ; fi
			fi
			if [ -f "${TimedTempDir}/StatusCode.num" ] ; then
				LastStatus="$(cat "${TimedTempDir}/StatusCode.num")"
				if Is_IntegerNr $LastStatus ; then
					if [ $LastStatus -eq 0 ] ; then SuccessTry=1 ; fi
				else
					LastStatus=110
				fi
			else
				if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
			fi
			RemainingOpportunities=$((RemainingOpportunities - 1))
			if [ $SuccessTry -eq 0 ] && [ $RemainingOpportunities -gt 0 ] ; then
				ReposeTime=$((TimeoutSeconds / 2))
				if [ $ReposeTime -gt 15 ] ; then ReposeTime=15 ; fi
				RemainingCount=$ReposeTime
				while [ $RemainingCount -gt 0 ] && [ ! -f "$StopFile" ] ; do
					sleep 1
					RemainingCount=$((RemainingCount - 1))
				done
			fi
		done
		if [ $SuccessTry -eq 0 ] ; then
#			if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
#			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] && [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "Timeout:${OpportunitiesNr}x${TimeoutSeconds}s." 1>&2
				LastStatus=109
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ -f "${TimedTempDir}/log.txt" ] ; then
			cat "${TimedTempDir}/log.txt"
			rm "${TimedTempDir}/log.txt"
		fi
		rm -r "$TimedTempDir"
	fi
	return $StatusCode
}


##### INITSCRIPT TEMPLATE BASE FUNCTIONS #####

LogService_DEPRECATED ()
# Syntax as a sentence: LogService $ThisLevel $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.
# Depends on functions: Dirname
# Depends on software packages: (none)
# Depends on environment variables: LogLevel MainControllerLog INIT_SCRIPT_InitCall
{
	local ThisLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local DatetimeStamp=''
	local AskedText=''
	local CurString=''
	
	if [ $ThisLevel -ge 0 ] ; then
		DatetimeStamp="$(date '+%Y-%m-%dT%T%z')"
	fi
	ThisLevel="$(printf '%s\n' "$ThisLevel" | sed -e 's|^-||g')"
	if [ "$*" != "" ] && [ $LogLevel -ge $ThisLevel ] && [ $ThisLevel -gt 0 ] ; then
		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 [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
			CallType=" ${ParO} Manual ${ParC}"
		fi
		if [ $ThisLevel -eq 3 ] ; then
			printf '%s\n' "$AskedText"
		else
			if [ $ThisLevel -eq 2 ] ; then
				printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
			else
				if [ $ThisLevel -eq 1 ] ; then
					printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
				else
					printf '%s\n' "$AskedText" 1>&2
				fi
			fi
		fi
		if [ "$MainControllerLog" != "" ] ; then
			mkdir -p "$(Dirname "$MainControllerLog")"
			printf '%s\n' "${DatetimeStamp}${CallType} $AskedText" >> "$MainControllerLog"
		fi
	fi
}

LogService_Begin ()
# Syntax as a sentence: LogService_Begin $Message
{
	local Message="$1"
	local CallType=""
	
	if [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
		CallType=" ${ParO} Manual ${ParC}"
#	else
#		CallType=" ${ParO}INIT_SCRIPT_InitCall${ParC}"
	fi
	if [ -r /lib/lsb/init-functions ] ; then
		if [ "$(cat /lib/lsb/init-functions | grep -e '^log_daemon_msg ()')" != "" ] ; then
			log_daemon_msg "$Message" "$ServiceName"
		else
			LogService_Pending="${LogService_Pending}${Message}"
		fi
	else
		printf '%s' "$Message"
	fi
	mkdir -p "$(Dirname "$MainControllerLog")"
	printf '%s' "$(date '+%Y-%m-%dT%T%z')${CallType} $Message" >> "$MainControllerLog"
}

LogService_StatusEnd ()
# Syntax as a sentence: LogService_StatusEnd $StatusCode
{
	local StatusCode=$1
	
	if [ -r /lib/lsb/init-functions ] ; then
		if [ "$(cat /lib/lsb/init-functions | grep -e '^log_end_msg ()')" != "" ] ; then
			if [ "$LogService_Pending" != "" ] ; then printf '%s' "$LogService_Pending" ; fi
			log_end_msg $StatusCode
		else
			if [ $StatusCode -eq 0 ] ; then
				log_success_msg "$LogService_Pending [ OK ]"
			else
				log_failure_msg "$LogService_Pending [fail]"
			fi
		fi
	else
		if [ "$LogService_Pending" != "" ] ; then printf '%s' "$LogService_Pending" ; fi
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' ' [ OK ]'
		else
			printf '%s\n' ' [fail]'
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' ' [ OK ]' >> "$MainControllerLog"
	else
		printf '%s\n' " [fail] $StatusCode" >> "$MainControllerLog"
	fi
	LogService_Pending=""
}

ServiceVersion ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^# .*Service.* version:' | head -n 1 | cut -f 2- -d ':')"
	if [ "$Value" = "" ] && [ "$ServiceName" != "" ] ; then
		Value="$(cat "$MeExecutable" | grep -ie "^# .*${ServiceName}.* version:" | head -n 1 | cut -f 2- -d ':')"
	fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

ServiceCopyright ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^#Copyright' -ie '^# Copyright' | head -n 1 | cut -f 2- -d ' ')"
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

TemplateVersion ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^# .*template.* version: ' | head -n 1 | cut -f 2- -d ':')"
	if [ "$Value" = "" ] ; then
		Value="$(cat "$MeExecutable" | grep -ie "^# .*init.*script.* version:" | head -n 1 | cut -f 2- -d ':')"
	fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

TemplateCopyright ()
{
	local Value=""
	
	Value="$(cat "$MeExecutable" | grep -ie '^#Copyright' | grep -ie '^# Copyright' | head -n 2 | tail -n 1 | cut -f 2- -d ' ')"
	if [ "$Value" = "$(ServiceCopyright)" ] ; then Value="" ; fi
	if [ "$Value" != "" ] ; then
			echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
	fi
}

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

FoundServiceScript ()
{
	local Value=''
	if [ -x "/etc/init.d/${ServiceName}" ] ; then Value="/etc/init.d/${ServiceName}" ; fi
	if [ -x "/etc/rc.d/init.d/${ServiceName}" ] ; then Value="/etc/rc.d/init.d/${ServiceName}" ; fi
	if [ -x "/bin/${ServiceName}" ] ; then Value="/bin/${ServiceName}" ; fi
	if [ -x "/sbin/${ServiceName}" ] ; then Value="/sbin/${ServiceName}" ; fi
	if [ -x "/usr/bin/${ServiceName}" ] ; then Value="/usr/bin/${ServiceName}" ; fi
	if [ -x "/usr/sbin/${ServiceName}" ] ; then Value="/usr/sbin/${ServiceName}" ; fi
	if [ -x "/usr/local/bin/${ServiceName}" ] ; then Value="/usr/local/bin/${ServiceName}" ; fi
	if [ -x "/usr/local/sbin/${ServiceName}" ] ; then Value="/usr/local/sbin/${ServiceName}" ; fi
	if [ -x "/etc/init.d/${ServiceName}.sh" ] ; then Value="/etc/init.d/${ServiceName}.sh" ; fi
	if [ -x "/etc/rc.d/init.d/${ServiceName}.sh" ] ; then Value="/etc/rc.d/init.d/${ServiceName}.sh" ; fi
	if [ -x "/bin/${ServiceName}.sh" ] ; then Value="/bin/${ServiceName}.sh" ; fi
	if [ -x "/sbin/${ServiceName}.sh" ] ; then Value="/sbin/${ServiceName}.sh" ; fi
	if [ -x "/usr/bin/${ServiceName}.sh" ] ; then Value="/usr/bin/${ServiceName}.sh" ; fi
	if [ -x "/usr/sbin/${ServiceName}.sh" ] ; then Value="/usr/sbin/${ServiceName}.sh" ; fi
	if [ -x "/usr/local/bin/${ServiceName}.sh" ] ; then Value="/usr/local/bin/${ServiceName}.sh" ; fi
	if [ -x "/usr/local/sbin/${ServiceName}.sh" ] ; then Value="/usr/local/sbin/${ServiceName}.sh" ; fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

LsbHeaderValue ()
# See http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/initscrcomconv.html
{
	local HeaderKey="$1"
	local VariableKey=""
	local Value=""
	
	if [ "$(printf '%s\n' "$HeaderKey" | grep -e '-')" != "" ] ; then
		VariableKey="$(printf '%s\n' "$HeaderKey" | sed -e 's|-||g')"
	else
		VariableKey="${HeaderKey}-"
	fi
	Value="$(cat "$MeExecutable" | grep -ie "^# ${HeaderKey}:" | grep -ve "$VariableKey" | head -n 1 | cut -f 2- -d ':')"
	Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

BreakingControls ()
{
	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 ",${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
}

ListeningNetPortsByPids ()
{
	local ListeningData=""
	local CurrentPid=""
	local CurrentValues=""
	local LastStatus=$?
	local Value=''
	
	# Very old netstat versions don't have options -l -p and aren't useful
	ListeningData="$(env LANG=en netstat -tulnp 2>/dev/null | tr -s ' ' | grep -e ' LISTEN ' -e ' LISTEN$' -e ' ESTABLISHED ' -e ' ESTABLISHED$' | cut -f 1,4,7 -d ' ')"
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then
		for CurrentPid in $* ; do
			CurrentValues="$(printf '%s\n' "$ListeningData" | grep -e " ${CurrentPid}/" | cut -f 1,2 -d ' ' | sed -e 's| .*:|/|g')"
			if [ "$CurrentValues" != "" ] ; then Value="$Value $CurrentValues" ; fi
		done
		Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SystemdUnitsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvFacilities="$1"
	local CurrentUnit=""
	local Value=""
	
	SystemvFacilities="$(printf '%s\n' "$SystemvFacilities" | sed -e 's|\$||g')"
	for CurrentUnit in $SystemvFacilities ; do
		case "$CurrentUnit" in
			"local_fs" ) Value="$Value local-fs.target" ;;
			"network" )
				if Is_Executable systemctl ; then
					if [ "$(systemctl list-unit-files | grep -e '^networking.service ')" != "" ] ; then
						Value="$Value networking.service network-online.target"
					else
						Value="$Value network.target network-online.target"
					fi
				else
					Value="$Value networking.service network-online.target"
				fi
				;;
			"named" ) Value="$Value nss-lookup.target" ;;
			"portmap" ) Value="$Value rpcbind.target" ;;
			"remote_fs" ) Value="$Value remote-fs.target" ;;
			"syslog" ) Value="$Value syslog.service" ;;
			"time" ) Value="$Value time-sync.target" ;;
			"all" ) Value="$Value runlevel2.target networking.service" ;;
			"0" ) Value="$Value runlevel0.target" ;;
			"1" ) Value="$Value runlevel1.target" ;;
			"S" ) Value="$Value runlevel1.target" ;;
			"s" ) Value="$Value runlevel1.target" ;;
			"2" ) Value="$Value runlevel2.target" ;;
			"3" ) Value="$Value runlevel3.target" ;;
			"4" ) Value="$Value runlevel4.target" ;;
			"5" ) Value="$Value runlevel5.target" ;;
			"6" ) Value="$Value runlevel6.target" ;;
			* )	if [ -f "/lib/systemd/system/${CurrentUnit}.service" ] || [ -f /etc/systemd/system/${CurrentUnit}.service ] ; then
					CurrentUnit="${CurrentUnit}.service"
				fi
				Value="$Value $CurrentUnit"
				;;
		esac
	done
	Value="$(echo aaa$Value | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SystemdUnitsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(SystemdUnitsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

UpstartJobsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvFacilities="$1"
	local CurrentJob=""
	local Value=""
	
	SystemvFacilities="$(printf '%s\n' "$SystemvFacilities" | sed -e 's|\$||g')"
	for CurrentJob in $SystemvFacilities ; do
		case "$CurrentJob" in
			"local_fs" ) Value="$Value local-filesystems" ;;
			"network" ) Value="$Value net-device-up" ;;
			"named" ) Value="$Value net-device-up IFACE!=lo" ;;
			"portmap" ) Value="$Value portmap" ;;
			"remote_fs" ) Value="$Value remote-filesystems" ;;
			"syslog" ) Value="$Value rsyslog" ;;
			"time" ) Value="$Value runlevel__[12345]" ;;
			"all" ) Value="$Value runlevel__[2345] net-device-up__IFACE!=lo" ;;
			* )	if [ -f "/etc/init/${CurrentJob}.conf" ] ; then
					CurrentJob="${CurrentJob}"
				fi
				Value="$Value $CurrentJob"
				;;
		esac
	done
	if [ "$Value" != "" ] ; then
		echo $Value | sed -e 's|^ ||g'
	fi
}

UpstartJobsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(UpstartJobsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

UpstartRunlevelsFromSystemv ()
# See https://www.freedesktop.org/software/systemd/man/systemd.special.html
{
	local SystemvRunlevels="$1"
	local CurrentRunlevel=""
	local Value=""
	
	SystemvRunlevels="$(printf '%s\n' "$SystemvRunlevels" | sed -e 's|\$||g')"
	for CurrentRunlevel in $SystemvRunlevels ; do
		if [ "$(printf '%s\n' "$CurrentRunlevel" | grep -e '^.$')" != "" ] ; then
			if [ "$CurrentRunlevel" = "S" ] || [ "$CurrentRunlevel" = "s" ] ; then CurrentRunlevel="1" ; fi
			Value="${Value}${CurrentRunlevel}"
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '%s\n' "runlevel [${Value}]"
	fi
}

UpstartRunlevelsFromLsbHeader ()
{
	local HeaderKey="$1"
	local Value=""
	
	Value="$(LsbHeaderValue "$HeaderKey")"
	Value="$(UpstartRunlevelsFromSystemv "$Value")"
	if [ "$Value" != "" ] ; then printf '%s\n' $Value ; fi
}

InstallSystemService ()
# Description: Registers system init profile for this service
# Example: InstallSystemService /tmp/myservice.sh myservice
{
	local SourceScript="$1"
	local ServiceName="$2"
	local InitProfileFile=""
	local CurrentUnits=""
	local MoreUnits=""
	local PreviousUmask=""
	local TheTemplateVersion=""
	local ChildParameter=""
	local LastStatus=0
	local StatusCode=0

	if [ ! -f "$SourceScript" ] ; then
		printf '%s\n' "${sERROR}E: File not found: ${SourceScript}${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$InitSoftware" != "systemd" ] && [ "$InitSoftware" != "upstart" ] && [ "$InitSoftware" != "systemv" ] ; then
			printf '%s\n' "${sERROR}E: Unknown init software detected: ${InitSoftware}${fRESET}" 1>&2
			LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ServiceEnabler" = "" ] && [ "$InitProfilesDir" = "" ] ; then
			printf '%s\n' "${sERROR}E: $InitSoftware init enablement utility not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitExecutableParser" != "" ] && [ "$InitExecutableParser" != "$ProgramExecutablePath" ] ; then
		PreviousUmask="$(umask)"
		rm -f "$InitExecutableParser"	# Previous could be a link
		printf '%s\n' '#!/bin/sh' > "$InitExecutableParser"
		cat "$MeCallFile" | sed -ne '/BEGIN INIT INFO/,//p' | sed -e '/END INIT INFO/q' >> "$InitExecutableParser"
#		ln -s "$ProgramExecutablePath" "$InitExecutableParser"
		printf '%s\n' "\"${ProgramExecutablePath}\" \"\$@\"" >> "$InitExecutableParser"
		printf '%s\n' "exit \$?" >> "$InitExecutableParser"
		chmod u=rwx,go=rx "$InitExecutableParser"
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitProfilesDir" != "" ] ; then
		TheTemplateVersion="$(TemplateVersion)"
		case "$InitSoftware" in
			"systemd" )
				# Documentation at https://www.freedesktop.org/software/systemd/man/systemd.unit.html and https://www.freedesktop.org/software/systemd/man/systemd.service.html
				InitProfileFile="${InitProfilesDir}/${ServiceName}.service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					InitProfileFile_0="${InitProfilesDir_0}/${ServiceName}.service"
				fi
				cat /dev/null > "$InitProfileFile"
				if [ "$ChildSingularName" != "" ] ; then
					ChildParameter=" %i"
				fi
				printf '%s\n' "# $ServiceName service parameters for systemd" >> "$InitProfileFile"
				printf '%s\n' "# ${ParO}${LongDescription}${ParC}" >> "$InitProfileFile"
				printf '%s\n' "# Note: Many parameters have been parsed from LSB headers of SystemV init script. For example, runlevel*.target" >> "$InitProfileFile"
				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Unit]" >> "$InitProfileFile"
				printf '%s\n' "Description=$ShortDescription" >> "$InitProfileFile"
				if [ "$ServiceDocumentation" != "" ] ; then
					printf '%s\n' "Documentation=$ServiceDocumentation" >> "$InitProfileFile"
				fi
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Default-Start')"
				if [ "$CurrentUnits" != "" ] ; then
					CurrentUnits="$(printf '%s' "$CurrentUnits" | tr -s ' ' '\n' | awk '!seen[$0]++')" ; CurrentUnits="$(echo TrimAndSingle $CurrentUnits | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					printf '%s\n' "Wants=$CurrentUnits" >> "$InitProfileFile"
				fi
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Should-Start') $(SystemdUnitsFromLsbHeader 'Should-Stop')"
				if [ "$CurrentUnits" != " " ] ; then
					CurrentUnits="$(printf '%s' "$CurrentUnits" | tr -s ' ' '\n' | awk '!seen[$0]++')" ; CurrentUnits="$(echo TrimAndSingle $CurrentUnits | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					printf '%s\n' "After=$CurrentUnits" >> "$InitProfileFile"
				fi
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Required-Start') $(SystemdUnitsFromLsbHeader 'Required-Stop')"
				if [ "$CurrentUnits" != " " ] ; then
					CurrentUnits="$(printf '%s' "$CurrentUnits" | tr -s ' ' '\n' | awk '!seen[$0]++')" ; CurrentUnits="$(echo TrimAndSingle $CurrentUnits | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					printf '%s\n' "Requires=$CurrentUnits" >> "$InitProfileFile"
				fi
				CurrentUnits="$(echo aaa$(SystemdUnitsFromLsbHeader 'X-Start-Before') | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
				if [ "$CurrentUnits" != "" ] ; then
					printf '%s\n' "Before=$CurrentUnits" >> "$InitProfileFile"
				fi
#				CurrentUnits="$(echo $(SystemdUnitsFromLsbHeader 'Default-Stop') $(SystemdUnitsFromLsbHeader 'Should-Stop'))"
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Should-Stop')"
				if [ "$CurrentUnits" != "" ] ; then
					# Unlike documentation says, PartOf it's not only limited to stopping and restarting of units but starting too.
					printf '%s\n' "#PartOf=$CurrentUnits" >> "$InitProfileFile"
				fi
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Default-Stop')"
				if [ "$CurrentUnits" != "" ] ; then
					printf '%s\n' "Conflicts=$CurrentUnits" >> "$InitProfileFile"
				fi
				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Service]" >> "$InitProfileFile"
				if [ "$DaemonizeOnStart" = "0" ] ; then
					printf '%s\n' "Type=oneshot" >> "$InitProfileFile"
				else
					printf '%s\n' "Type=forking" >> "$InitProfileFile"
				fi
				printf '%s\n' "RemainAfterExit=yes" >> "$InitProfileFile" # This avoids that systemd triggers ExecStop just after ExecStart.
				printf '%s\n' "ExecStart=$ProgramExecutablePath start${ChildParameter}" >> "$InitProfileFile"
				printf '%s\n' "ExecReload=$ProgramExecutablePath reload${ChildParameter}" >> "$InitProfileFile"
				if [ "$MainControllerStays" != "0" ] ; then
					# Otherwise: Systemd logs a failure if pid file disappears (one-shot case)
					printf '%s\n' "PIDFile=$MainControllerPidFile" >> "$InitProfileFile"
				fi
					# Systemd doesn't run ExecStop if service didn't start and/or isn't loaded
#					printf '%s\n' "ExecStop=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
#				else
					# A case as eventoj shows that ExecStopPost is not called. Let's see if this ExecStop workaround implies a repeated call for other cases.
					printf '%s\n' "ExecStop=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
					printf '%s\n' "ExecStopPost=$ProgramExecutablePath stop${ChildParameter}" >> "$InitProfileFile"
#				fi
				if [ "$TimeoutStartSec" != "" ] ; then
					if [ $TimeoutStartSec -ge 0 ] ; then
						printf '%s\n' "TimeoutStartSec=$TimeoutStartSec" >> "$InitProfileFile"
					else
						printf '%s\n' "TimeoutStartSec=infinity" >> "$InitProfileFile"
					fi
				fi
				if [ "$TimeoutStopSec" != "" ] ; then
					if [ $TimeoutStopSec -ge 0 ] ; then
						printf '%s\n' "TimeoutStopSec=$(($TimeoutStopSec + 1))" >> "$InitProfileFile"
					else
						printf '%s\n' "TimeoutStopSec=infinity" >> "$InitProfileFile"
					fi
				fi
				printf '%s\n' "" >> "$InitProfileFile"
				printf '%s\n' "[Install]" >> "$InitProfileFile"
				printf '%s\n' "WantedBy=default.target" >> "$InitProfileFile"
				CurrentUnits="$(SystemdUnitsFromLsbHeader 'Provides')"
				for CurrentUnit in $CurrentUnits ; do
					CurrentUnit="$(printf '%s' "$CurrentUnit" | cut -f 1 -d '.')"
					if [ "$CurrentUnit" != "$ServiceName" ] ; then
						if [ "$(printf '%s\n' "$CurrentUnit" | grep -e '\.')" = "" ] ; then
							CurrentUnit="${CurrentUnit}.service"
						fi
						printf '%s\n' "Alias=$CurrentUnit" >> "$InitProfileFile"
					fi
				done
				chown root:root "$InitProfileFile"
				chmod u=rw,go=r "$InitProfileFile"
				rm -f "${InitProfilesDir}/${ServiceName}@"*".service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "$InitProfileFile_0"
					rm -f "${InitProfilesDir_0}/${ServiceName}@"*".service"
				fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ "$DaemonizeChilds" != "0" ] ; then
#						cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=forking|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" | perl -pe 's|^ExecStopPost=|ExecStop=|gi' > "${InitProfilesDir}/${ServiceName}@.service"
						cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=forking|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" > "${InitProfilesDir}/${ServiceName}@.service"
						# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992
						SetIniVarValue "${InitProfilesDir}/${ServiceName}@.service" DefaultInstance Install '@' = '#Instance(@) means: no instance'
					else
						if [ "$MainControllerStays" = "0" ] ; then
							# If childs act in foreground, they only need own unit template if it's not the main controller who calls them.
#							cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=oneshot|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" | perl -pe 's|^ExecStop=|ExecStopPost=|gi' > "${InitProfilesDir}/${ServiceName}@.service"
							cat "$InitProfileFile" | perl -pe 's|^Type=.*|Type=oneshot|gi' | perl -pe "s|^PIDFile=.*|PIDFile=${ChildsPidsDir}/%i.pid|gi" > "${InitProfilesDir}/${ServiceName}@.service"
							# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992
							SetIniVarValue "${InitProfilesDir}/${ServiceName}@.service" DefaultInstance Install '@' = '#Instance(@) means: no instance'
						fi
					fi
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						chown root:root "${InitProfilesDir}/${ServiceName}@.service"
						chmod u=rw,go=r "${InitProfilesDir}/${ServiceName}@.service"
					fi
					sed -ie 's| %i||g' "$InitProfileFile"
					sed -ie 's|%i||g' "$InitProfileFile"
				fi
				;;
			"upstart" )
				# Upstart considers service enabled when service profile file is present; better to only create with EnableSystemService()
				sleep 0
				;;
			"systemv" )
				printf '%s\n' "${sERROR}E: No procedure to write a init service profile at ${InitProfilesDir}${fRESET}" 1>&2
				LastStatus=59 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"" )
				# Nothing to do.
				sleep 0
				;;
			* )
				printf '%s\n' "${sERROR}E: Unknown init software: ${InitSoftware}${fRESET}" 1>&2
				LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	fi
	return $StatusCode
}

UnInstallSystemService ()
# Description: Disables and removes system init profile for this service
# Example: UnInstallSystemService "$0"
{
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$InitSoftware" != "systemd" ] && [ "$InitSoftware" != "upstart" ] && [ "$InitSoftware" != "systemv" ] ; then
			printf '%s\n' "${sERROR}E: Unknown init software detected: ${InitSoftware}${fRESET}" 1>&2
			LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ServiceEnabler" = "" ] && [ "$InitProfilesDir" = "" ] ; then
			printf '%s\n' "${sERROR}E: $InitSoftware utility for init disable not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		DisableSystemService 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitProfilesDir" != "" ] ; then
		case "$InitSoftware" in
			"systemd" )
				rm -f "${InitProfilesDir}/${ServiceName}.service"
				rm -f "${InitProfilesDir}/${ServiceName}@*.service"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "${InitProfilesDir_0}/${ServiceName}.service"
					rm -f "${InitProfilesDir_0}/${ServiceName}@*.service"
				fi
				;;
			"upstart" )
				rm -f "${InitProfilesDir}/${ServiceName}.conf"
				if [ "$InitProfilesDir_0" != "" ] ; then
					rm -f "${InitProfilesDir_0}/${ServiceName}.conf"
				fi
				;;
			"systemv" )
				sleep 0
				;;
			"" )
				sleep 0
				;;
			* )
				printf '%s\n' "${sERROR}E: Unknown init software: ${InitSoftware}${fRESET}" 1>&2
				LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	fi
	if [ $StatusCode -eq 0 ] && [ "$InitExecutableParser" != "" ] && [ "$InitExecutableParser" != "$ProgramExecutablePath" ] ; then
		rm -f "$InitExecutableParser"
	fi
	return $StatusCode
}

SystemProgramIsEnabled ()
# 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
							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
							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
							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 ()
{
	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
}

WaitForProcesses ()
{
	local ProcessesToWaitFor="$1"
	local StopFile="$2"
	local CurrentProcess=""
	
	IFS=',' ; for CurrentProcess in $ProcessesToWaitFor ; do unset IFS
		CurrentProcess=$(echo aaa$CurrentProcess | sed -e 's|^aaa||g' | sed -e 's|^ ||g')
		LogProgram 3 "Waiting for process: $CurrentProcess"
		while [ "$(grep -sce "/${CurrentProcess}$" -sce "^${CurrentProcess}$" /proc/*/cmdline | grep -ve ':0$')" = "" ] && [ ! -f "$StopFile" ] ; do
			sleep 1
		done
	done
}

WaitForCleanExecution ()
{
	local ExecutionsIntervalS="$1"
	local CommandLine="$2"
	local StopFile="$3"
	local LastStatus=0
	
	LogProgram 3 "Waiting for command succeeds: $CommandLine"
	eval $CommandLine
	LastStatus=$?
	while [ $LastStatus -ne 0 ] && [ ! -f "$StopFile" ] ; do
		WaitStoppable $ExecutionsIntervalS "$StopFile" 1
		if [ ! -f "$StopFile" ] ; then
			eval $CommandLine
			LastStatus=$?
		fi
	done
}

Start1Process ()
# To launch the main controller or any child.
{
	local ChildId="$1" # Only if it's for a child process
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local PidFile=''
	local StopFile=''
	local ProcessName=''
#	local StartStatusNum=''
	local StartLastLog=''
	local StartText=''
	local ThePercentLoad1=''
	local ExecutionsIntervalS=''
	local CommandLine=''
	local RunningPid=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" = "" ] ; then
		PidFile="$MainControllerPidFile"
		StopFile="$MainControllerStopFile"
		ProcessName="$ShortDescription"
		if [ ! -d "$(Dirname "$MainControllerPidFile")" ] ; then
			mkdir -p "$(Dirname "$MainControllerPidFile")"
			chmod u=rwX,g=rX,o= "$(Dirname "$MainControllerPidFile")"
		fi
	else
		PidFile="${ChildsPidsDir}/${ChildId}.pid"
		StopFile="${ChildsPidsDir}/${ChildId}.stop"
		if [ "$ChildId" != "@" ] ; then
			ProcessName="$ChildSingularName $ChildId"
		else
			# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
			ProcessName="@@ workaround for Systemd bug #4992"
		fi
		if [ ! -d "$ChildsPidsDir" ] ; then
			mkdir -p "$ChildsPidsDir"
			chmod u=rwX,g=rX,o= "$ChildsPidsDir"
		fi
	fi
	rm -f "$StopFile"
	if [ $INIT_SCRIPT_InitCall -eq 1 ] && [ "$ChildId" = "" ] ; then
		# Main controller probably called by init boot
		WaitForProcesses "$FirstProcessesToWaitFor" "$StopFile"
		if [ "$CleanExecutionToWaitFor" != "" ] ; then
			ExecutionsIntervalS="$(printf '%s\n' "$CleanExecutionToWaitFor" | cut -sf 1 -d '|')"
			if ! Is_IntegerNr "$ExecutionsIntervalS" ; then ExecutionsIntervalS=15 ; fi
			CommandLine="$(printf '%s\n' "$CleanExecutionToWaitFor" | cut -f 2- -d '|')"
			WaitForCleanExecution "$ExecutionsIntervalS" "$CommandLine" "$StopFile"
		fi
	fi
#	StartStatusNum="${PidFile}.start.status"
	StartLastLog="${PidFile}.start.text"
	PidfileStatus "$PidFile" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then
		RunningPid=$(cat "$PidFile" 2>/dev/null)
		if [ "$RunningPid" == "$$" ] ; then LastStatus=3 ; fi  # It's me
	fi
	if [ $LastStatus -eq 3 ] ; then LastStatus=1 ; fi  # Result Conversion; Clearly not running.
	case $LastStatus in
		0 )	LogProgram 1 "E: $ProcessName is already running with PID ${RunningPid}. Cannot start a parallel process."
			LastStatus=107 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
		1 ) # Includes 3
			rm -f "$PidFile"
			if [ "$ChildId" = "" ] ; then
				# Main controller
				if [ "$DaemonizeOnStart" = "1" ] ; then
					# Background
#					if [ "$DaemonizeOnStart" = "1" ] && [ "${InitSoftware}-${INIT_SCRIPT_InitCall}" != "upstart-0" ] ; then
#					# Background - Upstart already daemonizes and controls PID for stops
					LogProgram 4 '$$' DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr"
					LogService_Begin "Starting $ProcessName"
#					DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					StartText="$(DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr" 2>&1 ; printf '%s' $? > "$StartStatusNum")"	$(This) never ends when fork method is ampersand.
					DaemonizeCommand "$PidFile" "$MeExecutable" main "$MaxChildLoopsNr" > "$StartLastLog" 2>&1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					StartText="$(cat "$StartLastLog" 2>/dev/null)"
					rm -f "$StartLastLog"
					PidfileStatus "$PidFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					LogService_StatusEnd $StatusCode
					LogProgram 3 "$StartText" > /dev/null
				else
					# Foreground
					printf '%s\n' "$$" > "$PidFile"
					LogProgram 3 "${ServiceName}: $ProcessName"
#					"$MeExecutable" main "$MaxChildLoopsNr"
					LogProgram 4 '$$' MainController "$MaxChildLoopsNr"
					MainController "$MaxChildLoopsNr"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					rm -f "$PidFile"
				fi
			else
				# Child
				if Is_IntegerNr $Child_LoadWithCalm ; then
					if [ $Child_LoadWithCalm -gt 0 ] ; then
						ThePercentLoad1=$(PercentLoad 1)
						if [ $ThePercentLoad1 -ge $Child_LoadWithCalm  ] ; then
							LogProgram 3 "Waiting for system load ${ParO}${ThePercentLoad1}%${ParC} to be descending and under ${Child_LoadWithCalm}% before load $ProcessName"
							WaitForCalm $Child_LoadWithCalm "$StopFile" 0 4
						else
							LogProgram 3 "System load ${ParO}${ThePercentLoad1}%${ParC} is already under calm level ${Child_LoadWithCalm}% to load $ProcessName"
						fi
					fi
				fi
				if [ "$DaemonizeChilds" = "1" ] ; then
					# Background
					LogProgram 4 '$$' DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
					LogService_Begin "Loading $ProcessName"
#					DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					StartText="$(DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr" 2>&1 ; printf '%s' $? > "$StartStatusNum")"	$(This) never ends when fork method is ampersand.
					DaemonizeCommand "$PidFile" "$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr" > "$StartLastLog" 2>&1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					StartText="$(cat "$StartLastLog" 2>/dev/null)"
					rm -f "$StartLastLog"
					PidfileStatus "$PidFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					LogService_StatusEnd $StatusCode
					LogProgram 3 "$StartText" > /dev/null
				else
					# Foreground
					printf '%s\n' "$$" > "$PidFile"
					LogProgram 3 "$ProcessName"
#					"$MeExecutable" "${LowerChildSingularName}-foreground" "$ChildId" "$MaxChildLoopsNr"
					LogProgram 4 '$$' ChildController "$ChildId" "$MaxChildLoopsNr"
					ChildController "$ChildId" "$MaxChildLoopsNr"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					rm -f "$PidFile"
				fi
			fi
			;;
		* )	printf '%s\n' "Unable to determine $ProcessName status${ParO}${LastStatus}${ParC}. Sure to stop it before starting." 1>&2
			LastStatus=106 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
	return $StatusCode
}

Start ()
{
#[durruter]
	DevicesUpDown up "$@"
	return $?
#[/durruter]
	local MainControllerPid=""
	local ChildsToStop=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildsPluralName" != "" ] && [ $# -gt 0 ] ; then
		# Only start specified childs
		for CurrentChild in "$@" ; do
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			if [ $Child_LoadWithCalm -ge 0 ] ; then sleep 1 ; fi # Sure slower sequence to evaluate new system load.
			CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
			Start1Process "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		# Main controller. It will re/load childs as necessary
#		if [ "$RestartCall" != "1" ] && [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && SystemProgramIsEnabled ; then
#			printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:${fRESET}" 1>&2
#			printf '%s\n' "         $(RecommendedInvocation start)" 1>&2
#		fi
		if [ $(id -u) -ne 0 ] || SystemProgramIsEnabled ; then # Only for main controller
			# Main controller will load childs as necessary. Both if it stays or not, it can be foreground or background.
			Start1Process "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
				printf '%s\n' "Service $ServiceName not enabled for system start." 1>&2
				printf '%s\n' "It can be enabled with: $(RecommendedInvocation enable)" 1>&2
				LastStatus=58 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

WaitStoppable ()
# Syntax as a sentence: WaitStoppable $IntegerMaxSeconds "$StopFile" $SecondsResolution
# Expected parameters:
#	$1	Seconds to wait while no StopFile (no fractional)
#	$2	(optional) File path which existence makes stop waiting.
#	$3	(optional) Number of seconds to wait between each StopFile checking. It can be decimal expressed (eg 0.5). Default: 1
# Depends on functions: Is_IntegerNr
# Depends on other software: grep
{
	local IntegerMaxSeconds=$1
	local StopFile="$2"
	local SleepResolution="$3"
	local InitialSecond=''
	local FinalSecond=''
	
	if Is_IntegerNr "$IntegerMaxSeconds" && [ $IntegerMaxSeconds -gt 0 ] ; then
		InitialSecond=$(date +%s)
		FinalSecond=$(($InitialSecond + $IntegerMaxSeconds))
		if [ "$SleepResolution" = "" ] ; then SleepResolution=1 ; fi
		# Sleep upto v2 (and maybe -v4) only support integers
		OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -E ' (1|2|3|4)\.')"
		if [ "$OldSleep" != "" ] ; then
			SleepResolution=1
		fi
		while [ ! -f "$StopFile" ] && [ $(date +%s) -lt $FinalSecond ] ; do
			sleep $SleepResolution
		done
	fi
}

RunningChilds_IDs ()
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentName=""
	local CurrentStatus=0
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 0 ] ; then
#			CurrentName="$(basename "$CurrentPidFile" | sed -e 's|\.pid$||g')"	Problems with a path begun with "-" in old basename versions
			CurrentName="$(printf '%s\n' "$CurrentPidFile" | tr -s '/' '\n' | tail -n 1)"
			CurrentName="$(printf '%s\n' "$CurrentName" | sed -e 's|\.pid$||g')"
			if [ "$Value" = "" ] ; then
				Value="$CurrentName"
			else
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentName")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

RunningChilds_Pids ()
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentPid=""
	local CurrentStatus=0
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 0 ] ; then
			CurrentPid="$(cat "${ChildsPidsDir}/${CurrentPidFile}" | sed -e 's| ||g')"
			if [ "$Value" = "" ] ; then
				Value="$CurrentPid"
			else
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentPid")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

EnabledChilds_IDs ()
{
	local ProfilesPath="$1"
	local FoundItems=""
	local CurrentItem=""
	local Operator=""
	local Value=""
	
	if [ "$ProfilesPath" = "" ] ; then ProfilesPath="$Childs_path_enabled" ; fi
	if [ "$ChildProfileExtension" != "" ] ; then
		FoundItems="$(ls -1A "$ProfilesPath" 2>/dev/null | grep -e "\.${ChildProfileExtension}$")"
		Operator="-f"
	else
		FoundItems="$(ls -1A "$ProfilesPath" 2>/dev/null)"
		Operator="-d"
	fi
	IFS="$(printf '\n\b')" ; for CurrentItem in $FoundItems ; do unset IFS
		if [ $Operator "${ProfilesPath}/${CurrentItem}" ] ; then
			if [ "$ChildProfileExtension" != "" ] ; then
				CurrentItem="$(printf '%s\n' "$CurrentItem" | sed -e "s|\.${ChildProfileExtension}$||g")"
			fi
			Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentItem")"
		fi
	done
	Value="$(printf '%s\n' "$Value" | grep -ve '^$')"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value" | sort
	fi
}

AvailableChilds_IDs ()
{
	local Value=""
	
	Value="$(EnabledChilds_IDs "$Childs_path_available")"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ThreadsAccumulationSummary ()
{
	local Separation=""
	local Headers=""
	local Values=""
	local CurrentHeader=""
	local CurrentValue=""
	local AskedPidStatus=""
	
	if [ "$ChildSingularName" != "" ] ; then
		CurrentHeader=" $ChildsPluralName "
		CurrentValue=" $Accumulated_Childs "
		while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
			CurrentValue=" $CurrentValue"
		done
		while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
			CurrentHeader=" $CurrentHeader"
		done
		Headers="${Headers}${CurrentHeader}"
		Values="${Values}${CurrentValue}"
	fi
	if [ "$ChildSingularName" != "" ] ; then
		CurrentHeader=" $(printf '%s' "$ChildsPluralName" | cut -c 1).running "
		CurrentValue=" $Accumulated_ChildsRunning "
		while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
			CurrentValue=" $CurrentValue"
		done
		while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
			CurrentHeader=" $CurrentHeader"
		done
		Headers="${Headers}${CurrentHeader}"
		Values="${Values}${CurrentValue}"
	fi
	CurrentHeader=" T.PIDs "
	CurrentValue=" $Accumulated_pid "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" First.start "
	if [ "$Accumulated_start" = "now" ] && [ "$MainControllerPidFile" != "" ] ; then
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ $AskedPidStatus -eq 0 ] ; then
			CurrentValue="$(env LANG=en stat "$MainControllerPidFile" 2>/dev/null | grep -e 'Modify:..*' | sed -e 's|.*Modify:||g')"
			CurrentValue="$(echo aaa$CurrentValue | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			if [ ${#CurrentValue} -ge 5 ] ; then
				if [ "$(date -d "$CurrentValue" '+%F')" = "$(date '+%F')" ] ; then
					CurrentValue=" $(LANG=en date -d "$CurrentValue" '+%T') "
				else
					CurrentValue=" $(LANG=en date -d "$CurrentValue" '+%b %d') "
				fi
				CurrentHeader=" Main.start "
			else
				CurrentValue=" - "
			fi
		else
			CurrentValue=" - "
		fi
	else
		CurrentValue=" $Accumulated_start "
	fi
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue="$CurrentValue "
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader="$CurrentHeader "
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" %CPU "
	CurrentValue=" $Accumulated_pcpu10 "
	if [ "$Accumulated_pcpu10" != "?" ] ; then
		CurrentValue=" $(printf '%s\n' "$CurrentValue" | sed -e 's|\(.\) $|.\1 |g' | sed -e 's| \.| 0.|g') "
	fi
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" Memory "
	CurrentValue=$(($Accumulated_rss * 1024))
	CurrentValue=" $(NumeroResumit "$CurrentValue" '2L.') "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	CurrentHeader=" T.threads "
	if [ "$Accumulated_Threads" = "0" ] && [ "$Accumulated_pid" != "0" ] ; then Accumulated_Threads='?' ; fi
	CurrentValue=" $Accumulated_Threads "
	while [ ${#CurrentHeader} -gt ${#CurrentValue} ] ; do
		CurrentValue=" $CurrentValue"
	done
	while [ ${#CurrentValue} -gt ${#CurrentHeader} ] ; do
		CurrentHeader=" $CurrentHeader"
	done
	Headers="${Headers}${CurrentHeader}"
	Values="${Values}${CurrentValue}"
	if [ "$MainControllerPidFile" != "" ] ; then
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ $AskedPidStatus -eq 3 ] && [ "$MainControllerStays" = "0" ] && [ $Accumulated_pid -eq 0 ] ; then
			# No childs, no daemons, not running.
			AskedPidStatus=$AskedPidStatus
		else
			printf '%s\n' "${Headers}_" | sed -e 's|.|_|g'
			printf '%s\n' "All processes summary:"
			printf '%s\n' "$Headers"
			printf '%s\n' "$Values"
		fi
	else
		printf '%s\n' "${Headers}_" | sed -e 's|.|_|g'
		printf '%s\n' "All processes summary:"
		printf '%s\n' "$Headers"
		printf '%s\n' "$Values"
	fi
}

PsValueOrDefault ()
# Returns the ps key value, or $DefaultValue if pid has disappeared.
{
	local Pid=$1
	local Key="$2"
	local DefaultValue="$3"
	local Value=""
	
	Value=$(PsOutputValues "$Key" $Pid)
	if [ "$Value" != "" ] && [ "$Value" != "?" ] ; then
		printf '%s\n' "$Value"
	else
		if [ "$DefaultValue" != "" ] ; then printf '%s\n' "$DefaultValue" ; fi
	fi
}

AccumulateThreadsInfo ()
{
	local ThreadsInfo="$1"
	local CurrentPid=""
	local CurrentValue=""
	local CurrentLine=""
	
	IFS="$(printf '\n\b')" ; for CurrentLine in $ThreadsInfo ; do unset IFS
		CurrentPid=$(OneWord () { printf '%s' $1; }; OneWord $CurrentLine)
		if Is_IntegerNr "$CurrentPid" ; then
			Accumulated_pid=$(($Accumulated_pid + 1))
			CurrentValue=$(PsValueOrDefault $CurrentPid start now)
			if [ $(date -d "$CurrentValue" '+%s') -lt $(date -d "$Accumulated_start" '+%s') ] ; then
				Accumulated_start="$CurrentValue"
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid pcpu '?' | sed -e 's|\.||g' | sed -e 's| ||g' | sed -e 's|^0\(..*\)|\1|g')
			if Is_IntegerNr "$CurrentValue" ; then
				if [ "$Accumulated_pcpu10" = "?" ] ; then Accumulated_pcpu10=0 ; fi
				Accumulated_pcpu10=$(($Accumulated_pcpu10 + $CurrentValue))
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid rss '?')
			if Is_IntegerNr "$CurrentValue" ; then
				Accumulated_rss=$(($Accumulated_rss + $CurrentValue))
			fi
			CurrentValue=$(PsValueOrDefault $CurrentPid $PsThreadsKey 1)
			if Is_IntegerNr "$CurrentValue" ; then
				Accumulated_Threads=$(($Accumulated_Threads + $CurrentValue))
			fi
		fi
	done
}

ProcessStatus ()
{
	local ChildId="$1" # Only if it's for a child process
	local OnlyOneLine"$2" # Specify "1" to avoid PIDs analysis
	local AskedPidStatus=""
	local ProcessPid=""
	local Tail=""
	local Description=""
	local FullRunning=0
	local AnalyzePids=""
	local ListeningPorts=""
	local CurrentPid=""
	local TempValue=""
	local ThreadsInfo=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$PsThreadsKey" = "" ] ; then
		# Global variable
		ps -p $$ -o thcount > /dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			PsThreadsKey=thcount
		else
			PsThreadsKey=nlwp
		fi
	fi
	if [ "$ChildId" != "" ] ; then
		if [ $DaemonizeChilds != "0" ] ; then
			PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
			AskedPidStatus=$?
			case $AskedPidStatus in
				0 )	ProcessPid=$(cat "${ChildsPidsDir}/${ChildId}.pid" | sed -e 's| ||g')
					if [ "$(pstree $ProcessPid)" != "" ] ; then
						Tail="concerned running pid $ProcessPid"
						FullRunning=1
					else
						Tail="running, but concerned PID $ProcessPid is not found"
					fi
					;;
				1 )	Tail="not running but the pid file exists" ;;
				3 )	Tail="not running" ;;
				4 )	Tail="unable to determine status." ;;
				* )
					Tail="unknown code${ParO}${AskedPidStatus}${ParC} from status query"
					LastStatus=$AskedPidStatus ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
			Description="$ChildSingularName ${ChildId}: $Tail"
		fi
	else
		PidfileStatus "$MainControllerPidFile"
		AskedPidStatus=$?
		if [ "$MainControllerStays" != "0" ] ; then
			if [ "$ChildSingularName" = "" ] ; then
				MainName="Service"
				RunningWord="running"
			else
				MainName="Service controller"
				RunningWord="running"
			fi
		else
			if [ "$ChildSingularName" = "" ] ; then
				MainName="Program"
				RunningWord="launching at this moment"
			else
				MainName="$ChildsPluralName trigger"
				RunningWord="launching at this moment"
			fi
		fi
		case $AskedPidStatus in
			0 )	ProcessPid=$(cat "$MainControllerPidFile")
				if [ "$(pstree $ProcessPid)" != "" ] ; then
					Tail="$MainName is $RunningWord with main PID $ProcessPid"
					FullRunning=1
				else
					Tail="$MainName is ${RunningWord}, but main PID $ProcessPid is not found."
				fi
				;;
			1 )	Tail="$MainName is not $RunningWord and the pid file exists." ;;
			3 )	Tail="$MainName is not ${RunningWord}." ;;
			4 )	Tail="Unable to determine $MainName status." ;;
			* )
				Tail="Unknown code${ParO}${AskedPidStatus}${ParC} from $MainName status query"
				LastStatus=$AskedPidStatus ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		Description="$(Uppercase "$ServiceName"): $Tail"
	fi
	Description="${Description}$(ProcessStatus_More $AskedPidStatus "$@")"
	if [ "$Description" != "" ] ; then
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' "$Description"
		else
			printf '%s\n' "$Description" 1>&2
		fi
	fi
	if [ $FullRunning -eq 1 ] && [ "$OnlyOneLine" != "1" ] ; then
		# Sed upto v3 doesn't support newlines as replacement
		#AnalyzePids="$(echo $(pstree -p $ProcessPid | sed -e 's|(|@\n|g' | sed -e 's|).*|\n@|g' | grep -ve @ | grep -ve ' ') | tr -s ' ' ',')"
		TempValue="$(env COLUMNS=999 pstree -p $ProcessPid | tr -s '()' ' ')"
		AnalyzePids=""
		for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then AnalyzePids="$AnalyzePids $CurrentPid" ; fi
		done
		ListeningPorts="$(ListeningNetPortsByPids $AnalyzePids)"
		if [ "$ListeningPorts" != "" ] ; then
			printf '%s\n' "Listening net ports: $(echo aaa$ListeningPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
		fi
		AnalyzePids=$(echo aaa$AnalyzePids | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' ',')
		# Prefer modern ps columns alignment
		ThreadsInfo="$(ps p $AnalyzePids -o pid,comm,start,pcpu,pmem,rss=Mem.KiB,${PsThreadsKey}=Threads 2>/dev/null)"
		if [ "$ThreadsInfo" = "" ] ; then
			# Fallback to compatible call: slow and unaligned columns
			ThreadsInfo="PID COMMAND          STARTED %CPU %MEM Mem.KiB Threads"
			AnalyzePids=$(printf '%s\n' "$AnalyzePids" | tr -s ',' ' ')
			ThreadsInfo="$(printf '%s\n' "$ThreadsInfo" ; PsOutputValues pid,comm,start,pcpu,pmem,rss,${PsThreadsKey} $AnalyzePids)"
		fi
		printf '%s\n' "$ThreadsInfo"
		AccumulateThreadsInfo "$ThreadsInfo"
		if [ "$ChildId" != "" ] ; then
			Accumulated_ChildsRunning=$(($Accumulated_ChildsRunning + 1))
		fi
	fi
	if [ "$ChildId" != "" ] ; then
		Accumulated_Childs=$(($Accumulated_Childs + 1))
	fi
	return $StatusCode
}

Status ()
{
	local ChildId="$1"
	local AskedPidStatus=0
	local CurrentChild=""
	local TheChilds=""
	local LastStatus=0
	local StatusCode=0
	local InfoTxt=""
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Child status
		for CurrentChild in "$@" ; do
			if [ "$DaemonizeChilds" = "1" ] ; then
				if [ "$StatusProcecessShow" = "1" ] ; then
					ProcessStatus "$CurrentChild"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
					AskedPidStatus=$?
					ProcessStatus_More $AskedPidStatus "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				AskedPidStatus=4
			fi
			Status_More $AskedPidStatus "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		# Whole service status
		if [ "$StatusProcecessShow" = "1" ] ; then
			ProcessStatus
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			PidfileStatus "$MainControllerPidFile"
			AskedPidStatus=$?
			ProcessStatus_More $AskedPidStatus "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		TheChilds="$(AvailableChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
		TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
		IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
			if [ "$StatusProcecessShow" = "1" ] ; then
				#InfoTxt="$(ProcessStatus "$CurrentChild" 1)"	This (subshell) losses global variables modifications
				ProcessStatus "$CurrentChild" 1 > "${DirTemp}/Status.$$"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				InfoTxt="$(cat "${DirTemp}/Status.$$")"
				rm -f "${DirTemp}/Status.$$"
				if [ "$InfoTxt" != "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "$InfoTxt"
				fi
			else
				PidfileStatus "${ChildsPidsDir}/${ChildId}.pid"
				AskedPidStatus=$?
				ProcessStatus_More $AskedPidStatus "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	fi
	if [ "$StatusProcecessShow" = "1" ] ; then
		if [ "$ChildId" = "" ] || [ "$DaemonizeChilds" = "1" ] ; then
			TheThreadsAccumulationSummary="$(ThreadsAccumulationSummary)"
			if [ "$TheThreadsAccumulationSummary" != "" ] ; then printf '%s\n' "$TheThreadsAccumulationSummary" ; fi
		fi
	fi
	if [ "$ChildId" = "" ] || [ "$ChildSingularName" = "" ] ; then
		if SystemProgramIsEnabled ; then
			if [ "$TheThreadsAccumulationSummary" != "" ] ; then
				printf '%s\n' "$ServiceName is enabled to start as system service. Service will start on boot."
			else
				printf '%s\n' "$ServiceName is enabled to start as system service." 1>&2
				printf '%s\n' "Service will start on boot, and manually with command: $(RecommendedInvocation start)"
			fi
		else
			printf '%s\n' "$ServiceName is disabled, to not start as system service. Service will not start on boot."
		fi
		Status_More $AskedPidStatus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CleanChildsPids ()
{
	local FoundPidfiles=""
	local CurrentStatus=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		PidfileStatus "${ChildsPidsDir}/${CurrentPidFile}" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
		CurrentStatus=$?
		if [ $CurrentStatus -eq 1 ] || [ $CurrentStatus -eq 3 ] ; then
			rm "${ChildsPidsDir}/${CurrentPidFile}"
		fi
	done
}

Stop1Daemon ()
{
	local PidFile="$1"
	local StopFile="$2"
	local DaemonName="$3"
	local ExpectedToStay="$4" # If it's supposed to be running (1) or not (0); this only affects messages.
	local LeaveStopFile="$5" # If to not remove $StopFile (1) or remove $StopFile when success (0)
	# Rest of parameters will be written as text to StopFile
	local Pid=""
	local TimeoutCount=0
	local StopStatus=""
	local StopLastLog=""
	local MultipleCount=2
	local FractionSecond=0.5
	local LastStatus=0
	local StatusCode=0

	if [ "$DaemonName" = "" ] ; then DaemonName="program" ; fi
	if [ ! -d "$(Dirname "$PidFile")" ] ; then
		mkdir -p "$(Dirname "$PidFile")"
		chmod u=rwX,g=rX,o= "$(Dirname "$PidFile")"
	fi
	if [ "$StopFile" != "" ] ; then
		if [ ! -d "$(Dirname "$StopFile")" ] ; then
			mkdir -p "$(Dirname "$StopFile")"
			chmod u=rwX,g=rX,o= "$(Dirname "$StopFile")"
		fi
		printf '%s\n' "$(date '+%Y-%m-%dT%T%z') [$$] Stop1Daemon $*" > "$StopFile"
		StopStatus="${StopFile}.status"
	else
		printf '%s\n' "${sWARN}W: No StopFile specified to Stop1Daemon ${PidFile}${fRESET}" 1>&2
		StopStatus="${PidFile}.stop.status"
	fi
	if [ -f "$PidFile" ] ; then
		Pid="$(cat "$PidFile")"
		if [ "$(pstree $Pid)" = "" ] ; then
			if [ "$ExpectedToStay" = "1" ] ; then
				printf '%s\n' "${sWARN}W: $DaemonName was not running.${fRESET}" 1>&2
			fi
			rm "$PidFile"
		else
			LogService_Begin "Stopping $DaemonName"
			# Sleep upto v2 (and maybe -v4) only support integers
			OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -E ' (1|2|3|4)\.')"
			if [ "$OldSleep" != "" ] ; then
				MultipleCount=1
				FractionSecond=1
			fi
			TimeoutCount=$(($GentleStopTimeoutS * $MultipleCount))
			while [ "$(pstree $Pid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep $FractionSecond
				TimeoutCount=$(($TimeoutCount - 1))
			done
#			if [ "$(pstree $Pid)" != "" ] || [ "$ExpectedToStay" = "1" ] ; then
			if [ "$(pstree $Pid)" != "" ] ; then
				TimeoutCount=$(($GentleStopTimeoutS / 2)) ; if [ $TimeoutCount -lt 1 ] ; then TimeoutCount=1 ; fi
				StopLastLog="$(StopPidFile "$PidFile" $TimeoutCount 2>&1 ; printf '%s' $? > "$StopStatus")"
				LastStatus=$(cat "$StopStatus") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				rm -f "$StopStatus"
			fi
			if [ "$(pstree $Pid)" = "" ] ; then
				rm -f "$PidFile"
			fi
			LogService_StatusEnd $StatusCode
			LogProgram 3 "$StopLastLog" > /dev/null
		fi
	else
		if [ "$ExpectedToStay" = "1" ] && [ $INIT_SCRIPT_InitCall -eq 0 ] ; then
			printf '%s\n' "$DaemonName was not running."
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$LeaveStopFile" != "1" ] ; then rm -f "$StopFile" ; fi
	return $StatusCode
}

Stop ()
{
#[durruter]
	DevicesUpDown down "$@"
	return $?
#[/durruter]
	local MainControllerPid=""
	local ChildsToStop=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0

	if [ "$ChildsPluralName" != "" ] && [ $# -gt 0 ] ; then
		# Only stop specified childs - No matter if they are daemons or foregrounds
		for CurrentChild in "$@" ; do
			# Previous .stop signals blast to accelerate task
			Stop_Pre "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
			touch "$CurrentStopFile" 2>/dev/null
		done
		for CurrentChild in "$@" ; do
			if [ "$InitSoftware" = "systemd" ] ; then CurrentChild="$(printf '%s\n' "$CurrentChild" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
			CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
			CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
#			Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called for specified ${ChildsPluralName}${ParC}"
			Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called for specified ${ChildsPluralName}${ParC}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			Stop_Post "$CurrentChild"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		Stop_Pre "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$RestartCall" != "1" ] && [ $(id -u) -eq 0 ] && [ "$INIT_SCRIPT_InitCall" = "0" ] ; then
			# To avoid warning when installer forces stop
			PidfileStatus "$MainControllerPidFile"
			LastStatus=$?
#			if [ $LastStatus -eq 0 ] && SystemProgramIsEnabled ; then
#				printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:" 1>&2
#				printf '%s\n' "   $(RecommendedInvocation stop)${fRESET}" 1>&2
#			fi
		fi
		if [ $# -eq 0 ] ; then
			# Main controller before all childs, to avoid it reloads dead childs. - No matter if it's a daemon or foreground
#			Stop1Daemon "$MainControllerPidFile" "$MainControllerStopFile" "$ServiceName controller" $MainControllerStays 1 "${ParO}Stop called for whole service${ParC}"
			Stop1Daemon "$MainControllerPidFile" "$MainControllerStopFile" "$ServiceName controller" $MainControllerStays 0 "${ParO}Stop called for whole service${ParC}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$ChildsPluralName" != "" ] ; then
			# Stop all running childs - No matter if they are daemons or foregrounds
			ChildsToStop="$(RunningChilds_IDs)"
			IFS="$(printf '\n\b')" ; for CurrentChild in $ChildsToStop ; do unset IFS
				# Previous .stop signals blast to accelerate task
				Stop_Pre "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				touch "$CurrentStopFile" 2>/dev/null
			done
			IFS="$(printf '\n\b')" ; for CurrentChild in $ChildsToStop ; do unset IFS
				if [ "$InitSoftware" = "systemd" ] ; then
					# systemctl use only when concrete childs aren't specified.
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
							systemctl stop "${ServiceName}@${CurrentChild}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else	
							CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
							CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
#							Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called${ParC}"
							Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called${ParC}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				else
					CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
					CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
#					Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}Stop called${ParC}"
					Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}Stop called${ParC}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Stop_Post "$CurrentChild"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
			CleanChildsPids
			rmdir "$ChildsPidsDir" >/dev/null 2>&1
		fi
		Stop_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

MyIdChild ()
# Returns the child ID if own process is a child one.
{
	local FoundPidfiles=""
	local CurrentPidFile=""
	local CurrentName=""
	local CurrentPid=""
	local Value=""
	
	FoundPidfiles="$(ls -1A "$ChildsPidsDir" 2>/dev/null | grep -e '\.pid$')"
	IFS="$(printf '\n\b')" ; for CurrentPidFile in $FoundPidfiles ; do unset IFS
		CurrentPid="$(cat "${ChildsPidsDir}/${CurrentPidFile}" | sed -e 's| ||g')"
		if [ "$CurrentPid" = "$$" ] ; then
#			CurrentName="$(basename "$CurrentPidFile" | sed -e 's|\.pid$||g')"	Problems with a path begun with "-" in old basename versions
			CurrentName="$(printf '%s\n' "$CurrentPidFile" | tr -s '/' '\n' | tail -n 1)"
			CurrentName="$(printf '%s\n' "$CurrentName" | sed -e 's|\.pid$||g')"
			printf '%s\n' "$CurrentName"
		fi
	done
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

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

CleanTempFiles ()
# Deletes only own (child or main controller) items.
{
	local TheNameChild=""
	local CurrentPid=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')" = "$$" ] ; then
		# Being the main controller process
		rmdir "$ChildsTempDir" 2>/dev/null
		rm -fr "${UserTempDir}/main"
		rmdir "$UserTempDir" 2>/dev/null
	else
		TheNameChild="$(MyIdChild)"
		if [ "$TheNameChild" != "" ] ; then
			# Being a child daemon
			rm -fr "${ChildsTempDir}/${TheNameChild}"
		else
			# Being another call
			if [ $(MeInstalled) -eq 1 ] ; then
				CurrentPid="$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')"
				if [ "$CurrentPid" = "" ] || [ "$(pstree $CurrentPid)" = "" ] ; then
					# Main controller is not running
					if [ "$(RunningChilds_IDs)" = "" ] ; then
						# No childs running
						rm -fr "$UserTempDir"
					fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

LoadChilds ()
# Background childs: Start enabled childs that are not running and not manually stopped.
# Foreground childs: Run childs.
{
	local LoadStopped="$1" # If manually stopped childs must be loaded anyway (1) or not (0)
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local TheEnabledChilds=""
	local TheRunningChilds=""
	local CurrentChild=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	TheEnabledChilds="$(EnabledChilds_IDs)"
	TheRunningChilds="$(RunningChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheEnabledChilds ; do unset IFS
		CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
		if [ "$LoadStopped" = "1" ] ; then rm -f "$CurrentStopFile" ; fi
		if [ "$(printf '%s\n' "$TheRunningChilds" | grep -e "^${CurrentChild}$")" = "" ] && [ ! -f "$CurrentStopFile" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					LogProgram 3 "Starting $ChildSingularName $CurrentChild"
					PrepareExitcodeToFile
					ExecuteString systemctl start "${ServiceName}@${CurrentChild}" 2>&1 | tee -a "$MainControllerLog"
					GetFiledExitcode $?
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						LogProgram 3 "Starting $ChildSingularName $CurrentChild"
						PrepareExitcodeToFile
						ExecuteString systemctl start "${ServiceName}@${CurrentChild}" 2>&1 | tee -a "$MainControllerLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						Start1Process "$CurrentChild" "$MaxChildLoopsNr"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				Start1Process "$CurrentChild" "$MaxChildLoopsNr"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	return $StatusCode
}

ReloadChilds ()
# Background childs: Stop childs that are not enabled, and start enabled childs that are not running.
# Foreground childs: Run childs.
{
	local LoadStopped="$1" # If manually stopped childs must be loaded anyway (1) or not (0)
	local MaxChildLoopsNr="$2"  # -1 for unlimited (default)
	local TheEnabledChilds=""
	local TheRunningChilds=""
	local TheAvailableChilds=""
	local CurrentChild=""
	local CurrentPidFile=""
	local CurrentStopFile=""
	local LastStatus=0
	local StatusCode=0
	
	TheEnabledChilds="$(EnabledChilds_IDs)"
	TheRunningChilds="$(RunningChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheRunningChilds ; do unset IFS
		if [ "$(printf '%s\n' "$TheEnabledChilds" | grep -e "^${CurrentChild}$")" = "" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
					systemctl stop "${ServiceName}@${CurrentChild}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
						CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
						Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}ReloadChilds called${ParC}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 1 0 "${ParO}ReloadChilds called${ParC}"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	TheAvailableChilds="$(AvailableChilds_IDs)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheAvailableChilds ; do unset IFS
		if [ "$(printf '%s\n' "$TheEnabledChilds" | grep -e "^${CurrentChild}$")" = "" ] ; then
			if [ "$InitSoftware" = "systemd" ] && [ "$SystemdChilds" != "0" ] ; then
				# systemctl use only when concrete childs aren't specified.
				if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
					printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
					systemctl stop "${ServiceName}@${CurrentChild}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
						printf '%s\n' "Stopping $ChildSingularName $CurrentChild"
						systemctl stop "${ServiceName}@${CurrentChild}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
						CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
						Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 0 0 "${ParO}ReloadChilds called${ParC}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				CurrentPidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				CurrentStopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				Stop1Daemon "$CurrentPidFile" "$CurrentStopFile" "$ChildSingularName $CurrentChild" 0 0 "${ParO}ReloadChilds called${ParC}"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	LoadChilds "$LoadStopped" "$MaxChildLoopsNr"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

UpstartInitProfile ()
{
	local TheTemplateVersion=""
	local CurrentUnits=""
	local MoreUnits=""
	local StartStopOnEvents=""
	
	# TO DO: Manage runlevels taking some good reference to combine them with "start on others"
	TheTemplateVersion="$(TemplateVersion)"
	if [ "$TheTemplateVersion" != "" ] ; then
		printf '%s\n' "# $ServiceName $(ServiceVersion) service parameters for upstart"
	else
		printf '%s\n' "# $ServiceName service parameters for upstart"
	fi
	printf '%s\n' "# ${ParO}${LongDescription}${ParC}"
	printf '%s\n' "# Note: Many parameters have been parsed from LSB headers of SystemV init script. For example, runlevel* and net-device-up"
	printf '%s\n' ""
	printf '%s\n' "description	\"${ShortDescription}\""
	if [ "$TheTemplateVersion" != "" ] ; then
		printf '%s\n' "version	\"${TheTemplateVersion}\""
	else
		printf '%s\n' "version	\"$(ServiceVersion)\""
	fi
	printf '%s\n' ""
#	CurrentUnits="$(UpstartJobsFromLsbHeader 'Required-Start' | sed -e 's| | and started |')"
	CurrentUnits="$(UpstartJobsFromLsbHeader 'Required-Start' | sed -e 's| | and |' | sed -e 's|__| |')"
#	if [ "$CurrentUnits" != "" ] ; then CurrentUnits="started $CurrentUnits" ; fi
	MoreUnits="$(UpstartRunlevelsFromLsbHeader 'Default-Start')"
	if [ "$MoreUnits" != "" ] ; then
		if [ "$CurrentUnits" != "" ] ; then CurrentUnits="$CurrentUnits and " ; fi
		CurrentUnits="${CurrentUnits}${MoreUnits}"
	fi
	if [ "$CurrentUnits" != "" ] ; then
		printf '%s\n' "start on ${ParO}${CurrentUnits}${ParC}"
		StartStopOnEvents="$StartStopOnEvents $CurrentUnits"
	fi
	# Cannot convert the "Should-Start" logic.
	CurrentUnits="$(echo aaa$(UpstartJobsFromLsbHeader 'Required-Stop') $(UpstartJobsFromLsbHeader 'Should-Stop') | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | sed -e 's| | or stopping |' | sed -e 's|__| |')"
	if [ "$CurrentUnits" != "" ] ; then CurrentUnits="stopping $CurrentUnits" ; fi
	MoreUnits="$(UpstartRunlevelsFromLsbHeader 'Default-Stop')"
	if [ "$MoreUnits" != "" ] ; then
		if [ "$CurrentUnits" != "" ] ; then CurrentUnits="$CurrentUnits or " ; fi
		CurrentUnits="${CurrentUnits}${MoreUnits}"
	fi
	if [ "$CurrentUnits" != "" ] ; then
		printf '%s\n' "stop on ${ParO}${CurrentUnits}${ParC}"
		StartStopOnEvents="$StartStopOnEvents $CurrentUnits"
	fi
	printf '%s\n' ""
#	echo "pre-start exec \"$ProgramExecutablePath\" start"
#	echo "exec \"$ProgramExecutablePath\" start"
	printf '%s\n' "exec \"$ProgramExecutablePath\" start-foreground"
	printf '%s\n' "pre-stop exec \"$ProgramExecutablePath\" stop"
	printf '%s\n' ""
	if [ "$TimeoutStartSec" != "" ] ; then
		if [ $TimeoutStartSec -ge 0 ] ; then
			printf '%s\n' "kill timeout $TimeoutStartSec"
		else
			printf '%s\n' "kill timeout 9999"
		fi
	fi
#	if [ "$DaemonizeOnStart" = "1" ] ; then
#		No way to declare pidfile; upstart assumes foreground pid and cannot do "stop" action.
#		echo "expect fork"
#	fi
	if [ "$StartStopOnEvents" = "" ] ; then
		printf '%s\n' "manual"
	fi
}

EnableSystemService ()
{
	local ExpectedDisabled="$1" # If it's supposed to be disabled (1) or not (0); this only affects messages.
	local LastStatus=0
	local StatusCode=0
	
	if SystemProgramIsEnabled ; then
		if [ $ExpectedDisabled -eq 1 ] ; then
			printf '%s\n' "${sWARN}W: $ServiceName is already enabled to start as system service.${fRESET}" 1>&2
		fi
	else
		case "$ServiceEnabler" in
			"systemctl" )
				systemctl --system enable "${ServiceName}.service"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						systemctl --system enable "${ServiceName}@.service"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							systemctl --system enable "${ServiceName}@.service"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
				;;
			"insserv" )
				insserv --default "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"update-rc.d" )
				update-rc.d "$ServiceName" defaults
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"chkconfig" )
				chkconfig --add "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"$RcLocal" )
				if [ "$RcLocal" != "" ] ; then
					if [ -f "$RcLocal" ] ; then
						if [ "$(cat "$RcLocal" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -E '^exit( |$)')" != "" ] ; then
							RcLinesNr=$(cat "$RcLocal" | wc -l)
							ExitLineNr=$(cat "$RcLocal" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | sed -e '/^exit/q' | wc -l)
							RcPart1="$(cat "$RcLocal" | head -n $(($ExitLineNr - 1)))"
							RcPart2="$(cat "$RcLocal" | tail -n $(($RcLinesNr - $ExitLineNr)))"
							printf '%s\n' "$RcPart1" > "$RcLocal"
							printf '%s\n' "$ProgramExecutablePath start" >> "$RcLocal"
							printf '%s\n' "$RcPart2" >> "$RcLocal"
						else
							if [ "$(cat "$RcLocal")" != "" ] && [ "$(cat "$RcLocal" | tail -n 1)" != "" ] ; then	#"
								printf '\n' >> "$RcLocal"
							fi
							printf '%s\n' "$ProgramExecutablePath start" >> "$RcLocal"
						fi
					else
						printf '%s\n' "${sERROR}E: File not found to write boot execution: ${RcLocal}${fRESET}" 1>&2
						LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					printf '%s\n' "${sERROR}E: Unknown init enablement utility${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"/etc/init" )
				InitProfileFile="${InitProfilesDir}/${ServiceName}.conf"
				UpstartInitProfile > "$InitProfileFile"
				chown root:root "$InitProfileFile"
				chmod u=rw,go=r "$InitProfileFile"
				;;
			"" )
				if [ "$InitProfilesDir" = "" ] ; then
					printf '%s\n' "${sERROR}E: $InitSoftware utility for init enablement not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: No $InitSoftware utility to enable service profile at ${InitProfilesDir}${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported init enablement utility: ${ServiceEnabler}${fRESET}" 1>&2
				LastStatus=51 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')" != "" ] ; then
				NewSystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED' -ive '^RUN=')"
				if [ "$(printf '%s\n' "$NewSystemvEnablementData" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ve '')" != "" ] ; then
					printf '%s\n' "$NewSystemvEnablementData" > "/etc/default/${ServiceName}"
				else
					rm "/etc/default/${ServiceName}"
				fi
			fi
		fi
		if SystemProgramIsEnabled ; then
			printf '%s\n' "$ServiceName enabled to start as system service."
			printf '%s\n' "Service will start on boot, and manually with command: $(RecommendedInvocation start)"
		fi
	fi
	return $StatusCode
}

DisableSystemService ()
{
	local ExpectedEnabled="$1" # If it's supposed to be enabled (1) or not (0); this only affects messages.
	local LastStatus=0
	local StatusCode=0
	
	if ! SystemProgramIsEnabled ; then
		if [ $ExpectedEnabled -eq 1 ] ; then
			printf '%s\n' "${sWARN}W: $ServiceName is already disabled, and is not starting as system service.${fRESET}" 1>&2
		fi
	else
		case "$ServiceEnabler" in
			"systemctl" )
				systemctl --system --force disable "${ServiceName}.service"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ "$(command -v insserv 2>/dev/null)" != "" ] ; then
					# Old template versions enabled services with insserv
					insserv --remove "$ServiceName" > /dev/null 2>&1
				fi
				if [ "$ChildSingularName" != "" ] ; then
					if [ -f "${InitProfilesDir}/${ServiceName}@.service" ] ; then
						systemctl --system --force disable "${ServiceName}@.service"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ "$InitProfilesDir_0" != "" ] && [ -f "${InitProfilesDir_0}/${ServiceName}@.service" ] ; then
							systemctl --system --force disable "${ServiceName}@.service"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
				;;
			"insserv" )
				insserv --remove "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"update-rc.d" )
				update-rc.d -f "$ServiceName" remove
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"chkconfig" )
				chkconfig --del "$ServiceName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
			"$RcLocal" )
				if [ "$RcLocal" != "" ] ; then
					if [ ! -f "$RcLocal" ] ; then
						printf '%s\n' "${sERROR}E: File not found to clean boot execution from: ${RcLocal}${fRESET}" 1>&2
					fi
				else
					printf '%s\n' "${sERROR}E: Unknown init enablement utility${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"/etc/init" )
				InitProfileFile="${InitProfilesDir}/${ServiceName}.conf"
				rm -f "$InitProfileFile"
				;;
			"" )
				if [ "$InitProfilesDir" = "" ] ; then
					printf '%s\n' "${sERROR}E: $InitSoftware utility for init disablement not found ${ParO}systemctl, insserv, update-rc.d,...${ParC}.${fRESET}" 1>&2
					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: No $InitSoftware utility to disable service profile at ${InitProfilesDir}${fRESET}" 1>&2
					LastStatus=57 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported init enablement utility: ${ServiceEnabler}${fRESET}" 1>&2
				LastStatus=51 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
		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
			NewRcContent="$(cat "$RcLocal" | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > "$RcLocal"
		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
			NewRcContent="$(cat /etc/rc.d/rc.local | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > /etc/rc.d/rc.local
		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
			NewRcContent="$(cat /etc/rc.local | grep -ivE '^[[:blank:]]*(${ProgramExecutablePath}|${ServiceName}) start')"
			printf '%s\n' "$NewRcContent" > /etc/rc.local
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ie '^ENABLED' -ie '^RUN=')" != "" ] ; then
				NewSystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED' -ive '^RUN=')"
				if [ "$(printf '%s\n' "$NewSystemvEnablementData" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ve '')" != "" ] ; then
					printf '%s\n' "$NewSystemvEnablementData" > "/etc/default/${ServiceName}"
				else
					rm "/etc/default/${ServiceName}"
				fi
			fi
		fi
		if ! SystemProgramIsEnabled ; then
			printf '%s\n' "$ServiceName disabled; it will not start as system service."
		fi
	fi
	return $StatusCode
}

EnableObject ()
{
	local ExpectedDisabled="$1" # If it's supposed to be disabled (1) or not (0); this only affects messages.
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local TempContent=""
	local ChildProfile_Available=""
	local ChildProfile_Enabled=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Enable child
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		for CurrentChild in "$@" ; do
			ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
			if [ $Operator "$ChildProfile_Available" ] ; then
				ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
				if [ $Operator "$ChildProfile_Enabled" ] && [ "$(ReadlinkF "$ChildProfile_Enabled")" = "$ChildProfile_Available" ] ; then
					if [ $ExpectedDisabled -eq 1 ] ; then
						printf '%s\n' "${sWARN}W: $ChildSingularName $CurrentChild is already enabled for $ServiceName load.${fRESET}" 1>&2
					fi
				else
					rm -fr "$ChildProfile_Enabled"
					if [ "$(ln --help | grep -e '--relative')" != "" ] ; then
						ln -s -r "$ChildProfile_Available" "$ChildProfile_Enabled"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						ln -s "$ChildProfile_Available" "$ChildProfile_Enabled"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					if [ $StatusCode -eq 0 ] ; then
						printf '%s\n' "$ChildSingularName $CurrentChild enabled for $ServiceName load."
					fi
				fi
			else
				printf '%s\n' "${sERROR}E: $ChildSingularName $CurrentChild not found at ${Childs_path_available}${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	else
		# Enable main service for system operation
		if [ $(id -u) -eq 0 ] ; then
			EnableSystemService $ExpectedDisabled
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "${sERROR}E: This program needs superuser ${ParO}root${ParC} permissions to enable $ServiceName as system service${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

DisableObject ()
{
	local ExpectedEnabled="$1" # If it's supposed to be enabled (1) or not (0); this only affects messages.
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local TempContent=""
	local ChildProfile_Available=""
	local ChildProfile_Enabled=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] && [ "$ChildSingularName" != "" ] ; then
		# Disable child
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		for CurrentChild in "$@" ; do
			ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ $Operator "$ChildProfile_Enabled" ] ; then
				rm -fr "$ChildProfile_Enabled"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					printf '%s\n' "$ChildSingularName $CurrentChild disabled; it will not load with ${ServiceName}."
				fi
			else
				# rm anyway because it could be a broken link to undetectable file/dir
				rm -f "$ChildProfile_Enabled"
				ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
				if [ $ExpectedEnabled -eq 1 ] ; then
					if [ $Operator "$ChildProfile_Available" ] ; then
						printf '%s\n' "${sWARN}W: $ChildSingularName $CurrentChild is already disabled, and is not loading with ${ServiceName}.${fRESET}" 1>&2
					else
						printf '%s\n' "${sERROR}E: $ChildSingularName $CurrentChild not found.${fRESET}" 1>&2
						LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		done
	else
		# Disable main service for system operation
		if [ $(id -u) -eq 0 ] ; then
			DisableSystemService $ExpectedEnabled
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "${sERROR}E: This program needs superuser ${ParO}root${ParC} permissions to disable $ServiceName as system service${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

DeleteChild ()
{
	local ChildId="$1"
	local TheExtension=""
	local PidFile=""
	local StopFile=""
	local CurrentChild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		for CurrentChild in "$@" ; do
			ChildProfile="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ ! $Operator "$ChildProfile" ] ; then
				ChildProfile="${Childs_path_available}/${CurrentChild}${TheExtension}"
			fi
			if [ $Operator "$ChildProfile" ] ; then
				PidFile="${ChildsPidsDir}/${CurrentChild}.pid"
				StopFile="${ChildsPidsDir}/${CurrentChild}.stop"
				if [ -f "$PidFile" ] ; then
					Stop1Daemon "$PidFile" "$StopFile" "$ChildSingularName $CurrentChild" 1 1 "${ParO}DeleteChild called${ParC}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				DisableObject 0 "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					rm -f "$StopFile"
					rm -fr "${Childs_path_available}/${CurrentChild}${TheExtension}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $StatusCode -eq 0 ] ; then
					printf '%s\n' "$ChildSingularName $CurrentChild erased." 1>&2
				fi
			else
				printf '%s\n' "${sERROR}E: $ChildSingularName \"$CurrentChild\" not found at ${Childs_path_available}${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName not specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ListChilds ()
{
	local TheChilds=""
	local CurrentChild=""

	TheChilds="$(AvailableChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
		printf '%s\n' "$CurrentChild"
	done
}

ReportChilds ()
{
	local TheChilds=""
	local CurrentChild=""

	TheChilds="$(AvailableChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; EnabledChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" ; RunningChilds_IDs)"
	TheChilds="$(printf '%s\n' "$TheChilds" | grep -ve '^$' | sort -u)"
	IFS="$(printf '\n\b')" ; for CurrentChild in $TheChilds ; do unset IFS
		ListChildReport "$CurrentChild"
	done
}

ServiceHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local ChildActions=''
	local InstallerActions=''
	local BaseName=''
	
	BaseName="$(printf '%s\n' "$MeExecutable" | tr -s '/' '\n' | tail -n 1)"
	printf '%s\n' "${ShortDescription} ${ParO}${ServiceName}${ParC} $(ServiceVersion)"
	printf '%s\n' "$ServiceDocumentation"
	printf '%s\n' ""
	if [ $(MeInstalled) -eq 1 ] ; 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 [ "$ChildsPluralName" != "" ] ; then
			ChildActions="|reload|create|delete|list|report|details"
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			InstallerActions="|uninstall|purge"
		fi
#[durruter]
#		printf '%s\n' "Usage: ${SudoPrefix}${ServicePrefix}${ServiceName} {start|stop|restart|status|enable|disable${ChildActions}${InstallerActions}}${SudoSuffix}"
#		printf '%s\n' ""
#		if [ $(id -u) -eq 0 ] && ! SystemProgramIsEnabled ; then
#			printf '%s\n' "Example to enable the service:"
#			printf '%s\n' "	$(RecommendedInvocation enable)"
#		fi
#		PidfileStatus "$MainControllerPidFile"
#		AskedPidStatus=$?
#		if [ $AskedPidStatus -ne 1 ] ; then
#			printf '%s\n' "Example to start the service:"
#			printf '%s\n' "	$(RecommendedInvocation start)"
#		else
#			printf '%s\n' "Example to stop the service:"
#			printf '%s\n' "	$(RecommendedInvocation stop)"
#		fi
#		if [ "$ChildActions" != "" ] ; then
#			printf '%s\n' "Example to see details about one ${ChildSingularName}:"
#			printf '%s\n' "	$(RecommendedInvocation details ${ChildSingularName})"
#			if [ "$DaemonizeChilds" = "1" ] ; then
#				printf '%s\n' "Examples to run 1 shot only per each ${ChildSingularName}, and in a foreground sequential way:"
#				printf '%s\n' "	$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1) main 1"
#				printf '%s\n' "	$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1) childs-foreground 1"
#			fi
#		fi
		if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
			printf '%s\n' "Sintaxis:"
			printf '%s\n' ""
			printf '%s\n' "$ServiceName nat           (Salidas a internet/WAN)"
			printf '%s\n' "$ServiceName dnat          (Entradas de puertos internet/WAN hacia equipos locales)"
#			printf '%s\n' "$ServiceName shaping       (Moderacion de trafico - frenos)"
			printf '%s\n' "$ServiceName service       (Administracion del servicio entero)"
			printf '%s\n' "$ServiceName bans          (Listar direcciones IP bloqueadas desde Fail2ban)"
			printf '%s\n' "$ServiceName unban4fail    (desbloquear una IP para Fail2ban)"
			printf '%s\n' "$ServiceName monitor [NIC] (Bitacora en vivo de los eventos IP)"
			printf '%s\n' "$ServiceName via           (rumbos que los paquetes IP tomaran)"
			printf '%s\n' "$ServiceName addresses     Informe de direciones del sistema"
			if [ "$ProgramInstaller" = "1" ] ; then
				printf '%s\n' "$ServiceName uninstall (Desinstalar $ServiceName)"
				printf '%s\n' "$ServiceName purge     (Desinstalar $ServiceName y borrar sus datos)"
			fi
			printf '%s\n' ""
			printf '%s\n' "Nota: La propia configuracion se guarda en ${SystemConfigDir}/"
		else
			printf '%s\n' "Syntax:"
			printf '%s\n' ""
			printf '%s\n' "$ServiceName nat           (Outputs to internet/WAN)"
			printf '%s\n' "$ServiceName dnat          (Port inputs from internet/WAN and to local computers)"
#			printf '%s\n' "$ServiceName shaping       (Traffic moderation)"
			printf '%s\n' "$ServiceName service       (Entire service administration)"
			printf '%s\n' "$ServiceName bans          (List IP addresses blocked from Fail2ban)"
			printf '%s\n' "$ServiceName unban4fail    (unlock an IP for Fail2ban)"
			printf '%s\n' "$ServiceName monitor [NIC] (live log of IP events)"
			printf '%s\n' "$ServiceName via           (directions IP packets will take)"
			printf '%s\n' "$ServiceName addresses     Report addresses bound to host"
			if [ "$ProgramInstaller" = "1" ] ; then
				printf '%s\n' "$ServiceName uninstall (Remove $ServiceName)"
				printf '%s\n' "$ServiceName purge     (Uninstall $ServiceName and delete its data)"
			fi
			printf '%s\n' ""
			printf '%s\n' "Note: Configuration is saved at ${SystemConfigDir}/"
		fi
#[/durruter]
	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 service:"
			printf '%s\n' "${SudoPrefix}${BaseName} install${SudoSuffix}"
			printf '%s\n' ""
			printf '%s\n' "To remove the service:"
			printf '%s\n' "${SudoPrefix}${ServicePrefix}${ServiceName} uninstall${SudoSuffix}"
		else
			printf '%s\n' "${sWARN}W: Program not installed. Use your package manager to install ${ServiceName}.${fRESET}"
		fi
	fi
}

ChildController_Kernel ()
{
	local ChildId="$1"
	local ChildProfile="$2"
	local MaxLoopsNr="$3"  # -1 for unlimited (default)
	local RemainLoopsNr=-1
	local ChildStopFile=""
	local ChildLog="${Childs_logs}/${ChildId}.log"	# Note: ChildController_Kernel() is already logged by caller
	local LastStatus=0
	local StatusCode=0
	
	ChildStopFile="${ChildsPidsDir}/${ChildId}.stop"
	if [ -f "$ChildStopFile" ] ; then printf '%s\n' "${sWARN}W: File $ChildStopFile avoids $ChildSingularName bucle.${fRESET}" 1>&2 ; fi
	if ! Is_IntegerNr "$MaxLoopsNr" ; then MaxLoopsNr=-1 ; fi
	if [ $MaxLoopsNr -ge 0 ] ; then RemainLoopsNr=$MaxLoopsNr ; fi
	while [ ! -f "$ChildStopFile" ] && [ $RemainLoopsNr -ne 0 ] ; do
		printf '%s\n' "$ChildsPluralName controller not yet developed." 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		WaitStoppable 10 "$ChildStopFile" 0.5
		RemainLoopsNr=$((RemainLoopsNr - 1))
	done
	if [ -f "$ChildStopFile" ] ; then printf '%s\n' "I: $ChildSingularName $ChildId terminates with stop signal." 1>&2 ; fi
	Pipelined_Status=$StatusCode
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ChildController ()
{
	local ChildId="$1"
	local MaxLoopsNr="$2"  # -1 for unlimited (default)
	local ChildProfile=""
	local Operator=""
	local TheExtension=""
	local LogLinesNr=""
	local PidFile=""
	local RunningPid=""
	local ChildLog="${Childs_logs}/${ChildId}.log"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$InitSoftware" = "systemd" ] ; then ChildId="$(printf '%s\n' "$ChildId" | sed -e 's|\\x20| |g')" ; fi	# systemd parses spaces as: \x20
		if [ $INIT_SCRIPT_InitCall -ne 1 ] ; then
			CallType=" ${ParO} Manual ${ParC}"
#		else
#			CallType=" ${ParO}INIT_SCRIPT_InitCall${ParC}"
		fi
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
			Operator="-f"
		else
			TheExtension=""
			Operator="-d"
		fi
		ChildProfile="${Childs_path_enabled}/${ChildId}${TheExtension}"
		if [ ! $Operator "$ChildProfile" ] ; then
			ChildProfile="${Childs_path_available}/${ChildId}${TheExtension}"
		fi
		if [ $Operator "$ChildProfile" ] ; then
			PidFile="${ChildsPidsDir}/${ChildId}.pid"
			PidfileStatus "$PidFile" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then RunningPid=$(cat "${ChildsPidsDir}/${ChildId}.pid" | sed -e 's| ||g') ; fi
#			if [ $LastStatus -ne 0 ] || [ $RunningPid -eq $$ ] ; then
			if [ $LastStatus -ne 0 ] || [ "$(ParentPstree $$ | sed -e 's|^| |g' | sed -e 's|$| |g' | grep -e " $RunningPid ")" != "" ] ; then
				# Child not running or it's me. This function can be called directly through "child-foreground" action.
				Pipelined_Status=""
				printf '%s\n' "$(date '+%Y-%m-%dT%T%z')${CallType} Launching $ServiceName task for $ChildSingularName $ChildId" | tee -a "$ChildLog"
				if [ $Child_MaxLogLines -gt 0 ] ; then
					# Limited log lines number
					if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
						printf '%s\n' "Recovering previous broken log from ${Childs_logs}/${ChildId}.log.last"
						LogLinesNr=$(cat "${Childs_logs}/${ChildId}.log.last" | wc -l)
						if [ $LogLinesNr -gt $Child_MaxLogLines ] ; then
							cat "${Childs_logs}/${ChildId}.log.last" | head -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
							printf '%s\n' "${ParO}...${ParC} $(($LogLinesNr - $Child_MaxLogLines)) log lines omitted here." >> "$ChildLog"
							cat "${Childs_logs}/${ChildId}.log.last" | tail -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
						else
							cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						fi
						rm "${Childs_logs}/${ChildId}.log.last"
					fi
					# Pipelines use makes threading one more process as own beginner's child.
					PrepareExitcodeToFile
					ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr" 2>&1 | tee -a "${Childs_logs}/${ChildId}.log.last"
					GetFiledExitcode $?
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
						printf '%s\n' "Recovering previous broken log from ${Childs_logs}/${ChildId}.log.last"
						cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						rm "${Childs_logs}/${ChildId}.log.last"
					fi
					if [ $Child_MaxLogLines -eq 0 ] ; then
						# No log
						Start_Pre "$ChildId"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						Start_Post "$ChildId"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						# Unlimited log
						# Pipelines use makes threading one more process as own beginner's child.
						PrepareExitcodeToFile
						Start_Pre "$ChildId" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						PrepareExitcodeToFile
						ChildController_Kernel "$ChildId" "$ChildProfile" "$MaxLoopsNr" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						PrepareExitcodeToFile
						Start_Post "$ChildId" 2>&1 | tee -a "$ChildLog"
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
				if [ "$Pipelined_Status" != "" ] ; then
					LastStatus=$Pipelined_Status ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ -f "${Childs_logs}/${ChildId}.log.last" ] ; then
					printf '%s\n' "Appending session log to ${Childs_logs}/${ChildId}.log"
					if [ $Child_MaxLogLines -gt 0 ] ; then
						LogLinesNr=$(cat "${Childs_logs}/${ChildId}.log.last" | wc -l)
						if [ $LogLinesNr -gt $Child_MaxLogLines ] ; then
							cat "${Childs_logs}/${ChildId}.log.last" | head -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
							printf '%s\n' "${ParO}...${ParC} $(($LogLinesNr - $Child_MaxLogLines)) log lines omitted here." >> "$ChildLog"
							cat "${Childs_logs}/${ChildId}.log.last" | tail -n $(($Child_MaxLogLines / 2)) >> "$ChildLog"
						else
							cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
						fi
					else
						cat "${Childs_logs}/${ChildId}.log.last" >> "$ChildLog"
					fi
					rm "${Childs_logs}/${ChildId}.log.last"
				fi
			else
				LogProgram 1 "E: $ChildSingularName $ChildId is already running with PID ${RunningPid}."
				LastStatus=107 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			printf '%s\n' "${sERROR}E: $ChildSingularName $ChildId not found at ${Childs_path_available}${fRESET}" 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName not specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Configuration_InitSystem ()
# 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
				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
}

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 InitCall_Previous="$INIT_SCRIPT_InitCall"
	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
	TheFoundServiceScript="$(FoundServiceScript)"
	if [ $StatusCode -eq 0 ] && [ -x "$TheFoundServiceScript" ] ; then
		if [ "$PackageManager_Call" = "" ] && [ "$Mode" != "postcp" ] && [ "$Mode" != "force" ] && [ "$Mode2" != "force" ] ; then
#			if [ "$(IniVarValue "$(cat "$TheFoundServiceScript" | grep -e '^ProgramInstaller=' | head -n 1)" ProgramInstaller '' '' = '' '')" = "0" ] ; then
			if [ "$(cat "$TheFoundServiceScript" | 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' "$TheFoundServiceScript" | 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
			# /PreviousService* records used here on manual script upgrade
			if [ ! -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}" ] ; then
				if [ "$PreviousServiceEnabled" = "" ] ; then
					if SystemProgramIsEnabled ; then
						PreviousServiceEnabled=1
					else
						PreviousServiceEnabled=0
					fi
				fi
				echo $PreviousServiceEnabled > "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
			fi
			AllChildsStopTimeoutS=$(ls -1A "${CurrentConfigDir}/${ChildsPluralName}_available" 2>/dev/null | wc -l)
			AllChildsStopTimeoutS=$(($AllChildsStopTimeoutS * $GentleStopTimeoutS))
			if [ "$StopStartOnUpgrade" = "1" ] ; then
				PidfileStatus "$MainControllerPidFile"
				LastStatus=$?
				if [ $LastStatus -eq 0 ] ; then
					printf '%s\n' "Trying to stop previous $ServiceName"
					PreviousServiceRunning=1
					echo $PreviousServiceRunning > "${DirTemp}/PreviousServiceRunning.${ServiceName}"
				fi
				TimedExecution $(($AllChildsStopTimeoutS + $GentleStopTimeoutS)) 2 "" $(RecommendedInvocation stop) 2>&1 | grep -ive 'Unknown'
			else
				rm -f "${DirTemp}/PreviousServiceRunning.${ServiceName}"
			fi
			if [ "$Mode" != "postcp" ] ; then
				if [ $AllChildsStopTimeoutS -lt $GentleStopTimeoutS ] ; then AllChildsStopTimeoutS=$GentleStopTimeoutS ; fi
				printf '%s\n' "Trying to uninstall previous $ServiceName"
				# Sub-execution will also spend time trying to stop service
				TimedExecution $(($TimeoutStartSec + $AllChildsStopTimeoutS + $GentleStopTimeoutS)) 1 "" "$TheFoundServiceScript" uninstall "$@" 2>&1 | grep -ive 'Trying to stop' | grep -ive 'not running' | grep -ive 'Unknown'
				#LastStatus=$?	pipelines override execution exitcode.
				#if [ $LastStatus -eq 87 ] || [ $LastStatus -eq 127 ] ; then # Unknown action
				#	rm "$TheFoundServiceScript"
				#	LastStatus=$?
				#fi
				#if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ -x "$TheFoundServiceScript" ] ; then
					printf '%s\n' "${sWARN}W: old executable $TheFoundServiceScript not removed.${fRESET}" 1>&2
					printf '%s\n' "Actions recommended:"
					printf '%s\n' "	1. Sure service ${ServiceName} is stopped"
					printf '%s\n' "	2. Retry un/installation"
					printf '%s\n' "	3. Remove old program manually if necessary"
					LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			# Important to do this after uninstalling previous version.
			Configuration_Saved "$@"
			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 are at: $SystemConfigFile"
		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 service script.
		DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}"
		DeleteIfNotMe "/etc/init.d/${ServiceName}"
		DeleteIfNotMe "/bin/${ServiceName}"
		DeleteIfNotMe "/sbin/${ServiceName}"
		DeleteIfNotMe "/usr/bin/${ServiceName}"
		DeleteIfNotMe "/usr/sbin/${ServiceName}"
		DeleteIfNotMe "/usr/local/bin/${ServiceName}"
		DeleteIfNotMe "/usr/local/sbin/${ServiceName}"
		DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}.sh"
		DeleteIfNotMe "/etc/init.d/${ServiceName}.sh"
		DeleteIfNotMe "/bin/${ServiceName}.sh"
		DeleteIfNotMe "/sbin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/bin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/sbin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/local/bin/${ServiceName}.sh"
		DeleteIfNotMe "/usr/local/sbin/${ServiceName}.sh"
		DeleteIfNotMe "$InitExecutableParser"
		DeleteIfNotMe "$ProgramExecutablePath"
	fi
	touch "${DirTemp}/${ServiceName}.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" != "$InitExecutableParser" ] ; 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"
#			if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$ProgramExecutablePath" | grep -e '^/usr/' -e '^/bin/' -e '^/sbin/')" = "" ] && [ "$(printf '%s\n' "$InitExecutableParser" | grep -e '^/usr/' -e '^/bin/' -e '^/sbin/')" != "" ] ; then
#				# Program has been installed in an init place such as /etc/init.d/ and link is intended to allow normal PATH access.
#				PreviousUmask="$(umask)"
#				umask u=rwx,go=rx
#				rm -f "$InitExecutableParser"
#				ln -s "$ProgramExecutablePath" "$InitExecutableParser"
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				umask "$PreviousUmask"
#			fi
		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 LastStatus=0
	local StatusCode=0
	
	if [ ! -f "${DirTemp}/${ServiceName}.install-precp.done" ] ; then
		Install_precp postcp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		rm -f "${DirTemp}/${ServiceName}.install-precp.done"
	fi
#[durruter]
#	if [ $StatusCode -eq 0 ] ; then
#		InstallSystemService "$ProgramExecutablePath" "$ServiceName" "$@"
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#	fi
#[/durruter]
	if [ $StatusCode -eq 0 ] ; then
		if [ "$PreviousServiceEnabled" = "" ] ; then PreviousServiceEnabled="$(cat "${DirTemp}/PreviousServiceEnabled.${ServiceName}" 2>/dev/null)" ; fi
		rm -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
		if [ "$PreviousServiceEnabled" = "1" ] ; then
			# Keep enablement status
			EnableSystemService 0
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$PreviousServiceEnabled" = "0" ] ; then
				# Keep enablement status
				DisableSystemService 0 > /dev/null 2>&1
			else
				# Set first enablement status
				if [ "$DisabledAtFirst" = "0" ] ; then
					EnableSystemService 0
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					DisableSystemService 0 > /dev/null 2>&1
				fi
			fi
		fi
	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 ] ; then
		if [ "$PreviousServiceRunning" = "" ] ; then PreviousServiceRunning="$(cat "${DirTemp}/PreviousServiceRunning.${ServiceName}" 2>/dev/null)" ; fi
		rm -f "${DirTemp}/PreviousServiceRunning.${ServiceName}"
		if [ "$PreviousServiceRunning" = "1" ] ; then
			printf '%s\n' "Starting $ServiceName $(ServiceVersion)"
			if SystemProgramIsEnabled ; then
				eval $(RecommendedInvocation start)
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

Uninstall_predel ()
{
	local LastStatus=0
	local StatusCode=0
	
	# /PreviousService* records used here on package manager upgrade
	if [ ! -f "${DirTemp}/PreviousServiceEnabled.${ServiceName}" ] ; then
		if [ "$PreviousServiceEnabled" = "" ] ; then
			if SystemProgramIsEnabled ; then
				PreviousServiceEnabled=1
			else
				PreviousServiceEnabled=0
			fi
		fi
		echo $PreviousServiceEnabled > "${DirTemp}/PreviousServiceEnabled.${ServiceName}"
	fi
	TheFoundServiceScript="$(FoundServiceScript)"
	if [ "$StopStartOnUpgrade" = "1" ] ; then
		PidfileStatus "$MainControllerPidFile"
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			PreviousServiceRunning=1
			echo $PreviousServiceRunning > "${DirTemp}/PreviousServiceRunning.${ServiceName}"
		fi
		if [ -x "$TheFoundServiceScript" ] ; then
			printf '%s\n' "Trying to stop $ServiceName"
			eval $(RecommendedInvocation stop)
			sleep 1
		fi
	fi
	Uninstall_predel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	UnInstallSystemService "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "System service removed."
	fi
	return $StatusCode
}

Uninstall_del ()
{
	local MeVersion''
	MeVersion="$(ServiceVersion)"
#	DeleteIfNotMe "$InitExecutableParser"
#	DeleteIfNotMe "$ProgramExecutablePath"
#	DeleteIfNotMe "/etc/rc.d/init.d/${ServiceName}"
#	DeleteIfNotMe "/etc/init.d/${ServiceName}"
#	DeleteIfNotMe "/bin/${ServiceName}"
#	DeleteIfNotMe "/sbin/${ServiceName}"
#	DeleteIfNotMe "/usr/bin/${ServiceName}"
#	DeleteIfNotMe "/usr/sbin/${ServiceName}"
#	DeleteIfNotMe "/usr/local/bin/${ServiceName}"
#	DeleteIfNotMe "/usr/local/sbin/${ServiceName}"
	rm -f "/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/init.d/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/rc.d/init.d/${ServiceName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/init.d/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/etc/rc.d/init.d/${ServiceName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "$InitExecutableParser"
	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_Postdel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_purge ()
{
	local LastStatus=0
	local StatusCode=0
	
	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 "$MainControllerLog"
	rm -f "/var/log/upstart/${ServiceName}."*
	Uninstall_purge_MorePost "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "${DirTemp}/${ServiceName}.uninstall.tmp" "/var/tmp/${ServiceName}.uninstall.tmp"
	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_Early ()
# To minimize load on fast fork. Convenient to not write to disk.
{
	local LastStatus=0
	local StatusCode=0
	
	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
	if [ -d /run ] ; then
		MainControllerPidFile="/run/${ServiceName}.pid"
	else
		# Old FHS
		MainControllerPidFile="/var/run/${ServiceName}.pid"
	fi
	if [ "$RootRequired" = "0" ] && [ $(id -u) -ne 0 ] ; then
		MainControllerPidFile="${DirTemp}/${ServiceName}.$(id -u)/${ServiceName}.pid"
	else
		MainControllerLog="/var/log/${ServiceName}.log"
	fi
	Configuration_Early_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Configuration_Saved ()
# Settings that can be saved to disk, unless "ro" parameter is passed
{
	local ReadOnly=0
	local CallTree=''
	local CallTreeLevels=''
	local CurrentFile=''
	local VarDescription=''
	local BaseName=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
	BreakingControls
	Configuration_Early "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	ShortDescription="$(LsbHeaderValue 'Short-Description')"
	LongDescription="$(LsbHeaderValue 'Description')"
	Accumulated_Childs=0
	Accumulated_ChildsRunning=0
	Accumulated_pid=0
	Accumulated_start=now
	Accumulated_pcpu10=0
	Accumulated_rss=0
	Accumulated_Threads=0
	Configuration_Saved_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	# Global stop file is an external signal to abandon any program activity (start/wait/stop) and it's never invoked by program itself.
	if [ -d /run ] ; then
		MainControllerStopFile="/run/${ServiceName}.stop"
	else
		# Old FHS
		MainControllerStopFile="/var/run/${ServiceName}.stop"
	fi
	if [ ! -d "$SystemConfigDir" ] && [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigDir")" ] ; then
		mkdir -p "$SystemConfigDir"
		chmod u=rwX,go=rX "$SystemConfigDir"
	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 "/etc/default/${ServiceName}" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] ; then
			mv "/etc/default/${ServiceName}" "$SystemConfigFile"
		else
			SystemConfigFile="/etc/default/${ServiceName}"
		fi
	fi
	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 [ "$(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
		mkdir -p "$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
	CurrentConfigDir="$SystemConfigDir"
	CurrentConfigFile="$SystemConfigFile"
	if [ "$RootRequired" = "0" ] && [ $(id -u) -ne 0 ] ; then
		CurrentConfigDir="$UserConfigDir"
		CurrentConfigFile="$UserConfigFile"
		# Global stop file is an external signal to abandon any program activity (start/wait/stop) and it's never invoked by program itself.
		MainControllerStopFile="${DirTemp}/${ServiceName}.$(id -u)/${ServiceName}.stop"
	fi
	if [ -f "${SystemConfigDir}/${ServiceName}.conf" ] && [ "$SystemConfigFile" != "${SystemConfigDir}/${ServiceName}.conf" ] ; then
		# Old config file present
		if [ $ReadOnly -eq 0 ] ; then
			# Migrate old file to new path
			if [ ! -f "$SystemConfigFile" ] ; then
				mv "${SystemConfigDir}/${ServiceName}.conf" "$SystemConfigFile"
			else
				cat "${SystemConfigDir}/${ServiceName}.conf" >> "$SystemConfigFile"
				mv "${SystemConfigDir}/${ServiceName}.conf" "${SystemConfigDir}/${ServiceName}.conf.bak"
			fi
		else
			# Assume old file as the good one
			if [ ! -f "$SystemConfigFile" ] ; then SystemConfigFile="${SystemConfigDir}/${ServiceName}.conf" ; fi
		fi
	fi
	if [ $(id -u) -eq 0 ] && [ $ReadOnly -eq 0 ] ; then
		if [ -f "/etc/default/${ServiceName}" ] ; then
			# Remove old file only dedicated to enablement variable.
			SystemvEnablementData="$(cat "/etc/default/${ServiceName}" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | grep -ive '^ENABLED=' -ive '^RUN=' | grep -ve '^$')"
			if [ "$SystemvEnablementData" = "" ] ; then rm "/etc/default/${ServiceName}" ; fi
		fi
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		# Only when working with unprivileged childs, user config directory is sure necessary
		if [ ! -d "$UserConfigDir" ] && [ "$RootRequired" != "1" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$UserConfigDir"
			chmod u=rwX,g=rX,o= "$UserConfigDir"
		fi
	fi
	if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigFile")" ] ; then
		if [ ! -f "$SystemConfigFile" ] ; then
			printf '%s\n' "# $LongDescription" >> "$SystemConfigFile"
			if [ "$RootRequired" = "0" ] ; then
				printf '%s\n' "# $ServiceName system-wide and user-default working parameters" >> "$SystemConfigFile"
			else
				printf '%s\n' "# $ServiceName working parameters" >> "$SystemConfigFile"
			fi
			printf '%s\n' "" >> "$SystemConfigFile"
			printf '%s\n' "# Main log is written to: $MainControllerLog" >> "$SystemConfigFile"
			printf '%s\n' "# You can create this file as a signal to program abandons any activity (start/wait/stop): $MainControllerStopFile" >> "$SystemConfigFile"
			printf '%s\n' "" >> "$SystemConfigFile"
		fi
	fi
	if [ $(id -u) -ne 0 ] ; then
		MainControllerLog="${UserConfigDir}/${ServiceName}.log"
	fi
	UserTempDir="${DirTemp}/${ServiceName}.$(id -u)"
	if [ ! -d "$UserTempDir" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$UserTempDir"
		chmod u=rwX,g=rX,o= "$UserTempDir"
	fi
	UserTempDirX="${DirTempX}/${ServiceName}.$(id -u)"
	if [ ! -d "$UserTempDirX" ] && [ $ReadOnly -eq 0 ] ; then
		mkdir -p "$UserTempDirX"
		chmod u=rwX,g=rX,o= "$UserTempDirX"
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		if [ -d /run ] ; then
			ChildsPidsDir="/run/${ServiceName}.d"
		else
			# Old FHS
			ChildsPidsDir="/var/run/${ServiceName}.d"
		fi
		if [ $(id -u) -ne 0 ] ; then
			ChildsPidsDir="${UserTempDir}/${ChildsPluralName}.run"
		fi
		ChildsTempDir="${UserTempDir}/${ChildsPluralName}.d"
		if [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$ChildsTempDir"
		fi
		Childs_path_available="${CurrentConfigDir}/${ChildsPluralName}_available"
		if [ ! -d "$Childs_path_available" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$Childs_path_available"
			chmod u=rwX,g=rX,o= "$Childs_path_available"
		fi
		Childs_path_enabled="${CurrentConfigDir}/${ChildsPluralName}_enabled"
		if [ ! -d "$Childs_path_enabled" ] && [ $ReadOnly -eq 0 ] ; then
			mkdir -p "$Childs_path_enabled"
			chmod u=rwX,g=rX,o= "$Childs_path_enabled"
		fi
		LoadVarsValues "$UserConfigFile" "${ChildsPluralName}_logs ${ChildSingularName}_LoadWithCalm ${ChildSingularName}_MaxLogLines ${ChildsPluralName}_MoreLoadsInterval SystemdChilds" 2 "$SystemConfigFile"
		LastStatus=$?
		VarsTranslation="Childs_logs=\"\$${ChildsPluralName}_logs\" Child_LoadWithCalm=\"\$${ChildSingularName}_LoadWithCalm\" Child_MaxLogLines=\"\$${ChildSingularName}_MaxLogLines\" Childs_MoreLoadsInterval=\"\$${ChildsPluralName}_MoreLoadsInterval\""
		eval $VarsTranslation
		if [ $LastStatus -eq 104 ] ; then
			Childs_logs="/var/log/${ServiceName}_${ChildsPluralName}"
			if [ $(id -u) -ne 0 ] ; then
				Childs_logs="${UserConfigDir}/log"
			fi
			Childs_logs="$(GetSetLocalConfig "$ReadOnly" "${ChildsPluralName}_logs" '' "\"${Childs_logs}\"" "\n# Directory for all logs from $ChildSingularName tasks")"	#in
			if [ $ReadOnly -eq 0 ] ; then
				mkdir -p "$Childs_logs"
			fi
			VarDescription="\n# Percent of ${ParO}descending${ParC} load average to wait for, before each $ChildSingularName automatic launch. Set it to -1 for not \"waiting to calm\""
			if [ "$DaemonizeOnStart" = "0" ] && [ $TimeoutStartSec -ge 0 ] ; then
				VarDescription="${VarDescription}\n# WARNING: Due to main controller is not forking as a daemon, service start can be killed after $TimeoutStartSec seconds."
			fi
			Child_LoadWithCalm="$(GetSetLocalConfig "$ReadOnly" "${ChildSingularName}_LoadWithCalm" '' "$Child_LoadWithCalm_Default" "$VarDescription")"
			if ! Is_IntegerNr "$Child_LoadWithCalm" ; then
				Child_LoadWithCalm=-1
				printf '%s\n' "${sWARN}W: Invalid ${ChildSingularName}_LoadWithCalm value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			Child_MaxLogLines="$(GetSetLocalConfig "$ReadOnly" "${ChildSingularName}_MaxLogLines" '' -1 "\n# ${ChildSingularName}_MaxLogLines: Set it to -1 for unlimited $ChildSingularName log, 0 for no $ChildsPluralName log, and great that 0 to limit log lines number to archive from each $ChildSingularName session.")"	#in
			if ! Is_IntegerNr "$Child_MaxLogLines" ; then
				Child_MaxLogLines=-1
				printf '%s\n' "${sWARN}W: Invalid ${ChildSingularName}_MaxLogLines value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			Childs_MoreLoadsInterval="$(GetSetLocalConfig "$ReadOnly" "${ChildsPluralName}_MoreLoadsInterval" '' $Childs_MoreLoadsInterval_Default "\n# ${ChildsPluralName}_MoreLoadsInterval: Seconds of interval to retry load the $ChildsPluralName that are enabled but not running. Zero for not more automatic loads after service start.")"	#in
			if ! Is_IntegerNr "$Childs_MoreLoadsInterval" ; then
				Childs_MoreLoadsInterval=0
				printf '%s\n' "${sWARN}W: Invalid ${ChildsPluralName}_MoreLoadsInterval value at ${SystemConfigFile}${fRESET}" 1>&2
			fi
			if [ "$DaemonizeChilds" != "0" ] && [ "$InitSoftware" = "systemd" ] ; then
				SystemdChilds="$(GetSetLocalConfig "$ReadOnly" SystemdChilds '' 0 "\n# Systemd is limiting the number of child processes to fork; 1=Use Systemd to launch $ChildsPluralName; 0=Launch $ChildsPluralName with independent method.")"
				# (this issue is not affecting to "stop" actions)
			fi
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$LogLevel" = "4" ] ; then ForcedLogLevel=$LogLevel ; fi  # Debugging probably a nested call (exported LogLevel)
	LoadVarsValues "$UserConfigFile" 'FirstProcessesToWaitFor CleanExecutionToWaitFor LogLevel MaxLogLines' 2 "$SystemConfigFile"
	LastStatus=$?
	if [ $LastStatus -eq 104 ] ; then
		VarDescription="\n# FirstProcessesToWaitFor: Wait until named processes are running before start actions. ${ParO}blank = no wait${ParC}.\n#FirstProcessesToWaitFor=NetworkManager,dbus-daemon"
		FirstProcessesToWaitFor="$(GetSetLocalConfig "$ReadOnly" FirstProcessesToWaitFor '' "" "$VarDescription")"
		VarDescription="\n# CleanExecutionToWaitFor: Wait until command exits with status code 0 before start actions. ${ParO}blank = no wait${ParC}.\n# Syntax is interval seconds to repeat execution|Command\n#CleanExecutionToWaitFor=\"10|nc -v -w 4 -z 216.146.35.35 53\"\n#CleanExecutionToWaitFor=\"60|/bin/true\""
		CleanExecutionToWaitFor="$(GetSetLocalConfig "$ReadOnly" CleanExecutionToWaitFor '' '""' "$VarDescription")"
		LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '\n# 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
	if [ "$ForcedLogLevel" != "" ] ; then  # Debugging probably a nested call (exported LogLevel)
		LogLevel=$ForcedLogLevel
	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
}

Configuration ()
# Full configuration
{
	local ReadOnly=0
	local LastStatus=0
	local StatusCode=0
	
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$FastConfiguration" = "1" ] ; then
			Configuration_Early "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Configuration_InitSystem "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			Configuration_Saved "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}


##### SERVICE FUNCTIONS from @-funcions 0:2023.08.29 #####

#[durruter]
QuoteAsNeeded1 ()
# Syntax as a function: $(QuoteAsNeeded1 [String1] [String2] [...])
# Description: Returns (stdout) each specified parameter by adding SINGLE quotes to it only if it contains spaces, tabs or quotes.
# Notes:
#	- Changes to double quote if a string already contains single quotes
# Depends on functions: (none)
# Depends on software packages: grep sed
{
	local CurParameter=''
	local First=1
	
	for CurParameter in "$@" ; do
		if [ $First -eq 0 ] ; then printf ' ' ; fi
		if [ "$(printf '%s' "$CurParameter" | grep -e "'")" != "" ] ; then
			printf '"'
			printf '%s' "$CurParameter" | sed -e 's|"|\\"|g'
			printf '"'
		else
			if [ $(WordsNr () { printf '%s' $#; }; WordsNr $CurParameter) -gt 1 ] ; then
				printf "'%s'" "$CurParameter"
			else
				if [ "$(printf '%s' "$CurParameter" | grep -e '"')" != "" ] ; then
					printf "'%s'" "$CurParameter"
				else
					printf '%s' "$CurParameter"
				fi
			fi
		fi
		First=0
	done
	if [ $First -eq 0 ] ; then printf '\n' ; fi
}

QuoteAsNeeded2 ()
# Syntax as a function: $(QuoteAsNeeded1 [String1] [String2] [...])
# Description: Returns (stdout) each specified parameter by adding DOUBLE quotes to it only if it contains spaces, tabs or quotes.
# Notes:
#	- Escapes any double quote found
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local CurParameter=''
	local First=1
	
	for CurParameter in "$@" ; do
		if [ $First -eq 0 ] ; then printf ' ' ; fi
		if [ "$(printf '%s' "$CurParameter" | grep -e '"')" != "" ] ; then
			printf '"'
			printf '%s' "$CurParameter" | sed -e 's|"|\\"|g'
			printf '"'
		else
			if [ $(WordsNr () { printf '%s' $#; }; WordsNr $CurParameter) -gt 1 ] ; then
				printf '"%s"' "$CurParameter"
			else
				if [ "$(printf '%s' "$CurParameter" | grep -e "'")" != "" ] ; then
					printf '"%s"' "$CurParameter"
				else
					printf '%s' "$CurParameter"
				fi
			fi
		fi
		First=0
	done
	if [ $First -eq 0 ] ; then printf '\n' ; 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.
# 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
}

PrimerElementoEncontrado ()
# Sintaxis como función: $(PrimerElementoEncontrado $Test "$Elementos")
# Descripción:
#	De entre los elementos especificados, devuelve el primero
#	que corresponde a un fichero/directorio existente.
# Parámetros esperados:
#	$1	Test a hacer. 3 ejemplos: -f -d -x
#		Se pueden combinar varios test. Por ejemplo -fd para fichero OR directorio.
#		Usando el signo + se aplica AND: Por ejemplo +fx para fichero Y ejecutable.
#	$2	Lista (separada por ":") de elementos a evaluar. Para
#		cada elemento, se ignoran los espacios de inicio y fin.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local Test="$1"
	local Elementos="$2"
	local ElementoActual=''
	local TestActual=''
	local NrTestActual=''
	local LongTest=0
	local Alguno=''
	local Ninguno=''
	local Valor=''
	
	LongTest=${#Test}
	IFS=":" ; for ElementoActual in $Elementos ; do unset IFS
		if [ "$Valor" = "" ] ; then
			ElementoActual="$(expr "$ElementoActual" : "[ ]*\(.*[^ ]\)[ ]*$")"
			if [ "$ElementoActual" != "" ] ; then
				NrTestActual=2
				Alguno=''
				Todos="1"
				while [ $NrTestActual -le $LongTest ] ; do
					TestActual="-$(printf '%s' "$Test" | cut -c $NrTestActual)"
					if [ $TestActual "$ElementoActual" ] ; then
						Alguno="1"
					else
						Todos=''
					fi
					NrTestActual=$((NrTestActual + 1))
				done
#				if [ "$(printf '%s' "$Test" | cut -c 1)" = "+" ] ; then
				if [ "$(printf '%c' "$Test")" = "+" ] ; then
					# AND
					if [ "$Todos" != "" ] ; then
						Valor="$ElementoActual"
					fi
				else
					# OR
					if [ "$Alguno" != "" ] ; then
						Valor="$ElementoActual"
					fi
				fi
			fi
		fi
	done
	if [ "$Valor" != "" ] ; then printf '%s\n' "$Valor" ; fi
}

AddRunpartsToScript ()
# Sintaxis como instrucción: AddRunpartsToScript "$TheScript" "$BeginOrEnd" "$NewScriptsDirectory" "$Comment" $Parameters...
# Descripción:
#	Añade al programillo especificado una llamada run-parts para ejecutar lo que haya dentro de un directorio.
#	Útil para mejorar el procesado de eventos. Sólo realiza el añadido si no estaba ya.
# Parámetros esperados:
#	$1	Ruta al fichero-script a modificar (sólo se modifica en caso de no estarlo ya)
#	$2	"b" o "e" según si se quiere poner la llamada al inicio o al final del script/programillo
#	$3	Ruta de directorio a especificar a run-parts
#	$4	Comentario, incluyendo marca de comentario (#).
#	$5 en adelante: parámetros a pasar a las ejecuciones de run-parts.
#		Nota1: Si algún parámetro a pasar necesita entrecomillado, hay que añadirlo a la cadena.
#		Nota2: Es el run-parts de Debian el que admite pasar parámetros. Por ejemplo CentOS 6 sólo admite el directorio.
#		       Si la versión de run-parts no admite pasar parámetros, no se le pasaran.
#		       Una forma de detectarlo:
#		       RunPartsPasaArgs="$(run-parts --help 2>&1 | grep -e "--arg=")"
#		       LastStatus=$?
#		       if [ "$RunPartsPasaArgs" = "" ] || [ $LastStatus -ne 0 ] ; then echo "NO ADMITE" ; fi
# Depends on functions: WhereProgram
# Depends on software packages: (none)
{
	local TheScript="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local BeginOrEnd="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local NewScriptsDirectory="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Comment="$1"
	if [ $# -gt 0 ] ; then shift ; fi
#	local Parameters="$@"
#	local NrParametersRemaining=0
	local Head=''
	local Tail=''
	local HayParametros="0"
	local RunPartsPasaArgs=''
	local RunpartsLine=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -f "$TheScript" ] ; then
		touch "$TheScript"
		chmod ug+x "$TheScript"
	fi
	if [ "$(cat "$TheScript" | cut -f 1 -d '#' | tr -s '\t' ' ' | tr -s ' ' | grep -e "^run-parts .*${NewScriptsDirectory}" -e "^ run-parts .*${NewScriptsDirectory}")" = "" ] ; then
		if [ "$BeginOrEnd" = "b" ] || [ "$BeginOrEnd" = "B" ] ; then
			if [ "$(cat "$TheScript" | grep -e '^#!')" != "" ] ; then
				Head="$(cat "$TheScript" | grep --before-context=99999 -e '^#!')"
				Tail="$(cat "$TheScript" | grep --after-context=99999 -e '^#!' | grep -ve '^#!')"
			else
				Head=''
				Tail="$(cat "$TheScript")"
			fi
		else
			if [ "$(cat "$TheScript" | grep -e '^exit ')" != "" ] ; then
				Head="$(cat "$TheScript" | grep --before-context=99999 -e '^exit ' | grep -ve '^exit ')"
				Tail="$(cat "$TheScript" | grep --after-context=99999 -e '^exit ')"
			else
				Head="$(cat "$TheScript")"
				Tail=''
			fi
		fi
		if [ ! -e "${TheScript}.bak" ] ; then cp -a "$TheScript" "${TheScript}.bak" ; fi
		cat /dev/null > "$TheScript"
		if [ "$Head" != "" ] ; then
			printf '%s\n' "$Head" >> "$TheScript"
			printf '\n' >> "$TheScript"
		fi
		if [ "$Comment" != "" ] ; then
			printf '%s\n' "$Comment" >> "$TheScript"
		fi
		RunParts="$(WhereProgram run-parts)"
		if [ "$RunParts" = "" ] ; then RunParts="run-parts" ; fi
		RunpartsLine="$RunParts"
		RunPartsPasaArgs="$(run-parts --help 2>&1 | grep -e "--arg=")"
		LastStatus=$?
		if [ "$RunPartsPasaArgs" != "" ] && [ $LastStatus -eq 0 ] ; then
#			NrParametersRemaining=$#
			while [ $# -gt 0 ] ; do
				RunpartsLine="$RunpartsLine -a $1"
				shift
				HayParametros="1"
#				NrParametersRemaining=$#
			done
			if [ "$HayParametros" = "1" ] ; then
				RunpartsLine="$RunpartsLine --"
			fi
		fi
		RunpartsLine="$RunpartsLine \"$NewScriptsDirectory\""
		printf '%s\n' "$RunpartsLine" >> "$TheScript"
		if [ "$Tail" != "" ] ; then
			printf '\n' >> "$TheScript"
			printf '%s\n' "$Tail" >> "$TheScript"
		fi
		mkdir -p "$NewScriptsDirectory"
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

SystemLocale ()
# Returns (stdout) the default locale for users (ca_ES.UTF-8, es_ES.UTF-8, en_GB.UTF-8,..)
# It's searched in /etc/default/locale /etc/environment /etc/profile /etc/skel/.bashrc /etc/skel/.bash_login /etc/skel/.bash_profile /etc/skel/.profile
# Expected arguments:
#	$1	(optional) Root directory to search (eg. /mnt/subsystem2)
# Depends on functions: (none)
# Depends on software packages: grep, sed
{
	local RootDir="$1"
	local Value=''
	
	TestFile ()
	{
		local TheFile="$1"
		local ReadyValue="$2"
		if [ "$Value" = "" ] ; then
			if [ -f "$TheFile" ] ; then
				Value="$(cat "$TheFile" | grep -e "^LANG=")"
				if [ "$Value" = "" ] ; then
					Value="$(cat "$TheFile" | grep -e "^LANGUAGE=")"
				fi
			fi
		fi
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	}
	
	RootDir="$(printf '%s' "$RootDir" | sed -e 's|/$||g')"
	
	Value="$(TestFile "${RootDir}/etc/default/locale")"
	Value="$(TestFile "${RootDir}/etc/environment")"
	Value="$(TestFile "${RootDir}/etc/profile")"
	Value="$(TestFile "${RootDir}/etc/skel/.bashrc")"
	Value="$(TestFile "${RootDir}/etc/skel/.bash_login")"
	Value="$(TestFile "${RootDir}/etc/skel/.bash_profile")"
	Value="$(TestFile "${RootDir}/etc/profile /etc/skel/.profile")"
	   
	if [ "$Value" != "" ] ; then
		Value="$(printf '%s' "$Value" | cut -f 2 -d '=')"
		Value="$(printf '%s' "$Value" | sed -e "s|\"||g")"  # To remove quotes
		Value="$(printf '%s' "$Value" | sed -e "s|\'||g")"  # To remove quotes
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SystemLanguage ()
# Returns (stdout) the default language for users (ca, es, en,..)
# It's searched in /etc/default/locale /etc/environment /etc/profile /etc/skel/.bashrc /etc/skel/.bash_login /etc/skel/.bash_profile /etc/skel/.profile
# Expected arguments:
#	$1	(optional) Root directory to search (eg. /mnt/subsystem2)
# Depends on functions: SystemLocale
# Depends on software packages: (none)
{
	local RootDir="$1"
	local Value=''

	Value="$(SystemLocale "$RootDir")"
	if [ "$Value" != "" ] ; then
		Value="$(printf '%s' "$Value" | cut -f 1 -d '_')"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

UserLanguage ()
# Returns (stdout) the simplest language name abbreviation for current user in session (ca, es, en,..)
# Depends on functions: Is_Executable SystemLanguage
# Depends on software packages: grep
{
	Value=''
	if [ "$Value" = "" ] ; then
		Value="$LANG"
		if [ "$Value" = "" ] ; then
			Value="$LANGUAGE"
		fi
		Value="$(printf '%s' "$Value" | cut -f 2 -d '=')"
		Value="$(eval printf '%s' $Value)"  # To remove quotes
		Value="$(printf '%s' "$Value" | cut -f 1 -d '_')"
	fi
	if [ "$Value" = "" ] ; then
		if Is_Executable locale ; then
			Value="$(locale | grep -e "^LANG=")"
			if [ "$Value" = "" ] ; then
				Value="$(locale | grep -e "^LANGUAGE=")"
			fi
			Value="$(printf '%s' "$Value" | cut -f 2 -d '=')"
			Value="$(eval printf '%s' $Value)"  # To remove quotes
			Value="$(printf '%s' "$Value" | cut -f 1 -d '_')"
		fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(SystemLanguage)"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ListadoEncabezados ()
# Sintaxis como instrucción: ListadoEncabezados "$ListId" "$KeyType" $Column1Label $Column2Label $Column3Label $ColumnLabel...
# Descripción:
#	Almacena los encabezados para las columnas de datos para ListadoMostrar
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que crear los datos temporales. Se recomienda algo como Nombre.$$
#	$2	Tipo ordenación de la clave: "0" o "A" según si es numérico o alfanumérico respectivamente.
#	$3...	Etiqueta de encabezado. Algo como "_NOMBRE" implica alineación a la derecha.
# PENDIENTE: Poder especificar que se recorten columnas a una longitud maxima.
# Notes:
#	- Tab characters in data are replaced by spaces
# Depends on functions: (none)
# Depends on software packages: grep sed
{
	local ListId="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local KeyType="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DataPath=''
	local CurLabel=''
	local CurColumnNr=0
	local CurColumnLength=0
	local DataRow=''
	local CurColAlign=
	local ColsAligns=''
	local SomeHeader=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] || [ -f "${DataPath}/heading.txt" ] ; then
		printf '%s\n' "${sWARN}W: Removing previous list with identifier: ${ListId}${fRESET}" 1>&2
	fi
	rm -fr "$DataPath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	mkdir -p "$DataPath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	chmod u=rwX,g=rX,o= "$DataPath"
	printf '%s' "$KeyType" > "${DataPath}/key.typ"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	for CurLabel in "$@" ; do
		CurColumnNr=$((CurColumnNr + 1))
		CurLabel="$(printf '%s' "$CurLabel" | tr '\t' ' ')"
		CurColAlign="$(printf '%c' "$CurLabel")"
		if [ "$CurColAlign" = "_" ] ; then
			CurLabel="$(printf '%s' "$CurLabel" | cut -c 2-)"
		else
			CurColAlign='/'
		fi
		ColsAligns="$ColsAligns	$CurColAlign"
		DataRow="$DataRow	$CurLabel"
		if [ "$CurLabel" != "" ] ; then SomeHeader=1 ; fi
		CurColumnLength=${#CurLabel}
		printf '%3s\n' "$CurColumnLength" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	done
	printf '%s' "$ColsAligns" | cut -f 2- > "${DataPath}/aligns.tab"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $SomeHeader -eq 1 ] ; then
		printf '%s' "$DataRow" | cut -f 2- > "${DataPath}/heading.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		cat /dev/null > "${DataPath}/heading.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ListadoNuevoRegistro ()
# Sintaxis como instrucción: ListadoNuevoRegistro "$ListId" "$OrderKey" $Column1Data $Column2Data $Column3Data $Data...
# Descripción:
#	Almacena los datos especificados para ser mostrados por la función ListadoMostrar
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que se almacenan los datos temporales.
#	$2	Dato a usar para ordenar los registros alfabéticamente. Para no reordenar, especificar cadena en blanco ""
#	$3...	Campos a mostrar en el listado
# Notes:
#	- Tab characters in data are replaced by spaces
# Depends on functions: (none)
# Depends on software packages: sed
{
	local ListId="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local OrderKey="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DataPath=''
	local CurColumnData=''
	local CurColumnNr=0
	local ColumnsLengthsFiles=''
	local CurColumnLengthsFile=''
	local DataRow=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] && [ -f "${DataPath}/heading.txt" ] ; then
		KeyType="$(cat "${DataPath}/key.typ")"
		OrderKey="$(printf '%s' "$OrderKey" | tr '\t' ' ')"
		DataRow="$OrderKey"
		ColumnsLengthsFiles="$(ls -1 "${DataPath}"/*.len)"
		IFS="$(printf '\n\b')" ; for CurColumnLengthsFile in $ColumnsLengthsFiles ; do unset IFS
			CurColumnNr=$((CurColumnNr + 1))
			CurColumnData="$(printf '%s' "$1" | tr '\t' ' ')"
			DataRow="$DataRow	$CurColumnData"
			printf '%3s\n' "${#CurColumnData}" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
			if [ $# -gt 0 ] ; then shift ; fi
		done
		# This is for more data than headed:
		for CurColumnData in "$@" ; do
			CurColumnNr=$((CurColumnNr + 1))
			CurColumnData="$(printf '%s' "$CurColumnData" | tr '\t' ' ')"
			DataRow="$DataRow	$CurColumnData"
			printf '%4s\n' "${#CurColumnData}" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
		done
		printf '%s\n' "$DataRow" >> "${DataPath}/data.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		printf '%s\n' "${sERROR}E: List data not found with identifier: ${ListId}${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ListadoMostrar ()
# Sintaxis como instrucción: ListadoMostrar "$ListId" "$ColumnSeparator" "$SortOrientation"
# Descripción:
#	Muestra los registros preparados anteriormente en forma de listado de columnas rectas, a base
#	de la inserción de espacios.
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que hay los datos preparados.
#	$2	Cadena de separación AÑADIDA entre columnas, como por ejemplo un espacio " " o una línea "|"
#	$3	"a" para ordenar la clave en sentido 0-9 A-Z , o "r" para ordenarlas en sentido 9-0 Z-A
#	$4	(optional) It can contain letter keys:
#			"p" = Show list building progress (stderr)
# Notas:
#	- Una vez usada la función, se borran los datos temporales y se pierde la información del listado.
#	- TAB can not be used as ColumnSeparator
# Depends on functions: Is_Executable
# Depends on software packages: grep sed
# Recommends other software: column/bsdmainutils
{
	local ListId="$1"
	local ColumnSeparator="$2"
	local SortOrientation="$3"
	local MoreOptions="$4"
	local DataPath=''
	local KeyType=''
	local Heading=''
	local CurHeader=''
	local SortParms=''
	local SortedRecords=''
	local CurRecord=''
	local ColumnsNr=0
	local CurColumnData=''
	local FirstRecord=0
	local ColsLengths=''
	local CurColLength=0
	local HeaderSeparator=''
	local ColsAligns=''
	local CurLine=''
	local HeaderLine=''
	local ShowProgress=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] && [ -f "${DataPath}/heading.txt" ] ; then
		if [ "$(printf '%s' "$MoreOptions" | grep -ie 'p')" ] ; then ShowProgress=1 ; fi
		KeyType="$(cat "${DataPath}/key.typ")"
		ColsAligns="$(cat "${DataPath}/aligns.tab")"
		Heading="$(cat "${DataPath}/heading.txt")"
		HeaderSeparator="$(printf '%s' "$ColumnSeparator" | sed -e 's|.| |g')"
		FirstRecord=1
		ColumnsNr="$(ls -1 "${DataPath}"/*.len | wc -l)"
		if [ -f "${DataPath}/data.txt" ] ; then
			if [ "$KeyType" = "0" ] ; then
				SortParms="$SortParms -n"
			fi
			if [ "$SortOrientation" = "r" ] ||  [ "$SortOrientation" = "R" ] ; then
				SortParms="$SortParms -r"
			fi
			SortedRecords="$(cat "${DataPath}/data.txt" | sort $SortParms | cut -f 2-)"
			IFS="$(printf '\n\b')" ; for CurRecord in $SortedRecords ; do unset IFS
				if [ $ShowProgress -eq 1 ] ; then printf '.' 1>&2 ; fi
				CurColumnNr=0
				CurLine=''
				while [ $CurColumnNr -lt $ColumnsNr ]; do
					CurColumnNr=$((CurColumnNr + 1))
					CurColumnData="$(printf '%s' "$CurRecord" | cut -sf $CurColumnNr)"
					if [ $FirstRecord -eq 1 ]; then
						CurColLength="$(cat "${DataPath}/${CurColumnNr}.len" | sort | tail -n 1)"
						ColsLengths="$ColsLengths	$CurColLength"
					else
						CurColLength="$(printf '%s' "$ColsLengths" | cut -sf $CurColumnNr)"
					fi
					if [ $CurColumnNr -gt 1 ] ; then
						CurLine="$(printf '%s' "${CurLine}$(printf '\t')${ColumnSeparator}")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							HeaderLine="$(printf '%s' "${HeaderLine}$(printf '\t')${HeaderSeparator}")"
						fi
					fi
					if [ "$(printf '%s' "$ColsAligns" | cut -sf $CurColumnNr)" = "_" ] ; then
						CurLine="${CurLine}$(printf "%${CurColLength}s" "$CurColumnData")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							CurHeader="$(printf "%s" "$Heading" | cut -sf $CurColumnNr)"
							HeaderLine="${HeaderLine}$(printf "%${CurColLength}s" "$CurHeader")"
						fi
					else
						CurLine="${CurLine}$(printf "%-${CurColLength}s" "$CurColumnData")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							CurHeader="$(printf "%s" "$Heading" | cut -sf $CurColumnNr)"
							HeaderLine="${HeaderLine}$(printf "%-${CurColLength}s" "$CurHeader")"
						fi
					fi
				done
				if [ $FirstRecord -eq 1 ]; then
					ColsLengths="$(printf '%s' "$ColsLengths" | cut -sf 2-)"
					FirstRecord=0
					if [ "$Heading" != "" ]; then
						printf '%s\n' "$HeaderLine" >> "${DataPath}/columns.tab"
					fi
				fi
				printf '%s\n' "$CurLine" >> "${DataPath}/columns.tab"
			done
			if [ $ShowProgress -eq 1 ] ; then printf '\n' 1>&2 ; fi
		fi
		if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
			CurColumnNr=0
			CurLine=''
			while [ $CurColumnNr -lt $ColumnsNr ]; do
				CurColumnNr=$((CurColumnNr + 1))
				CurColumnData="$(printf '%s' "$Heading" | cut -sf $CurColumnNr)"
				if [ $CurColumnNr -gt 1 ] ; then CurLine="$(printf '%s' "${CurLine}$(printf '\t')${HeaderSeparator}")" ; fi
				CurLine="${CurLine}${CurColumnData}"
			done
			printf '%s\n' "$CurLine" >> "${DataPath}/columns.tab"
		fi
		if [ -f "${DataPath}/columns.tab" ] ; then
			if Is_Executable column; then
				cat "${DataPath}/columns.tab" | column -t -s "$(printf '\t')"
			else
				cat "${DataPath}/columns.tab" | sed -e 's|\t||g'
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: List data not found with identifier: ${ListId}${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	rm -fr "$DataPath"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

LineReplaced ()
# Syntax as a function: "$(LineReplaced "$OriginalText" $NrLineToEdit "$NewLineContent" "$FillLinesContent")"
# Description: Returns (stdout) specified text with one line replaced by specified new content.
# Expected parameters:
#	$1	Text to be edited
#	$2	Number of the line to be replaced
#	$3	Replacement for specified line
#	$4	Content for each line to be inserted when specified line is beyond text length+1. If empty, new lines will be inserted anyway.
# Notes:
#	- With NrLineToEdit=0 no line is replaced.
# Depends on functions: Is_IntegerNr
# Depends on software packages: gawk
{
	local OriginalText="$1"
	local NrLineToEdit="$2"
	local NewLineContent="$3"
	local FillLinesContent="$4"
	local OriginalLinesNr=0
	local CurLineNr=0
	
	if Is_IntegerNr "$NrLineToEdit" ; then
		if [ "$OriginalText" != "" ] ; then OriginalLinesNr=$(printf '%s\n' "$OriginalText" | wc -l) ; fi
		if [ $NrLineToEdit -ge 1 ] ; then
			if [ $NrLineToEdit -ge 2 ] && [ $OriginalLinesNr -ge 1 ] ; then
				printf '%s\n' "$OriginalText" | head -n $((NrLineToEdit - 1))
			fi
			if [ $NrLineToEdit -gt $((OriginalLinesNr + 1)) ] ; then
				# Original was too short; let's fill inserting FillLinesContent
				CurLineNr=$OriginalLinesNr
				while [ $CurLineNr -lt $((NrLineToEdit - 1)) ] ; do
					CurLineNr=$((CurLineNr + 1))
					printf '%s\n' "$FillLinesContent"
				done
			fi
			printf '%s\n' "$NewLineContent"
			if [ $OriginalLinesNr -gt $NrLineToEdit ] ; then
				printf '%s\n' "$OriginalText" | awk "NR >= $((NrLineToEdit + 1))"
			fi
		else
			if [ $OriginalLinesNr -ge 1 ] ; then
				printf '%s\n' "$OriginalText"
			fi
		fi
	fi
}

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"
}

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
}

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 IPv4FromDevice()
#	- 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 CurIP=''
	local Comprovacio=''
	local FoundNr=0
	local InetTrobada=0
	local PosTallar=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
				CurIP="$($IpRoute address show dev "$CurDevice")"
				if [ "$(printf '%s' "$CurIP" | grep -e 'inet ..*\...*\...*\...*/..* brd ')" != "" ] ; then
					CurIP="$(printf '%s' "$CurIP" | grep -e 'inet ..*\...*\...*\...*/..* brd ' | head -n 1 | tr -s '\t' ' ')"
				else
					CurIP="$(printf '%s' "$CurIP" | grep -e 'inet ..*\...*\...*\...*/..* ' | head -n 1 | tr -s '\t' ' ')"
				fi
				CurIP="$(echo TrimAndSingle $CurIP | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2 -d ' ' | cut -f 1 -d '/')"
				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
			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
			IFS="$(printf '\n\b')" ; for CurIP in $(env LANG=en $IfConfig $NIC | grep -e 'inet ' -e ' ip ' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ') ; 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
}

EsCIDR ()
# Sintaxis como función: $(EsCIDR "$DireccionRed")
# Descripción:
#	Devuelve (stdout) "1" en caso de que el dato proporcionado valga como especificación
#	de red CIDR, es decir, compuesta por una IP, una barra "/" y un número del 0 al 32
#	Ejemplo de validez: "192.168.1.0/24"
# Depends on functions: EsIP
# Depends on software packages: grep
{
	local DireccionRed="$1"
	local Mascara=''
	local Numeros="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32"
	local Valor=''
	
	if [ "$(printf '%s' "$DireccionRed" | grep -e "/")" != "" ] ; then
		if [ "$(EsIP "$(printf '%s' "$DireccionRed" | cut -f 1 -d '/')")" = "1" ] ; then
			Mascara="$(printf '%s' "$DireccionRed" | cut -f 2 -d '/')"
			if [ "$(printf '%s' ",${Numeros}," | grep -e ",${Mascara},")" != "" ] ; then
				Valor="1"
			fi
		fi
	fi
	if [ "$Valor" = "" ] ; then
		printf '%s\n' "0"
	else
		printf '%s\n' "1"
	fi
}

ListaDispositivosRed ()
# Sintaxis como funcion: $(ListaDispositivosRed)
# Descripcion: Devuelve (stdout) una lista de todos los dispositivos de red disponibles en el sistema, separados por espacios.
# Depends on functions:	WhereProgram
# Depends on software packages: grep, sed, iproute|net-tools
{
	local Value=''
	local IpRoute=''
	
	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 link 2>/dev/null | grep -ve '^ ' | grep -e '..*: ..*: ' | cut -f 2 -d ':')"
		Value="$($IpRoute link show 2>/dev/null | grep -ve '^ ' | cut -f 2 -d ':' | sed -e 's|.* ||g' | cut -f 1 -d '@')"
		if [ "$Value" = "" ] ; then
			Value="$(env LANG=en netstat --interfaces --all 2>/dev/null | grep -ive "^Iface" | grep -ive "^Kernel" | cut -f 1 -d ' ')"
		fi
		Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

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 ' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1
# 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
}

DispositivoDeIP ()
# Sintaxis como funcion: $(DispositivoDeIP $DireccionIP)
# Descripcion: Devuelve (stdout) el nombre de dispositivo que tiene asignada la dirección IP especificada
# Depends on functions: WhereProgram
# Depends on software packages: sed, iproute
{
	local DireccionIP="$1"
	local IpRoute=''
	local IpRegexp=''
	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
		IpRegexp="$(printf '%s\n' "$DireccionIP" | sed -e 's|\.|\\.|g')"
		Value="$($IpRoute address show | tr -s '\t' ' ' | grep -e " ${IpRegexp}/" | sed -e 's| |\n|g' | sed -e 's|@if..*||g' | tail -n 1)"
		if [ "$Value" = "" ] ; then
			Value="$($IpRoute address show | tr -s '\t' ' ' | grep -e " $IpRegexp " | sed -e 's| |\n|g' | sed -e 's|@if..*||g' | tail -n 1)"
		fi
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

IPCDIRFromDevice ()
# Sintaxis como funcion: $(IPCDIRFromDevice $NIC)
# Descripcion: Devuelve (stdout) todas las IPv4 asignadas al dispositivo especificado, una dirección por línea, en formato CDIR (192.168.1.33/24)
# Depends on functions: WhereProgram
# Depends on software packages: grep, sed, iproute
{
	local NIC="$1"
	local IpRoute=''
	local SelectedLines=''
	local CurLine=''
	local CurIP=''
	local CurMask=''
	
	if [ "$NIC" != "" ] ; then
		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 address show $NIC | grep -e 'inet ..*\...*\...*\...*/..* .')"
#			Value="$(printf '%s\n' "$Value" | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
#			if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
#			$IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ' | cut -f 2 -d ' '
			SelectedLines="$($IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ')"
			IFS="$(printf '\n\b')" ; for CurLine in $SelectedLines ; do unset IFS
				CurIP="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\..' | cut -f 1 -d '/' | head -n 1)"
				# Debian 7 in Netwerkvereniging Coloclue returns IP with no mask but peer with mask at same line.
				CurMask="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | head -n 1 | cut -f 2 -d '/')"
				if [ "$CurIP" != "" ] ; then
					if [ "$CurMask" != "" ] ; then
						printf '%s\n' "${CurIP}/${CurMask}"
					else
						printf '%s\n' "$CurIP"
					fi
				fi
			done
		fi
	fi
}

IPv4FromDevice ()
# Sintaxis como funcion: $(IPv4FromDevice $NIC)
# Descripcion: Devuelve (stdout) todas las IPv4 asignadas al dispositivo especificado, una dirección por línea
# Depends on functions: IPCDIRFromDevice
# Depends on software packages: (none)
{
#	local NIC="$1"
#	local IpRoute=''
#	
#	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 address show $NIC 2>/dev/null | grep -e 'inet ..*\...*\...*\...*/..* .')"
#		Value="$(printf '%s\n' "$Value" | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ' | cut -f 1 -d '/')"
#		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
#	fi
	IPCDIRFromDevice "$@" | cut -f 1 -d '/'
}

IpDeDispositivo ()
# Sintaxis como funcion: $(IpDeDispositivo $NIC)
# Descripcion: Devuelve (stdout) una IP que tiene asignado el dispositivo especificada, lo más probable es que sea la predeterminada.
# Notes:
#	- Use IPv4FromDevice() or LlistarIPpropies() to get all IPs
# Depends on functions: IPv4FromDevice
# Depends on software packages: (none)
{
#	local NIC="$1"
#	local IpRoute=''
#	
#	if [ "$NIC" != "" ] ; then
#		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 address show $NIC | grep -e 'inet .* brd ' | head -n 1)"
#			Value="$($IpRoute address show $NIC | grep -e "inet .* $NIC" | head -n 1 | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
##			Value=$(SomeWord () { printf '%s' $2; }; SomeWord $Value)
#			Value="$(printf '%s\n' "$Value" | cut -f 1 -d '/')"
#			if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
#		fi
#	fi
	IPv4FromDevice "$@" | head -n 1
}

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:
#	ip r l | grep -ie '^default ' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1
# 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
}

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
}

Is_PrivateIPv4 ()
# Syntax as a function: Is_PrivateIPv4 $Address
# Description: Returns (exitcode 0) TRUE if specified address is assigned by IANA as in class A/B/C
# https://en.wikipedia.org/wiki/Private_network
# Use example (without brackets []):
#	if Is_PrivateIPv4 192.168.1.33 ; then echo "This is LAN" ; fi
# Notes:
#	Class A: 10.0.0.0/8
#	Class B: 172.16.0.0/12
#	Class C: 192.168.0.0/16
#	Link-local: 169.254.0.0/16
#	- carrier-grade NAT (100.64.0.0/10) is not be used on private networks or on the public Internet -> FALSE
#	- Way to detect 100.64.0.0/10 :
#	  ! Is_PublicIPv4 && ! Is_PrivateIPv4
# Depends on functions: EsIP
# Depends on software packages: grep
{
	local Address="$1"
	local AnalyzedPart=''
	local TrueCode=254 # 254=FALSE
	
	if [ "$(EsIP "$Address")" = "1" ] ; then
		AnalyzedPart="$(printf '%s' "$Address" | cut -f 1 -d '.')"
		if [ "$AnalyzedPart" = "172" ] ; then
			AnalyzedPart="$(printf '%s' "$Address" | cut -sf 2 -d '.')"
			if [ $AnalyzedPart -ge 16 ] && [ $AnalyzedPart -le 31 ] ; then AnalyzedPart='172.16' ; fi
		fi
		if [ "$(printf '%s' "$Address" | grep -e '^10\.' -e '^192\.168\.' -e '^169\.254\.' -e '^127\.')" ] || [ "$AnalyzedPart" = "172.16" ] ; then
			TrueCode=0
		fi
	fi
	return $TrueCode
}

Is_PublicIPv4 ()
# Syntax as a function: Is_PublicIPv4 $Address
# Description: Returns (exitcode 0) TRUE if specified address can be assigned by an ISP for Internet host
# https://en.wikipedia.org/wiki/Private_network
# Use example (without brackets []):
#	if Is_PrivateIPv4 1.2.3.4 ; then echo "This is an Internet address" ; fi
# Notes:
#	- carrier-grade NAT (100.64.0.0/10) is not be used on private networks or on the public Internet -> FALSE
#	- Way to detect 100.64.0.0/10 :
#	  ! Is_PublicIPv4 && ! Is_PrivateIPv4
# Depends on functions: EsIP Is_PrivateIPv4
# Depends on software packages: grep
{
	local Address="$1"
	local AnalyzedPart=''
	local TrueCode=254 # 254=FALSE
	
	if [ "$(EsIP "$Address")" = "1" ] ; then
		if ! Is_PrivateIPv4 "$Address" ; then
			TrueCode=0
			AnalyzedPart="$(printf '%s' "$Address" | cut -f 1 -d '.')"
			if [ "$AnalyzedPart" = "100" ] ; then
				# carrier-grade NAT?
				AnalyzedPart="$(printf '%s' "$Address" | cut -sf 2 -d '.')"
				if [ $AnalyzedPart -ge 64 ] && [ $AnalyzedPart -le 127 ] ; then
					TrueCode=254
				fi
			fi
		fi
	fi
	return $TrueCode
}

HttpGetContent ()
# Syntax as function: $(HttpGetContent "$Url" [DNS] [BindAddress] [TimeoutS])
# 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 or empty) 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.
#	$4	(optional or empty) Seconds timeout. Default is 15.
#	$5	(optional) Tries number. Default is 2.

# Notes:
#	- Logging and error messages are returned to stderr
# Depends on functions: Is_Executable Is_IntegerNr
# Depends on software packages: wget|curl
{
	local Url="$1"
	local OverrideDNS="$2"
	local BindAddress="$3"
	local TimeoutS="$4"
	local TriesNr="$5"
	local StatusCode=0
	local WgetBind=''
	local CurlBind=''
	local RetriesNr=0
	
	if [ "$BindAddress" != "" ] ; then
		WgetBind="--bind-address=$BindAddress"
		CurlBind="--interface $BindAddress"
	fi
	if ! Is_IntegerNr $TimeoutS ; then TimeoutS=15 ; fi
	if ! Is_IntegerNr $TriesNr ; then TriesNr=2 ; fi
	if [ $TriesNr -le 0 ] ; then TriesNr=1 ; fi
	RetriesNr=$((TriesNr - 1))
	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 $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate "--dns-servers=${OverrideDNS}" -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind --no-check-certificate -T $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
						wget $WgetBind --no-check-certificate -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget --no-check-certificate -T $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
						StatusCode=$?
					fi
				fi
			fi
		else
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget $WgetBind \"--dns-servers=${OverrideDNS}\" -T $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
				wget $WgetBind "--dns-servers=${OverrideDNS}" -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind -T $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
						wget $WgetBind -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget $WgetBind -T $TimeoutS -t $TriesNr -O - \"$Url\"" 1>&2
				wget $WgetBind -T $TimeoutS -t $TriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout $TimeoutS --retry $RetriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
				curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout $TimeoutS --retry $RetriesNr -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 $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
					curl $CurlBind --connect-timeout $TimeoutS --retry $RetriesNr -k "$Url"
					StatusCode=$?
				fi
			else
				printf '%s\n' "\$ curl $CurlBind --connect-timeout $TimeoutS --retry $RetriesNr -k \"$Url\"" 1>&2
				curl $CurlBind --connect-timeout $TimeoutS --retry $RetriesNr -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
}

PublicIP ()
# Syntax as function: $(PublicIP [BindAddress] [TimeoutS])
# Description: Returns (stdout) the public IP address in internet, querying it to well-known websites.
# Expected parameters:
#	$1	(optional or empty) 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.
#	$2	(optional) Seconds timeout for each try. There can be as many tries as servers configured.
# Notes:
#	- If cannot get the address or isn't valid, doesn't return anything.
# Depends on functions: EsIP HttpGetContent
# Depends on software packages: grep, sed
{
	local BindAddress="$1"
	local TimeoutS="$2"
	local Server1="http://www.formyip.com/"
	local Label1="Your IP is "
	local Server2="http://checkip.dyndns.org/"
	local Label2="IP Address: "
	local Server3="https://grn.cat/ip/"  # GRN
	local Label3=""
	local Server4="https://ifconfig.me/ip/"  # Google
	local Label4=""
	local LabelWords=0
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server1 '' "$BindAddress" "$TimeoutS" 1 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label1")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label1" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label1/$Label1/g" | grep -ie "$Label1" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label1)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server2 '' "$BindAddress" "$TimeoutS" 1 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label2")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label2" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label2/$Label2/g" | grep -ie "$Label2" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label2)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server3 '' "$BindAddress" "$TimeoutS" 1 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label3")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label3" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label3/$Label3/g" | grep -ie "$Label3" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label3)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server4 '' "$BindAddress" "$TimeoutS" 1 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label4")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label4" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label4/$Label4/g" | grep -ie "$Label4" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label4)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
		StatusCode=0
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

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	
# Notes:
#	- If does not resolve, returns empty "" string
# TO DO:
#	- Query opennic.org to get mode 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
	PossibleServers="$Proposals $PossibleServers"
	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
}

#[/durruter]


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

#[durruter]
EjecutarLinea ()
# Descripción: Crea un fichero ejecutable con la cadena suministrada para ser ejecutada externamente.
{
	local LaLinea="$1"
	local EjecutableTemp=""
	local LastStatus=0
	local StatusCode=0
	
	EjecutableTemp="$(mktemp)"
	printf '%s\n' "#!/bin/sh" >> "$EjecutableTemp"
	printf '%s\n' "$LaLinea" >> "$EjecutableTemp"
	printf '%s\n' "exit \$?" >> "$EjecutableTemp"
	chmod u+x "$EjecutableTemp"
	"$EjecutableTemp"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm "$EjecutableTemp"
	return $StatusCode
}

NewIdForEntry ()
# Genera, guarda y devuelve (echo) el siguiente identificador para una directiva nat/dnat/shaping
{
	local LastValue=""
	local Value=""
	
	LastValue="$(cat "${SystemConfigDir}/last-id.txt" 2>/dev/null)"
	if ! Is_IntegerNr $LastValue ; then
		LastValue=0
		printf '%s\n' "$LastValue" > "${SystemConfigDir}/last-id.txt"
	fi
	Value=$(($LastValue + 1))
	printf '%s\n' "$Value" > "${SystemConfigDir}/last-id.txt"
	printf '%s\n' "$Value"
}

RebuildNatLoaders ()
# Compone de nuevo los ficheros if-up_fast.sh if-down_fast.sh para la cadena de directorios
# afectados por una salida NAT
# Ejemplo: RebuildNatLoaders eth0 192.168.1.0
{
	local Selected_OutDev="$1"
	local NetIp="$2"
	local WanIp=""
	local LanMask=""
	local Dir_Id=""
	local Comment=""
	local Dev_Parameter=""
	local RedlocalActual=""
	local WanIpParametro=""
	local CurScript=""
	local FilePathsList=""
	local Dir_Dev=""
	local NetipActual=""
	local CurDirLine=''
	local Dir_ToRebuild=""
	local CurDev=''
	local CurNet=''
	local CurService=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Selected_OutDev" = "." ] ; then Selected_OutDev='' ; fi
	if [ "$NetIp" = "." ] ; then NetIp='' ; fi
	if [ ! -f "${SystemConfigDir}/${Selected_OutDev}/nat/${NetIp}/lan-mask.txt" ] ; then
		# Puede venir de una eliminacion; limpiamos directorios precedentes sin contenido.
		if [ "$Selected_OutDev" = "" ] ; then
			FilePathsList="$(find "${SystemConfigDir}/"*"/nat/"*"/lan-mask.txt" 2>/dev/null)"
			if [ "$FilePathsList" = "" ] ; then
				rm -fr "${SystemConfigDir}/"*"/nat"
				FilePathsList="$(find "${SystemConfigDir}/"*"/"*"/" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/"*"/"
				fi
			fi
		else
			FilePathsList="$(find "${SystemConfigDir}/${Selected_OutDev}/nat/"*"/lan-mask.txt" 2>/dev/null)"
			if [ "$FilePathsList" = "" ] ; then
				rm -fr "${SystemConfigDir}/${Selected_OutDev}/nat"
				FilePathsList="$(find "${SystemConfigDir}/${Selected_OutDev}/"*"/" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/${Selected_OutDev}"
				fi
			fi
		fi
	fi
	FilePathsList="$(find "${SystemConfigDir}/"*"/nat/"*"/lan-mask.txt" 2>/dev/null)"
	IFS="$(printf "\n\b")" ; for CurDirLine in $FilePathsList ; do unset IFS
		Dir_ToRebuild="1"
		Dir_Dev="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 4 | head -n 1)"
#		if [ "$Dir_Dev" = "." ] || [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Dir_Dev ")" != "" ] ; then
		if [ "$Dir_Dev" = "_any" ] || [ "$Dir_Dev" = "*" ] ; then
			Dir_Dev='_any'
			Dev_Parameter=''
		else
			if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Dir_Dev ")" != "" ] ; then
				Dir_Dev='to-wan'
				Dev_Parameter='$1'
			else
				Dev_Parameter="$Dir_Dev"
			fi
		fi
		NetipActual="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 2 | head -n 1)"
		if [ "$Dir_Dev" != "$Selected_OutDev" ] && [ "$Selected_OutDev" != "" ] ; then
			Dir_ToRebuild="0"
		fi
		if [ "$NetipActual" != "$NetIp" ] && [ "$NetIp" != "" ] ; then
			Dir_ToRebuild="0"
		fi
		if [ "$Dir_ToRebuild" = "1" ] ; then
			LanMask="$(cat "${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/lan-mask.txt" 2>/dev/null)"
			if Is_IntegerNr $LanMask ; then
				LanMask=$(OneWord () { printf '%s' $1; }; OneWord $LanMask)
			else
				if [ "$(EsIP "$LanMask")" = "1" ] ; then
					LanMask="$(BitsMascara "$LanMask")"
				else
					LanMask=32
				fi
			fi
			RedlocalActual="${NetipActual}/${LanMask}"
			WanIp="$(cat "${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/wan-ip.txt" 2>/dev/null)"
			if [ "$(EsIP "$WanIp")" != "1" ] ; then
				WanIpParametro='$NicIp'
			else
				WanIpParametro="$WanIp"
			fi
			Dir_Id="$(cat "${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/id.txt" 2>/dev/null)"
			if ! Is_IntegerNr $Dir_Id ; then
				Dir_Id=$(NewIdForEntry)
				printf '%s\n' "$Dir_Id" > "${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/id.txt"
			fi
			Comment="$(cat "${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/description.txt" 2>/dev/null)"
			Comment="$(printf '%s\n' "$Comment" | sed -e "s/\"/\'/g")"
			# Cargador de esta salida
			CurScript="${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName rule for NAT output: Subnet $RedlocalActual to device $Dir_Dev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Output IP" >> "$CurScript"
			printf '%s\n' 'NIC="$1"' >> "$CurScript"
			printf '%s\n' 'NicIp="$2"' >> "$CurScript"
			printf '%s\n' 'if [ "$NIC" != "" ] && [ "$NicIp" = "" ] ; then' >> "$CurScript"
			printf '%s\n' '	NicIp="$(ip address show "$NIC" | grep -e "inet " | sed -e "s|.*inet ||" | cut -f 1 -d "/")"' >> "$CurScript"
			printf '%s\n' 'fi' >> "$CurScript"
#			printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "E: device name and WAN IP were expected." 1>&2 ; exit 79 ; fi' >> "$CurScript"
#			if [ "$IptablesMetadata" = "1" ] ; then
#				printf '%s\n' "$ITcmd --table nat --append POSTROUTING --source $RedlocalActual --out-interface $Dev_Parameter --jump SNAT --to-source $WanIpParametro -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table nat --append POSTROUTING --out-interface $Dev_Parameter --jump SNAT --to-source $WanIpParametro -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table filter --append FORWARD --source $RedlocalActual --jump ACCEPT -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table filter --append FORWARD --destination $RedlocalActual --jump ACCEPT -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" >> "$CurScript"
#			else
#				printf '%s\n' "$ITcmd --table nat --append POSTROUTING --source $RedlocalActual --out-interface $Dev_Parameter --jump SNAT --to-source $WanIpParametro" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table nat --append POSTROUTING --out-interface $Dev_Parameter --jump SNAT --to-source $WanIpParametro" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table filter --append FORWARD --source $RedlocalActual --jump ACCEPT" >> "$CurScript"
#				printf '%s\n' "$ITcmd --table filter --append FORWARD --destination $RedlocalActual --jump ACCEPT" >> "$CurScript"
#			fi
			CurCommand="$ITcmd --table nat --append POSTROUTING"
			if [ "$RedlocalActual" != "" ] && [ "$RedlocalActual" != "*" ] && [ "$RedlocalActual" != "_any" ] ; then CurCommand="$CurCommand --source $RedlocalActual" ; fi
			if [ "$Dev_Parameter" != "" ] && [ "$Dev_Parameter" != "*" ] && [ "$Dev_Parameter" != "_any" ] ; then CurCommand="$CurCommand --out-interface $Dev_Parameter" ; fi
			CurCommand="$CurCommand --jump SNAT"
#			if [ "$WanIpParametro" != "" ] && [ "$WanIpParametro" != "*" ] && [ "$WanIpParametro" != "_any" ] && [ "$WanIpParametro" != "default" ] ; then CurCommand="$CurCommand --to $WanIpParametro" ; fi
			if [ "$WanIpParametro" != "" ] && [ "$WanIpParametro" != "*" ] && [ "$WanIpParametro" != "_any" ] && [ "$WanIpParametro" != "default" ] ; then
				printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sERROR}E: device name and WAN IP were expected.${fRESET}" 1>&2 ; exit 0 ; fi' >> "$CurScript"
				CurCommand="$CurCommand --to-source $WanIpParametro"
			else
				printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sWARN}W: device name and WAN IP were expected.${fRESET}" 1>&2 ; fi' >> "$CurScript"
			fi
			if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" ; fi
			printf '%s\n' "$CurCommand" >> "$CurScript"
			
			CurCommand="$ITcmd --table filter --append FORWARD"
			if [ "$RedlocalActual" != "" ] && [ "$RedlocalActual" != "*" ] && [ "$RedlocalActual" != "_any" ] ; then CurCommand="$CurCommand --source $RedlocalActual" ; fi
			CurCommand="$CurCommand --jump ACCEPT"
			if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" ; fi
			printf '%s\n' "$CurCommand" >> "$CurScript"

			if [ "$RedlocalActual" != "" ] && [ "$RedlocalActual" != "*" ] && [ "$RedlocalActual" != "_any" ] ; then
				CurCommand="$ITcmd --table filter --append FORWARD"
				CurCommand="$CurCommand --destination $RedlocalActual"
				CurCommand="$CurCommand --jump ACCEPT"
				if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Dir_Id}/${Comment}\"" ; fi
				printf '%s\n' "$CurCommand" >> "$CurScript"
			fi

			printf '%s\n' 'sysctl -w net/ipv4/ip_forward=1 > /dev/null' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Descargador de esta salida
			CurScript="${SystemConfigDir}/${Dir_Dev}/nat/${NetipActual}/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName rule for NAT output: Subnet $RedlocalActual to device $Dir_Dev" >> "$CurScript"
			printf '%s\n' "NewContent=\"\$($ITScmd | grep -ive 'durruter-nat/${Dir_Id}/')\"" >> "$CurScript"
			printf '%s\n' "echo \"\$NewContent\" | $ITRcmd" >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Cargador del servicio nat para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_Dev}/nat/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName rules for NAT outputs: Every subnet to device $Dir_Dev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Output IP" >> "$CurScript"
			printf '%s\n' "" >> "$CurScript"
			# Esto soluciona el problema de que la interfaz predeterminada va cambiando para cada evento if-up.
			printf '%s\n' "# Previously unloading same rules, to foresee repeated call on device events:" >> "$CurScript"
			IFS="$(printf "\n\b")" ; for CurNet in $(ls -1 "${SystemConfigDir}/${Dir_Dev}/nat"/) ; do unset IFS
				printf '%s\n' "" >> "$CurScript"
				cat "${SystemConfigDir}/${Dir_Dev}/nat/${CurNet}/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			done
			printf '%s\n' "" >> "$CurScript"
			printf '%s\n' "# Now comes the loading:" >> "$CurScript"
			IFS="$(printf "\n\b")" ; for CurNet in $(ls -1 "${SystemConfigDir}/${Dir_Dev}/nat"/) ; do unset IFS
				printf '%s\n' "" >> "$CurScript"
				cat "${SystemConfigDir}/${Dir_Dev}/nat/${CurNet}/if-up_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			done
			chmod ug+rx "$CurScript"
			# Descargador del servicio nat para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_Dev}/nat/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName rules for NAT outputs: Every subnet to device $Dir_Dev" >> "$CurScript"
			IFS="$(printf "\n\b")" ; for CurNet in $(ls -1 "${SystemConfigDir}/${Dir_Dev}/nat"/) ; do unset IFS
				printf '%s\n' "" >> "$CurScript"
				cat "${SystemConfigDir}/${Dir_Dev}/nat/${CurNet}/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			done
			chmod ug+rx "$CurScript"
			# Cargador de todos los servicios para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_Dev}/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName services for device $Dir_Dev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Output IP" >> "$CurScript"
			IFS="$(printf "\n\b")" ; for CurService in $(ls -1 "${SystemConfigDir}/${Dir_Dev}"/) ; do unset IFS
				printf '%s\n' "" >> "$CurScript"
				cat "${SystemConfigDir}/${Dir_Dev}/${CurService}/if-up_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			done
			chmod ug+rx "$CurScript"
			# Descargador de todos los servicios para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_Dev}/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName services for device $Dir_Dev" >> "$CurScript"
			IFS="$(printf "\n\b")" ; for CurService in $(ls -1 "${SystemConfigDir}/${Dir_Dev}"/) ; do unset IFS
				printf '%s\n' "" >> "$CurScript"
				cat "${SystemConfigDir}/${Dir_Dev}/"*"/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			done
			chmod ug+rx "$CurScript"
		fi
	done
	return $StatusCode
}

DnatDirCaseValues ()
# Syntax as a sentence: DnatDirCaseValues $NrCase "$Dir_RDev" "$Dir_Protocol" "$Dir_RPort1" "$Dir_RPorts" "$Dir_InputIp" "$Dir_DestIp" "$Dir_DestPorts" "$Dir_Comment" "$Dir_Id"
# Description: sets (global variables) case effect values from selected row in all Dir_ variables. Fixes some inconsistency, as about port numbers.
# Expected parameters:
#	$1	Number of line to get in list-formatted parameters.
#		(Each line~case is intended for each IP on same reception device)
#	$2	Common reception device name; no spaces expected.
#	$3	Common protocol name; no spaces expected.
#	$4	Reception port1 (first for a range). This data is only used in case of lack or bad-formed others (RPorts/DestPorts)
#	$5	List of reception ports/ranges: One line per case. In case of lack or bad-formed, Dir_RPort1 is used.
#	$6	List of reception device IPs: One line per case
#	$7	List of destination IPs: One line per case
#	$8	List of destination ports: One line per case. In case of lack or bad-formed, reception port/range case is used.
#	$9	List of comments: One line per case
#	$10	List of internal identifiers: One line per case
# Variables set by this function:
#	Case_Id                 String
#	Case_RDev               Same as Dir_RDev
#	Case_RDev_Current       Express NIC for effect, or, if to be "to-wan" one, current RDev.
#	Case_Protocol           Same as Dir_Protocol
#	Case_RPort1             Integer number
#	Case_RPort9             Integer number
#	Case_RPorts             Number or range compatible with iptables (ranges as start:end)
#	Case_RPorts_Show        Number or range human-readable
#	Case_RPorts_Portable    Number or range compatible with export/import actions
#	Case_InputIp            Address or "default" if meant to be RDev's
#	Case_InputIp_Current    Express address for effect, or, if to be default one, current RDev's address.
#	Case_DestIp             Address
#	Case_DestPort1          Integer number
#	Case_DestPort9          Integer number
#	Case_DestPorts          Number or range compatible with iptables (ranges as start-end)
#	Case_DestPorts_Show     Number or range human-readable. Empty if it matches Case_RPorts_Show.
#	Case_DestPorts_Portable Number or range compatible with export/import actions. A dot "." or equal "=" if it matches Case_RPorts_Portable.
#	Case_Comment            String
#	Case_Comment_Export     Double-quoted Case_Comment
{
	local NrCase="$1"
	local Dir_RDev="$2"
	local Dir_Protocol="$3"
	local Dir_RPort1="$4"
	local Dir_RPorts="$5"
	local Dir_InputIp="$6"
	local Dir_DestIp="$7"
	local Dir_DestPorts="$8"
	local Dir_Comment="$9"
	local Dir_Id="${10}"

	Case_Id="$(printf '%s\n' "$Dir_Id" | awk "NR >= $NrCase" | head -n 1)"
	
	Case_RDev="$(echo aaa$Dir_RDev | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Case_RDev" = "_any" ] || [ "$Case_RDev" = "any" ] || [ "$Case_RDev" = "*" ] ; then Case_RDev='*' ; fi
	if [ "$Case_RDev" = "." ] ; then
		Case_RDev=''
		Case_RDev_Current=''
	fi
	if [ "$Case_RDev" = "*" ] ; then
		Case_RDev_Current='*'
		Case_RDev_Portable="'*'"
	else
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Case_RDev ")" != "" ] ; then
			Case_RDev="to-wan"
		fi
		if [ "$Case_RDev" = "to-wan" ] ; then
			Case_RDev_Current="$(DefaultNICs | head -n 1)"
		else
			Case_RDev_Current="$Case_RDev"
		fi
		Case_RDev_Portable="$Case_RDev"
	fi
	
	Case_Protocol="$(echo aaa$Dir_Protocol | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$Case_Protocol" = "_any" ] || [ "$Case_Protocol" = "any" ] || [ "$Case_Protocol" = "*" ] ; then Case_Protocol='*' ; fi
	if [ "$Case_Protocol" = "*" ] ; then
		Case_Protocol_Portable="'*'"
	else
		Case_Protocol_Portable="$Case_Protocol"
	fi

	Case_RPorts="$(printf '%s\n' "$Dir_RPorts" | awk "NR >= $NrCase" | head -n 1)"
	if [ "$Case_RPorts" = "_any" ] || [ "$Case_RPorts" = "any" ] || [ "$Case_RPorts" = "*" ] ; then
		Case_RPorts='*'
		Case_RPort1='*'
		Case_RPort9='*'
		Case_RPorts_Show='*'
		Case_RPorts_Portable="'*'"
	else
		Case_RPorts="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-')"
		Case_RPort1="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
		if ! Is_IntegerNr $Case_RPort1 && Is_IntegerNr $Dir_RPort1 ; then
			Case_RPort1="$(echo aaa$Dir_RPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			Case_RPort9=$Case_RPort1	# Dir_RPort1 could not be range-consistent with Case_RPort9
		else
			Case_RPort1="$(echo aaa$Case_RPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			Case_RPort9="$(printf '%s\n' "$Case_RPorts" | cut -sf 2 -d '-')"
			Case_RPort9="$(echo aaa$Case_RPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			if ! Is_IntegerNr $Case_RPort9 && Is_IntegerNr $Case_RPort1 ; then
				Case_RPort9=$Case_RPort1
			fi
		fi
		if [ "$Case_RPort1" = "$Case_RPort9" ] ; then
			# Briefed; accepted by iptables too.
			Case_RPorts=$Case_RPort1
			Case_RPorts_Show=$Case_RPort1
			Case_RPorts_Portable=$Case_RPort1
		else
			Case_RPorts="${Case_RPort1}:${Case_RPort9}"
			Case_RPorts_Show="$(printf '%s\n' "$Case_RPorts" | tr -s ':' "-")"
			Case_RPorts_Portable="$Case_RPorts"
		fi
	fi
	
	Case_InputIp="$(printf '%s\n' "$Dir_InputIp" | awk "NR >= $NrCase" | head -n 1)"
	if [ "$Case_InputIp" = "_any" ] || [ "$Case_InputIp" = "any" ] || [ "$Case_InputIp" = "*" ] ; then Case_InputIp='*' ; fi
	if [ "$Case_InputIp" = "." ] ; then Case_InputIp='default' ; fi
	if [ "$Case_InputIp" = "*" ] ; then
		Case_InputIp_Current='*'
		Case_InputIp_Portable="'*'"
	else
		if [ "$(EsIP $Case_InputIp)" = "1" ] ; then
			Case_InputIp_Current="$(echo aaa$Case_InputIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
		else
#			if [ "$Case_InputIp" = "" ] || [ "$(printf '%s\n' " all any todo toda todos todas " | grep -ie " $Case_InputIp ")" != "" ] ; then
			if [ "$Case_InputIp" = "" ] ; then
				Case_InputIp="*"
			fi
			if [ "$Case_InputIp" = "*" ] ; then
				Case_InputIp_Current='*'
			else
				if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Case_InputIp ")" != "" ] ; then
					Case_InputIp="default"
				fi
				if [ "$Case_RDev_Current" = "*" ] ; then
					Case_InputIp_Current='*'
				else
					Case_InputIp_Current="$(IpDeDispositivo "$Case_RDev_Current")"
				fi
			fi
		fi
		Case_InputIp_Portable="$Case_InputIp"
	fi
	
	Case_DestIp="$(printf '%s\n' "$Dir_DestIp" | awk "NR >= $NrCase" | head -n 1)"
	
	Case_DestPorts="$(printf '%s\n' "$Dir_DestPorts" | awk "NR >= $NrCase" | head -n 1)"
	if [ "$Case_DestPorts" = "_any" ] || [ "$Case_DestPorts" = "any" ] || [ "$Case_DestPorts" = "*" ] ; then
		Case_DestPorts='*'
		Case_DestPort1='*'
		Case_DestPort9='*'
		Case_DestPorts_Show=''
		Case_DestPorts_Portable="'*'"
	else
		if [ "$Case_DestPorts" = "=" ] || [ "$Case_DestPorts" = "." ] ; then
			Case_DestPorts='='
			Case_DestPort1='='
			Case_DestPort9='='
			Case_DestPorts_Show=''
			Case_DestPorts_Portable="="
		else
			Case_DestPorts="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-')"
			Case_DestPort1="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
			if ! Is_IntegerNr $Case_DestPort1 && Is_IntegerNr $Case_RPort1 ; then
				Case_DestPort1=$Case_RPort1
				Case_DestPort9=$Case_RPort9	# Case_RPort1 could not be range-consistent with the case of Dir_DestPorts
			else
				Case_DestPort1="$(echo aaa$Case_DestPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
				Case_DestPort9="$(printf '%s\n' "$Case_DestPorts" | cut -sf 2 -d '-')"
				Case_DestPort9="$(echo aaa$Case_DestPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
				if ! Is_IntegerNr $Case_DestPort9 && Is_IntegerNr $Case_DestPort1 ; then
					Case_DestPort9=$Case_DestPort1
				fi
			fi
			if [ "$Case_DestPort1" = "$Case_DestPort9" ] ; then
				# Briefed; accepted by iptables too.
				Case_DestPorts=$Case_DestPort1
				Case_DestPorts_Show=$Case_DestPort1
				Case_DestPorts_Portable=$Case_DestPort1
			else
				Case_DestPorts="${Case_DestPort1}-${Case_DestPort9}"
				Case_DestPorts_Show="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' "-")"
				Case_DestPorts_Portable="$Case_DestPorts"
			fi
			if [ "$Case_DestPorts_Show" = "$Case_RPorts_Show" ] ; then Case_DestPorts_Show="" ; fi
			if [ "$(printf "$Case_DestPorts_Portable" | tr -s '-' ':')" = "$(printf "$Case_RPorts_Portable" | tr -s '-' ':')" ] ; then Case_DestPorts_Portable="=" ; fi
		fi
	fi

	Case_Comment="$(printf '%s\n' "$Dir_Comment" | awk "NR >= $NrCase" | head -n 1)"
#	Case_Comment_Export="\"${Case_Comment}\""
}

DnatIptablesLineValues ()
# Syntax as a sentence: DnatIptablesLineValues "$TableLine"
# Description: sets (global variables) values from provided iptables line, which is supposed to be a DNAT entry.
# Expected parameters:
#	$1	Already selected line from iptables-save
# Variables set by this function: Same as DnatDirCaseValues()
{
	local TableLine="$1"

	LinesParameters="$(printf '%s\n' "$TableLine" | sed -e 's| -|\n-|g')"
	
	Case_Comment="$(printf '%s\n' "$LinesParameters" | grep -e '^--comment ')"
	Case_Comment="$(printf '%s\n' "$Case_Comment" | cut -f 2- -d ' ')"
	Case_Comment="$(printf '%s\n' "$Case_Comment" | sed -e 's|##45;|-|g')"	# Decode "-" symbol
	Case_Comment="$(echo aaa$Case_Comment | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	if [ "$(printf '%s\n' "$Case_Comment" | grep -e '".*"$')" != "" ] ; then
		# Discard delimiter quotes: double
		Case_Comment="$(printf '%s\n' "$Case_Comment" | sed -e 's|^"||' | sed -e 's|"$||')"
	else
		if [ "$(printf '%s\n' "$Case_Comment" | grep -e "'.*'$")" != "" ] ; then
			# Discard delimiter quotes: simple
			Case_Comment="$(printf '%s\n' "$Case_Comment" | sed -e "s|^'||" | sed -e "s|'$||")"
		fi
	fi
	Case_Id=""
	if [ "$(printf '%s\n' "$Case_Comment" | grep -e "^durruter-dnat/.*/")" != "" ] ; then
		Case_Id="$(printf '%s\n' "$Case_Comment" | cut -f 2 -d '/')"
	fi
	if [ "$(printf '%s\n' "$Case_Comment" | grep -e "^durruter-dnat/.*/")" != "" ] ; then
		# Discard internal identifiers
		Case_Comment="$(printf '%s\n' "$Case_Comment" | cut -f 3- -d '/')"
	fi
#	Case_Comment_Export="\"${Case_Comment}\""
	
	Case_RDev="$(printf '%s\n' "$LinesParameters" | grep -e '^-i ')"
	if [ "$Case_RDev" = "" ] ; then
		Case_RDev="$(printf '%s\n' "$LinesParameters" | grep -e '^--in-interface ')"
	fi
	Case_RDev="$(printf '%s\n' "$Case_RDev" | cut -f 2- -d ' ')"
	Case_RDev="$(echo aaa$Case_RDev | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	if [ "$Case_RDev" = "" ] ; then Case_RDev="*" ; fi
	if [ "$Case_RDev" = "*" ] ; then
		Case_RDev_Portable="'*'"
	else
		Case_RDev_Portable="$Case_RDev"
	fi
	Case_RDev_Current="$Case_RDev"
	
	Case_Protocol="$(printf '%s\n' "$LinesParameters" | grep -e '^-p ')"
	if [ "$Case_Protocol" = "" ] ; then
		Case_Protocol="$(printf '%s\n' "$LinesParameters" | grep -e '^--protocol ')"
	fi
	Case_Protocol="$(printf '%s\n' "$Case_Protocol" | cut -f 2- -d ' ')"
	Case_Protocol="$(echo aaa$Case_Protocol | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	if [ "$Case_Protocol" = "" ] ; then Case_Protocol="*" ; fi
	if [ "$Case_Protocol" = "*" ] ; then
		Case_Protocol_Portable="'*'"
	else
		Case_Protocol_Portable="$Case_Protocol"
	fi

	Case_RPorts="$(printf '%s\n' "$LinesParameters" | grep -e '^--dport ')"
	if [ "$Case_RPorts" = "" ] ; then
		Case_RPorts="$(printf '%s\n' "$LinesParameters" | grep -e '^--destination-port ')"
	fi
	Case_RPorts="$(printf '%s\n' "$Case_RPorts" | cut -f 2- -d ' ')"
	Case_RPorts="$(echo aaa$Case_RPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	Case_RPorts="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-')"
	Case_RPort1="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
	Case_RPort1="$(echo aaa$Case_RPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
	Case_RPort9="$(printf '%s\n' "$Case_RPorts" | cut -sf 2 -d '-')"
	Case_RPort9="$(echo aaa$Case_RPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
	if ! Is_IntegerNr $Case_RPort9 && Is_IntegerNr $Case_RPort1 ; then
		Case_RPort9=$Case_RPort1
	fi
	if [ "$Case_RPorts" = "" ] || [ "$Case_RPorts" = "*" ] ; then
		Case_RPorts='*'
		Case_RPort1='*'
		Case_RPort9='*'
		Case_RPorts_Show='*'
		Case_RPorts_Portable="'*'"
	else
		if [ "$Case_RPort1" = "$Case_RPort9" ] ; then
			# Briefed; accepted by iptables too.
			Case_RPorts=$Case_RPort1
			Case_RPorts_Show=$Case_RPort1
			Case_RPorts_Portable=$Case_RPort1
		else
			Case_RPorts="${Case_RPort1}:${Case_RPort9}"
			Case_RPorts_Show="$(printf '%s\n' "$Case_RPorts" | tr -s ':' "-")"
			Case_RPorts_Portable="$Case_RPorts"
		fi
	fi
	
	Case_InputIp="$(printf '%s\n' "$LinesParameters" | grep -e '^-d ')"
	if [ "$Case_InputIp" = "" ] ; then
		Case_InputIp="$(printf '%s\n' "$LinesParameters" | grep -e '^--destination ')"
	fi
	Case_InputIp="$(printf '%s\n' "$Case_InputIp" | cut -f 2- -d ' ' | cut -f 1 -d ':' | cut -f 1 -d '/')"
	Case_InputIp="$(echo aaa$Case_InputIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	if [ "$Case_InputIp" = "" ] ; then
		Case_InputIp='*'
		Case_InputIp_Portable="'*'"
	else
		if [ "$(EsIP $Case_InputIp)" = "1" ] ; then
			Case_InputIp_Current="$(echo aaa$Case_InputIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
		else
#			Case_InputIp="default"
			Case_InputIp_Current=""
			if [ "$Case_Id" != "" ] ; then
				Case_InputIp_Current="$($ITScmd --table nat | grep -e "POSTROUTING.*--comment .*durruter-dnat/${Case_Id}/")"
				Case_InputIp_Current="$(printf '%s\n' "$Case_InputIp_Current" | sed -e 's| -|\n-|g' | grep -e '^--to-source ')"
				Case_InputIp_Current="$(printf '%s\n' "$Case_InputIp_Current" | cut -f 2- -d ' ' | cut -f 1 -d ':' | cut -f 1 -d '/')"
				Case_InputIp_Current="$(echo aaa$Case_InputIp_Current | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
			fi
			if [ "$(EsIP $Case_InputIp_Current)" != "1" ] ; then
				Case_InputIp_Current="$(IpDeDispositivo "$Case_RDev_Current")"
			fi
		fi
		Case_InputIp_Portable="$Case_InputIp"
	fi
#	if [ "$Case_RPorts" = "" ] ; then
#		Case_InputIp="*"
#		Case_InputIp_Current="*"
#	fi
	
	Case_DestIp="$(printf '%s\n' "$LinesParameters" | grep -e '^--to-destination ')"
	Case_DestIp="$(printf '%s\n' "$Case_DestIp" | cut -f 2- -d ' ' | cut -f 1 -d ':' | cut -f 1 -d '/')"
	Case_DestIp="$(echo aaa$Case_DestIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	
	Case_DestPorts="$(printf '%s\n' "$LinesParameters" | grep -e '^--to-destination ')"
	Case_DestPorts="$(printf '%s\n' "$Case_DestPorts" | cut -f 2- -d ' ')"
	Case_DestPorts="$(echo aaa$Case_DestPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
	Case_DestPorts="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '\n' | tail -n 1)"
	Case_DestPorts="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-')"
	if ! Is_IntegerNr $Case_DestPorts && [ "$(printf '%s' "$Case_DestPorts" | grep -e '.:.' -e '.-.')" = "" ] ; then Case_DestPorts='' ; fi
	if [ "$Case_DestPorts" = "" ] || [ "$Case_DestPorts" = "-" ] ; then Case_DestPorts='*' ; fi
	if [ "$Case_DestPorts" = "*" ] ; then
		Case_DestPort1="*"
		Case_DestPort9="*"
		Case_DestPorts_Show=''
		Case_DestPorts_Portable="'*'"
	else
		Case_DestPort1="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
		if ! Is_IntegerNr $Case_DestPort1 && Is_IntegerNr $Case_RPort1 ; then
			Case_DestPort1=$Case_RPort1
			Case_DestPort9=$Case_RPort9	# Case_RPort1 could not be range-consistent with the case of Dir_DestPorts
		else
			Case_DestPort1="$(echo aaa$Case_DestPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			Case_DestPort9="$(printf '%s\n' "$Case_DestPorts" | cut -sf 2 -d '-')"
			Case_DestPort9="$(echo aaa$Case_DestPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			if ! Is_IntegerNr $Case_DestPort9 && Is_IntegerNr $Case_DestPort1 ; then
				Case_DestPort9=$Case_DestPort1
			fi
		fi
		if [ "$Case_DestPort1" = "$Case_DestPort9" ] ; then
			# Briefed; accepted by iptables too.
			Case_DestPorts=$Case_DestPort1
			Case_DestPorts_Show=$Case_DestPort1
			Case_DestPorts_Portable=$Case_DestPort1
		else
			Case_DestPorts="${Case_DestPort1}-${Case_DestPort9}"
			Case_DestPorts_Show="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' "-")"
			Case_DestPorts_Portable="$Case_DestPorts"
		fi
		if [ "$Case_DestPorts_Show" = "$Case_RPorts_Show" ] ; then Case_DestPorts_Show="" ; fi
		if [ "$(printf '%s\n' "$Case_DestPorts_Portable" | tr -s ':' '-')" = "$(printf '%s\n' "$Case_RPorts_Portable" | tr -s ':' '-')" ] ; then Case_DestPorts_Portable="=" ; fi
	fi
}

DnatCaseMatchesFilter ()
# Syntax as a sentence: DnatCaseMatchesFilter "$Selected_RDev" "$Selected_Protocol" "$Selected_RPort1" "$Selected_RPort9" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPort1" "$Selected_DestPort9" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id"
# Description: Returns (stdout) "1" if parameters restrictions allows Case_* variables to match an entry.
# Notes:
#	- All strings are case-insensitive.
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPort1="$3"
	local Selected_RPort9="$4"
	local Selected_InputIp="$5"
	local Selected_DestIp="$6"
	local Selected_DestPort1="$7"
	local Selected_DestPort9="$8"
	local Selected_Comment="$9"
	local Selected_Enabled="${10}"
	local Selected_Id="${11}"
#	local WanDevice="${12}"
	local Value=1

	if [ "$Selected_RDev" = "." ] ; then
		Selected_RDev=''
		Selected_RDev_Current=''
	fi
	if [ "$Selected_RDev" = "*" ] ; then
		Selected_RDev_Current="$Selected_RDev"
	else
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then
			Selected_RDev="to-wan"
		fi
		if [ "$Selected_RDev" = "to-wan" ] ; then
			Selected_RDev_Current="$(DefaultNICs | head -n 1)"
		else
			Selected_RDev_Current="$Selected_RDev"
		fi
	fi
	if [ "$Selected_RDev" != "$Case_RDev" ] && [ "$Selected_RDev" != "all" ] ; then
		if [ "$Selected_RDev_Current" != "" ] && [ "$(printf '%s\n' "$Case_RDev_Current" | grep -ie "^${Selected_RDev_Current}$")" = "" ] ; then
			if [ "$Selected_RDev" != "to-wan" ] || [ "$Case_RDev" != "to-wan" ] ; then
				Value=0
			fi
		fi
	fi
	if [ "$Selected_Protocol" != "$Case_Protocol" ] && [ "$Selected_Protocol" != "all" ] ; then
		if [ "$Selected_Protocol" != "" ] && [ "$(printf '%s\n' "$Case_Protocol" | grep -ie "^$Selected_Protocol$")" = "" ] ; then Value=0 ; fi
	fi
	if [ "$Selected_RPort1" != "$Case_RPort1" ] && [ "$Selected_RPort1" != "all" ] ; then
		if Is_IntegerNr $Selected_RPort1 ; then
			if Is_IntegerNr $Case_RPort1 ; then
				if [ $Case_RPort1 -lt $Selected_RPort1 ] ; then Value=0 ; fi
			else
				Value=0
			fi
		fi
	fi
	if [ "$Selected_RPort9" != "$Case_RPort9" ] && [ "$Selected_RPort9" != "all" ] ; then
		if Is_IntegerNr $Selected_RPort9 ; then
			if Is_IntegerNr $Case_RPort9 ; then
				if [ $Case_RPort9 -gt $Selected_RPort9 ] ; then Value=0 ; fi
			else
				Value=0
			fi
		fi
#			if [ "$Case_InputIp" = "" ] || [ "$(printf '%s\n' " all any todo toda todos todas " | grep -ie " $Case_InputIp ")" != "" ] ; then
			if [ "$Case_InputIp" = "" ] ; then
				Case_InputIp="*"
			fi
	fi
	if [ "$Selected_InputIp" != "$Case_InputIp" ] && [ "$Selected_InputIp" != "all" ] ; then
		if [ "$Selected_InputIp" = "*" ] ; then
			Selected_InputIp_Regexp='.*'
		else
#			if [ "$Selected_InputIp" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
			if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
				# Selected default IP for device
				Selected_InputIp="$(IpDeDispositivo "$Selected_RDev")"
				if [ "$(EsIP "$Case_InputIp")" = "1" ] ; then
					Selected_InputIp_Regexp="$(printf '%s\n' "$Selected_InputIp" | sed -e 's|\.|\\.|g' | sed -e 's|*|.*|g')"	# Allowing simple asterisk * wildcards
					if [ "$(printf '%s\n' "$Case_InputIp" | grep -ie "^$Selected_InputIp_Regexp$")" = "" ] ; then Value=0 ; fi
				fi
			else
				Selected_InputIp_Regexp="$(printf '%s\n' "$Selected_InputIp" | sed -e 's|\.|\\.|g' | sed -e 's|*|.*|g')"	# Allowing simple asterisk * wildcards
				if [ "$Selected_InputIp" != "" ] && [ "$(printf '%s\n' "$Case_InputIp" | grep -ie "^$Selected_InputIp_Regexp$")" = "" ] ; then Value=0 ; fi
			fi
		fi
	fi
	if [ "$Selected_DestIp" != "$Case_DestIp" ] && [ "$Selected_DestIp" != "all" ] ; then
		Selected_DestIp_Regexp="$(printf '%s\n' "$Selected_DestIp" | sed -e 's|\.|\\.|g' | sed -e 's|*|.*|g')"	# Allowing simple asterisk * wildcards
		if [ "$Selected_DestIp" != "" ] && [ "$(printf '%s\n' "$Case_DestIp" | grep -ie "^$Selected_DestIp_Regexp$")" = "" ] ; then Value=0 ; fi
	fi
	if [ "$Selected_DestPort1" != "$Case_DestPort1" ] && [ "$Selected_DestPort1" != "all" ] ; then
		if Is_IntegerNr $Selected_DestPort1 ; then
			if Is_IntegerNr $Case_DestPort1 ; then
				if [ $Case_DestPort1 -lt $Selected_DestPort1 ] ; then Value=0 ; fi
			else
				Value=0
			fi
		fi
	fi
	if [ "$Selected_DestPort9" != "$Case_DestPort9" ] && [ "$Selected_DestPort9" != "all" ] ; then
		if Is_IntegerNr $Selected_DestPort9 ; then
			if Is_IntegerNr $Case_DestPort9 ; then
				if [ $Case_DestPort9 -gt $Selected_DestPort9 ] ; then Value=0 ; fi
			else
				Value=0
			fi
		fi
	fi
	if [ "$Selected_Comment" != "" ] && [ "$Selected_Comment" != "all" ] && [ "$(printf '%s\n' "$Case_Comment" | grep -ie "$Selected_Comment")" = "" ] ; then Value=0 ; fi
	if [ "$Selected_Enabled" != "" ] && [ "$Selected_Enabled" != "all" ] &&  [ "$(printf '%s\n' "$Case_Enabled" | grep -ie "^${Selected_Enabled}")" = "" ] ; then Value=0 ; fi
	if [ "$Selected_Id" != "" ] && [ "$Selected_Id" != "all" ] && [ "$(printf '%s\n' "$Case_Id" | grep -ie "^$Selected_Id$")" = "" ] ; then Value=0 ; fi
	printf '%s\n' "$Value"
}

RebuildDnatLoaders ()
# Compone de nuevo los ficheros if-up_fast.sh if-down_fast.sh para la cadena de directorios
# afectados por una entrada DNAT
# Ejemplo: RebuildDnatLoaders eth0 80 tcp
{
	local Selected_RDev="$1"
	local Selected_RPorts="$2"
	local Selected_Protocol="$3"
	local Dir_DestIp=""
	local Dir_InputIp=""
	local InputIp_Parameter=""
	local Dir_DestPorts=""
	local DestPort1=""
	local DestPort9=""
	local Dir_Id=""
	local Dir_Comment=""
	local FilePathsList=""
	local RDev_Parameter=""
	local RedLocal=""
	local CurScript_Up=""
	local CurScript_Down=""
	local Dir_RDev=""
	local Dir_RPort1=""
	local Port9Actual=""
	local Selected_RPort1=""
	local Dir_Protocol=""
	local Dir_Path=""
	local CurDirLine=''
	local Dir_ToRebuild=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
	if [ "$Selected_RPorts" = "." ] ; then Selected_RPorts='' ; fi
	if [ "$Selected_Protocol" = "." ] ; then Selected_Protocol='' ; fi
	Selected_RPort1="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
	if [ ! -f "${SystemConfigDir}/${Selected_RDev}/dnat/${Selected_RPort1}/${Selected_Protocol}/destination-ip.txt" ] ; then
		# Puede venir de una eliminacion; limpiamos directorios precedentes sin contenido.
		if [ "$Selected_RPorts" = "" ] ; then
			if [ "$Selected_RDev" = "" ] ; then
				FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/"*"/"*"/destination-ip.txt" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/"*"/dnat/"*
					FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/"*"/" 2>/dev/null)"
					if [ "$FilePathsList" = "" ] ; then
						rm -fr "${SystemConfigDir}/"*"/dnat"
						FilePathsList="$(find "${SystemConfigDir}/"*"/"*"/" 2>/dev/null)"
						if [ "$FilePathsList" = "" ] ; then
							rm -fr "${SystemConfigDir}/"*"/"
						fi
					fi
				fi
			else
				FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/dnat/"*"/"*"/destination-ip.txt" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/${Selected_RDev}/dnat/"*
					FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/dnat/"*"/" 2>/dev/null)"
					if [ "$FilePathsList" = "" ] ; then
						rm -fr "${SystemConfigDir}/${Selected_RDev}/dnat"
						FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/"*"/" 2>/dev/null)"
						if [ "$FilePathsList" = "" ] ; then
							rm -fr "${SystemConfigDir}/${Selected_RDev}"
						fi
					fi
				fi
			fi
		else
			if [ "$Selected_RDev" = "" ] ; then
				FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/${Selected_RPort1}/"*"/destination-ip.txt" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/"*"/dnat/${Selected_RPort1}"
					FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/"*"/" 2>/dev/null)"
					if [ "$FilePathsList" = "" ] ; then
						rm -fr "${SystemConfigDir}/"*"/dnat"
						FilePathsList="$(find "${SystemConfigDir}/"*"/"*"/" 2>/dev/null)"
						if [ "$FilePathsList" = "" ] ; then
							rm -fr "${SystemConfigDir}/"*"/"
						fi
					fi
				fi
			else
				FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/dnat/${Selected_RPort1}/"*"/destination-ip.txt" 2>/dev/null)"
				if [ "$FilePathsList" = "" ] ; then
					rm -fr "${SystemConfigDir}/${Selected_RDev}/dnat/${Selected_RPort1}"
					FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/dnat/"*"/" 2>/dev/null)"
					if [ "$FilePathsList" = "" ] ; then
						rm -fr "${SystemConfigDir}/${Selected_RDev}/dnat"
						FilePathsList="$(find "${SystemConfigDir}/${Selected_RDev}/"*"/" 2>/dev/null)"
						if [ "$FilePathsList" = "" ] ; then
							rm -fr "${SystemConfigDir}/${Selected_RDev}"
						fi
					fi
				fi
			fi
		fi
	fi
	FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/"*"/"*"/destination-ip.txt" 2>/dev/null)"
	IFS="$(printf "\n\b")" ; for CurDirLine in $FilePathsList ; do unset IFS
		Dir_Path="$(dirname "$CurDirLine")"
		Dir_ToRebuild="1"
		Dir_RDev="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 5 | head -n 1)"
		Dir_RPort1="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 3 | head -n 1)"
		Dir_Protocol="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 2 | head -n 1)"
		if [ "$Dir_RDev" != "$Selected_RDev" ] && [ "$Selected_RDev" != "" ] ; then
			Dir_ToRebuild="0"
		fi
		if [ "$Dir_RPort1" != "$Selected_RPort1" ] && [ "$Selected_RPorts" != "" ] ; then
			Dir_ToRebuild="0"
		fi
		if [ "$Dir_Protocol" != "$Selected_Protocol" ] && [ "$Selected_Protocol" != "" ] ; then
			Dir_ToRebuild="0"
		fi
		Dir_DestIp="$(cat "${Dir_Path}/destination-ip.txt")"
		LastStatus=0
		if [ "$(EsIP "$(printf '%s\n' "$Dir_DestIp" | grep -e '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$' | head -n 1)")" != "1" ] ; then
			printf '%s\n' "${sERROR}E: no se reconocieron datos de la entrada DNAT en:" 1>&2
			printf '%s\n' "          $Dir_Path${fRESET}" 1>&2
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$Dir_ToRebuild" = "1" ] && [ $LastStatus -eq 0 ] ; then
			Dir_RPorts="$(cat "${Dir_Path}/reception-ports.txt" 2>/dev/null)"
			Dir_InputIp="$(cat "${Dir_Path}/input-ip.txt" 2>/dev/null)"
			Dir_DestPorts="$(cat "${Dir_Path}/destination-ports.txt" 2>/dev/null)"
			Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
#			if ! Is_IntegerNr $Dir_Id ; then
#				Dir_Id=$(NewIdForEntry)
#				printf '%s\n' "$Dir_Id" > "${Dir_Path}/id.txt"
#			fi
			Dir_Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null | tr -s '"' "'")"
			# Loader for this entry directory
			CurScript_Up="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/${Dir_Protocol}/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript_Up"
			printf '%s\n' "# Loader of $ServiceName rule for DNAT input: Ports ${Dir_Protocol}/${Dir_RPort1}-* from device $Dir_RDev" >> "$CurScript_Up"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Attended IP" >> "$CurScript_Up"
			printf '%s\n' 'NIC="$1"' >> "$CurScript_Up"
			printf '%s\n' 'NicIp="$2"' >> "$CurScript_Up"
			printf '%s\n' 'if [ "$NIC" != "" ] && [ "$NicIp" = "" ] ; then' >> "$CurScript_Up"
			printf '%s\n' '	NicIp="$(ip address show "$NIC" | grep -e "inet " | sed -e "s|.*inet ||" | cut -f 1 -d "/")"' >> "$CurScript_Up"
			printf '%s\n' 'fi' >> "$CurScript_Up"
#			printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "E: device name and WAN IP were expected." 1>&2 ; exit 79 ; fi' >> "$CurScript_Up"
			printf '%s\n' 'sysctl -w net/ipv4/ip_forward=1 > /dev/null' >> "$CurScript_Up"
			chmod ug+rx "$CurScript_Up"
			# Unloader for this entry directory
			CurScript_Down="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/${Dir_Protocol}/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript_Down"
			printf '%s\n' "# Unloader of $ServiceName rule for DNAT input: Ports ${Dir_Protocol}/${Dir_RPort1}-* from device $Dir_RDev" >> "$CurScript_Down"
			chmod ug+rx "$CurScript_Down"
			NrCase=0
			for Case_Id in $Dir_Id ; do
				# Each case is intended for each IP on same reception device.
				NrCase=$(($NrCase + 1))
				if Is_IntegerNr $Case_Id ; then	# Non-numeric cases can be because of removed lines in id.txt
#					Case_DestIp="$(printf '%s\n' "$Dir_DestIp" | awk "NR >= $NrCase" | head -n 1)"
#					Case_InputIp="$(printf '%s\n' "$Dir_InputIp" | awk "NR >= $NrCase" | head -n 1)"
#					Case_RPorts="$(printf '%s\n' "$Dir_RPorts" | awk "NR >= $NrCase" | head -n 1)"
#					Case_RPort1="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
#					Case_RPort9="$(printf '%s\n' "$Case_RPorts" | tr -s ':' '-' | cut -sf 2 -d '-')"
#					if Is_IntegerNr $Case_RPort1 && Is_IntegerNr $Case_RPort9 ; then
#						Case_RPorts="${Case_RPort1}-${Case_RPort9}"
#					else
#						Case_RPorts="${Dir_RPort1}-${Dir_RPort1}"
#					fi
#					Case_DestPorts="$(printf '%s\n' "$Dir_DestPorts" | awk "NR >= $NrCase" | head -n 1)"
#					Case_DestPort1="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
#					Case_DestPort9="$(printf '%s\n' "$Case_DestPorts" | tr -s ':' '-' | cut -sf 2 -d '-')"
#					if Is_IntegerNr $Case_DestPort1 && Is_IntegerNr $Case_DestPort9 ; then
#						Case_DestPorts="${Case_DestPort1}-${Case_DestPort9}"
#					else
#						Case_DestPorts="${Case_RPorts}"
#					fi
#					Case_Comment="$(printf '%s\n' "$Dir_Comment" | awk "NR >= $NrCase" | head -n 1)"
					DnatDirCaseValues $NrCase "$Dir_RDev" "$Dir_Protocol" "$Dir_RPort1" "$Dir_RPorts" "$Dir_InputIp" "$Dir_DestIp" "$Dir_DestPorts" "$Dir_Comment" "$Dir_Id"
					if [ "$Case_InputIp" = "*" ] ; then
						InputIp_Parameter=''
					else
						if [ "$(EsIP "$Case_InputIp")" = "1" ] ; then
							InputIp_Parameter="$Case_InputIp"
						else
							InputIp_Parameter='$NicIp'
						fi
					fi
					if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then
						printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sERROR}E: device name and WAN IP were expected.${fRESET}" 1>&2 ; exit 0 ; fi' >> "$CurScript_Up"
					else
						printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sWARN}W: device name and WAN IP were expected.${fRESET}" 1>&2 ; fi' >> "$CurScript_Up"
					fi
					if [ "$Case_RDev" = "." ] ; then
						Case_RDev=''
						RDev_Parameter=''
					fi
					if [ "$Case_RDev" = "*" ] ; then
						RDev_Parameter=''
					else
						if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Case_RDev ")" != "" ] ; then
							Case_RDev='to-wan'
							RDev_Parameter='$1'
						else
							RDev_Parameter="$Case_RDev"
						fi
					fi
#					if [ "$IptablesMetadata" = "1" ] ; then
#						printf '%s\n' "$ITcmd --table nat --append PREROUTING --destination $InputIp_Parameter --protocol $Case_Protocol --dport $Case_RPorts --in-interface $RDev_Parameter --jump DNAT --to-destination ${Case_DestIp}:${Case_DestPorts} -m comment --comment \"durruter-dnat/${Case_Id}/${Case_Comment}\"" >> "$CurScript_Up"
#						printf '%s\n' "$ITcmd --table nat --append POSTROUTING --source $Case_DestIp --protocol $Case_Protocol --out-interface $RDev_Parameter --jump SNAT --to-source $InputIp_Parameter -m comment --comment \"durruter-dnat/${Case_Id}/${Case_Comment}\"" >> "$CurScript_Up"
#						printf '%s\n' "$ITScmd | grep -ive 'durruter-dnat/${Case_Id}/' | $ITRcmd" >> "$CurScript_Down"
#					else
#						printf '%s\n' "$ITcmd --table nat --append PREROUTING --destination $InputIp_Parameter --protocol $Case_Protocol --dport $Case_RPorts --in-interface $RDev_Parameter --jump DNAT --to-destination ${Case_DestIp}:${Case_DestPorts}" >> "$CurScript_Up"
#						printf '%s\n' "$ITcmd --table nat --append POSTROUTING --source $Case_DestIp --protocol $Case_Protocol --out-interface $RDev_Parameter --jump SNAT --to-source $InputIp_Parameter" >> "$CurScript_Up"
#						printf '%s\n' "$ITcmd --table nat --delete POSTROUTING --source $Case_DestIp --protocol $Case_Protocol --out-interface $RDev_Parameter --jump SNAT --to-source $InputIp_Parameter" >> "$CurScript_Down"
#					fi
					CurCommand="$ITcmd --table nat --append PREROUTING"
					if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then CurCommand="$CurCommand --destination $InputIp_Parameter" ; fi
					if [ "$Case_Protocol" != "" ] && [ "$Case_Protocol" != "*" ] && [ "$Case_Protocol" != "_any" ] ; then CurCommand="$CurCommand --protocol $Case_Protocol" ; fi
					if [ "$Case_RPorts" != "" ] && [ "$Case_RPorts" != "*" ] && [ "$Case_RPorts" != "_any" ] ; then CurCommand="$CurCommand --dport $Case_RPorts" ; fi
					if [ "$RDev_Parameter" != "" ] && [ "$RDev_Parameter" != "*" ] && [ "$RDev_Parameter" != "_any" ] ; then CurCommand="$CurCommand --in-interface $RDev_Parameter" ; fi
					CurCommand="$CurCommand --jump DNAT --to-destination ${Case_DestIp}"
					if [ "$Case_DestPorts" != "" ] && [ "$Case_DestPorts" != "*" ] && [ "$Case_DestPorts" != "_any" ] ; then CurCommand="${CurCommand}:${Case_DestPorts}" ; fi
					if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-dnat/${Case_Id}/${Case_Comment}\"" ; fi
					printf '%s\n' "$CurCommand" >> "$CurScript_Up"
					
					CurCommand="$ITcmd --table nat --append POSTROUTING"
					if [ "$Case_DestIp" != "" ] && [ "$Case_DestIp" != "*" ] && [ "$Case_DestIp" != "_any" ] ; then CurCommand="$CurCommand --source $Case_DestIp" ; fi
					if [ "$Case_Protocol" != "" ] && [ "$Case_Protocol" != "*" ] && [ "$Case_Protocol" != "_any" ] ; then CurCommand="$CurCommand --protocol $Case_Protocol" ; fi
					if [ "$RDev_Parameter" != "" ] && [ "$RDev_Parameter" != "*" ] && [ "$RDev_Parameter" != "_any" ] ; then CurCommand="$CurCommand --out-interface $RDev_Parameter" ; fi
#					if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then CurCommand="$CurCommand --to $InputIp_Parameter" ; fi
					if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then CurCommand="$CurCommand --jump SNAT --to-source $InputIp_Parameter" ; fi
					if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-dnat/${Case_Id}/${Case_Comment}\"" ; fi
					printf '%s\n' "$CurCommand" >> "$CurScript_Up"
					
					if [ "$IptablesMetadata" = "1" ] ; then
						printf '%s\n' "$ITScmd | grep -ive 'durruter-dnat/${Case_Id}/' | $ITRcmd" >> "$CurScript_Down"
					else
						CurCommand="$ITcmd --table nat --delete POSTROUTING"
						if [ "$Case_DestIp" != "" ] && [ "$Case_DestIp" != "*" ] && [ "$Case_DestIp" != "_any" ] ; then CurCommand="$CurCommand --source $Case_DestIp" ; fi
						if [ "$Case_Protocol" != "" ] && [ "$Case_Protocol" != "*" ] && [ "$Case_Protocol" != "_any" ] ; then CurCommand="$CurCommand --protocol $Case_Protocol" ; fi
						if [ "$RDev_Parameter" != "" ] && [ "$RDev_Parameter" != "*" ] && [ "$RDev_Parameter" != "_any" ] ; then CurCommand="$CurCommand --out-interface $RDev_Parameter" ; fi
						CurCommand="$CurCommand --jump SNAT --to-destination ${Case_DestIp}"
#						if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then CurCommand="$CurCommand --to $InputIp_Parameter" ; fi
						if [ "$InputIp_Parameter" != "" ] && [ "$InputIp_Parameter" != "*" ] && [ "$InputIp_Parameter" != "_any" ] ; then
							printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sERROR}E: device name and WAN IP were expected.${fRESET}" 1>&2 ; exit 0 ; fi' >> "$CurScript_Down"
							CurCommand="$CurCommand --jump SNAT --to-source $InputIp_Parameter"
						else
							printf '%s\n' 'if [ "$NicIp" = "" ] ; then echo "${sWARN}W: device name and WAN IP were expected.${fRESET}" 1>&2 ; fi' >> "$CurScript_Down"
						fi
						printf '%s\n' "$CurCommand" >> "$CurScript_Down"
					fi
				fi
			done
			# Cargador de las entradas para el número de puerto
			CurScript="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName rules for DNAT inputs: Every protocol at ports ${Dir_RPort1}-* from device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Attended IP" >> "$CurScript_Up"
			printf '%s\n' "" >> "$CurScript"
			# Esto soluciona el problema de que la interfaz predeterminada va cambiando para cada evento if-up.
			printf '%s\n' "# Previously unloading same rules, to foresee repeated call on device events:" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/"*"/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			printf '%s\n' "" >> "$CurScript"
			printf '%s\n' "# Now comes the loading:" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/"*"/if-up_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Descargador de las entradas para el número de puerto
			CurScript="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName rules for DNAT inputs: Every protocol at ports ${Dir_RPort1}-* from device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/"*"/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Cargador del servicio dnat para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_RDev}/dnat/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName rules for DNAT inputs: Every port from device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Attended IP" >> "$CurScript_Up"
			printf '%s\n' "" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/dnat/"*"/if-up_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Descargador del servicio nat para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_RDev}/dnat/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName rules for DNAT inputs: Every port from device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/dnat/"*"/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Cargador de todos los servicios para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_RDev}/if-up_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Loader of $ServiceName services for device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "# 2 parameters are expected: Effective NIC and Attended IP" >> "$CurScript_Up"
			printf '%s\n' "" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/"*"/if-up_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
			# Descargador de todos los servicios para el dispositivo
			CurScript="${SystemConfigDir}/${Dir_RDev}/if-down_fast.sh"
			printf '%s\n' '#!/bin/sh' > "$CurScript"
			printf '%s\n' "# Unloader of $ServiceName services for device $Dir_RDev" >> "$CurScript"
			printf '%s\n' "" >> "$CurScript"
			cat "${SystemConfigDir}/${Dir_RDev}/"*"/if-down_fast.sh" 2>/dev/null | grep -ve '^#!/' -ve '^$' -ve 'parameters .*expected' -ve 'expected .*parameters' >> "$CurScript"
			chmod ug+rx "$CurScript"
		fi
	done
	return $StatusCode
}

OneNatAlreadyEnabled ()
# Sintaxis como función: $(OneNatAlreadyEnabled "$RedLocal")
# Descripción:
#	Devuelve (echo) los datos RedLocal SalidaDev y SalidaIp separados por espacios
#	en caso de que la salida NAT especificada ya esté en iptables.
#	De lo contrario no devuelve nada.
# Notas:
#	- El campo Comment se devuelve tal cual, incluyendo comillas e identificadores durruter-nat
{
	local RedLocal="$1"
	local Valor=""
	local ListaOrigen=""
	local LineaActual=""
	local DatosActual=""
	local RedActual=""
	local DevActual=""
	local IpActual=""
	local CurComment=""
	
#	ListaOrigen="$($ITScmd | grep -ie "POSTROUTING" | grep -E "( -s| --source)" | grep -iE "( -j SNAT| --jump SNAT)" | grep -vE "(/32|durruter-dnat/)")"
	ListaOrigen="$($ITScmd | grep -ie "POSTROUTING" | grep -E "( -s| --source)" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve 'durruter-dnat/')"
	IFS="$(printf "\n\b")" ; for LineaActual in $ListaOrigen ; do unset IFS
		DatosActual="$(printf '%s\n' "$LineaActual" | sed -e "s/ -/\n-/g")"
		RedActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-s |^--source )" | cut -f 2- -d " ")"
		RedActual="$(echo aaa$RedActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		DevActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-o |^--out-interface )" | cut -f 2- -d " ")"
		DevActual="$(echo aaa$DevActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		IpActual="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-source )" | cut -f 2- -d " ")"
		IpActual="$(echo aaa$IpActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		CurComment="$(printf '%s\n' "$DatosActual" | grep -E "(^--comment )" | cut -f 2- -d " ")"
		if [ "$RedLocal" = "$RedActual" ] ; then
			if [ "$RedActual" = "" ] ; then RedActual="+" ; fi
			if [ "$DevActual" = "" ] ; then DevActual="+" ; fi
			if [ "$IpActual" = "" ] ; then IpActual="+" ; fi
#			if [ "$CurComment" = "" ] ; then CurComment="." ; fi
			Valor="$RedActual $DevActual $IpActual $CurComment"
		fi
	done
	if [ "$Valor" != "" ] ; then
		printf '%s\n' "$Valor"
	fi
}

NatYaGuardada ()
# Sintaxis como función: $(NatYaGuardada "$RedLocal")
# Descripción:
#	Devuelve (echo) "1" en caso de que la salida NAT especificada ya esté registrada en el cargador
{
	local RedLocal="$1"
	local IpRed=""
	local Valor="0"
	local FilePathsList=""
	
	if [ "$RedLocal" = "*" ] ; then
		IpRed="$(printf '%s\n' "$RedLocal" | cut -f 1 -d '/')"
		FilePathsList="$(find "${SystemConfigDir}/"*"/nat/_any/lan-mask.txt" 2>/dev/null)"
	else
		IpRed="$(printf '%s\n' "$RedLocal" | cut -f 1 -d '/')"
		FilePathsList="$(find "${SystemConfigDir}/"*"/nat/${IpRed}/lan-mask.txt" 2>/dev/null)"
	fi
	if [ "$FilePathsList" != "" ] ; then
		Valor="1"
	fi
	printf '%s\n' "$Valor"
}

GuardarNat ()
# Sintaxis como instrucción: GuardarNat $Selected_OutDev $RedLocal $IpWan $Id $Selected_Comment
# Descripcion:	Elimina los ficheros de las salidas nat anteriormente guardadas con la misma IP de red,
#				y guarda los datos especificados de nuevo.
{
	local Selected_OutDev="$1"
	local RedLocal="$2"
	local IpWan="$3"
	local Selected_Comment="$4"
	local Selected_Id="$5"
	local NetIp=""
	local LanMask=""
	local SalidasEncontradas=""
	local CurDir=""
	local Dir_Path=""
	local Id=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Selected_OutDev" = "." ] || [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_OutDev ")" != "" ] ; then
		Selected_OutDev='to-wan'
	fi
	if [ "$IpWan" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $IpWan ")" != "" ] ; then
		IpWan='default'
	fi
	Id="$Selected_Id"
	if ! Is_IntegerNr $Id ; then Id=$(NewIdForEntry) ; fi
	NetIp="$(printf '%s\n' "$RedLocal" | cut -f 1 -d '/')"
	LanMask="$(printf '%s\n' "$RedLocal" | cut -sf 2 -d '/')"
	SalidasEncontradas="$(find "${SystemConfigDir}/"*"/nat/${NetIp}/" 2>/dev/null)"
	SalidasEncontradas="$(printf '%s\n' "$SalidasEncontradas" | grep -e "/${NetIp}/$")"
	IFS="$(printf "\n\b")" ; for CurDir in $SalidasEncontradas ; do unset IFS
		rm -fr "$CurDir"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	done
	if [ $StatusCode -eq 0 ] ; then
		Dir_OutDev="$Selected_OutDev"
		if [ "$Dir_OutDev" = "*" ] ; then Dir_OutDev='_any' ; fi
		Dir_NetIp="$NetIp"
		if [ "$Dir_NetIp" = "*" ] ; then Dir_NetIp='_any' ; fi
		Dir_Path="${SystemConfigDir}/${Dir_OutDev}/nat/${Dir_NetIp}"
		mkdir -p "$Dir_Path"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_IpWan="$IpWan"
		if [ "$Dir_IpWan" = "*" ] ; then Dir_IpWan='_any' ; fi
		printf '%s\n' "$Dir_IpWan" > "${Dir_Path}/wan-ip.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "$LanMask" > "${Dir_Path}/lan-mask.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "$Id" > "${Dir_Path}/id.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "$Selected_Comment" > "${Dir_Path}/description.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		RebuildNatLoaders "$Selected_OutDev" "$NetIp"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Effective_NIC ()
# Syntax as a function: "$(Effective_NIC "$Selected_NIC")"
# Description: Returns NIC corresponding to specified value.
# Examples:
#	eth2 -> eth2
#	default -> eth0
#	default -> "" (empty string when not detected)
#	"" -> ""
#	* -> *
#	_any -> *
#	Q05e.X -> Q05e.X
# Notes: 
#	- Does not verify real NIC existence.
#	- Special value "*" is returned on its case.
{
	local Selected_NIC="$1"
	local Value=''

	if [ "$Selected_NIC" = "*" ] || [ "$Selected_NIC" = "_any" ] || [ "$Selected_NIC" = "any" ] ; then
		Value='*'
	else
		if [ "$Selected_NIC" = "default" ] || [ "$Selected_NIC" = "wan" ] || [ "$Selected_NIC" = "to-wan" ] || [ "$Selected_NIC" = "." ] || [ "$Selected_NIC" = "" ] ; then
			Value="$(DefaultNICs | head -n 1)"
		else
			Value="$Selected_NIC"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

Effective_IP ()
# Syntax as a function: "$(Effective_IP "$Selected_NIC" "$Selected_IP")"
# Description: Returns IP corresponding to selected or legated value as convenient.
# Examples:
#	eth1, 192.168.1.3 -> 192.168.1.3
#	eth1, default -> 192.168.1.1
#	default, default -> 192.168.1.1 (IP taken from default NIC)
#	"", default -> 192.168.1.1 (IP taken from default NIC)
#	eth1, * -> *
#	eth1, _any -> *
#	eth1, Q05e.X -> Q05e.X
# Notes: 
#	- Does not verify IP format.
#	- Special value "*" is returned on its case.
{
	local Selected_NIC="$1"
	local Selected_IP="$2"
	local Value=''

	if [ "$Selected_IP" = "*" ] || [ "$Selected_IP" = "_any" ] || [ "$Selected_IP" = "any" ] ; then
		Value='*'
	else
		if [ "$Selected_IP" = "default" ] || [ "$Selected_IP" = "." ] || [ "$Selected_IP" = "" ] ; then
			if [ "$Selected_NIC" = "default" ] || [ "$Selected_NIC" = "wan" ] || [ "$Selected_NIC" = "to-wan" ] || [ "$Selected_NIC" = "." ] || [ "$Selected_NIC" = "" ] ; then
				Value="$(PrimeraIPEquip)"
			else
				if [ "$Selected_NIC" != "*" ] ; then
					Value="$(IPv4FromDevice "$Selected_NIC" | head -n 1)"
				fi
				if [ "$Value" = "" ] ; then Value="$(PrimeraIPEquip)" ; fi
			fi
		else
			Value="$Selected_IP"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

Effective_Ports ()
# Syntax as a function: "$(Effective_Ports "$Previous_Ports" "$Selected_Ports")"
# Description: Returns ports specification corresponding to selected or legated value as convenient.
# Examples:
#	2, 1 -> 1
#	1001-1999, 2001-2999 -> 2001-2999
#	2001:2999, . -> 2001:2999
#	2, . -> 2
#	2, "" -> 2
#	2, * -> *
#	2, _any -> *
#	2, Q05e.X -> Q05e.X
# Notes: 
#	- Does not verify ports format.
#	- Special value "*" is returned on its case.
{
	local Previous_Ports="$1"
	local Selected_Ports="$2"
	local Value=''

	if [ "$Selected_Ports" = "*" ] || [ "$Selected_Ports" = "_any" ] || [ "$Selected_Ports" = "any" ] ; then
		Value='*'
	else
		if [ "$Selected_Ports" = "default" ] || [ "$Selected_Ports" = "=" ] || [ "$Selected_Ports" = "." ] || [ "$Selected_Ports" = "" ] ; then
			Value="$Previous_Ports"
		else
			Value="$Selected_Ports"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

Ask_NIC ()
# Syntax as a sentence: Ask_NIC $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by input interface
#		11 = Creating an entry by input interface
#		20 = Selecting current entries by output interface
#		21 = Creating an entry by output interface
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local NICs=''
	local WanNICs=''
	local CurText=''
	local Value="$SpecifiedValue"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		NICs="$(ListaDispositivosRed)"
		WanNICs="$(DefaultNICs)"
		if [ "$WanNICs" != "" ] ; then
			WanNICs=" ${ParO}que ahora es $(echo aaa$WanNICs | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | sed -e 's| | o |g')${ParC}"
		fi
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			# Input
			CurText="${CurText}${cHEADING1}Escriba el dispositivo de red atendido como entrada${cRESET}\n"
			CurText="${CurText}Input/WAN device\n"
		else
			# Output
			CurText="${CurText}${cHEADING1}Escriba el dispositivo de red de salida${cRESET}\n"
			CurText="${CurText}Output/WAN device\n"
		fi
		CurText="${CurText}${ParO}$NICs${ParC}\n"
		if [ "$Mode" = "11" ] || [ "$Mode" = "21" ] ; then
			# Creating
			CurText="${CurText}para autoseleccionar el predeterminado a WAN${WanNICs}, escriba: default\n"
			if [ "$Mode" = "11" ] ; then
				CurText="${CurText}para dedicar la entrada a todos los dispositivos, escriba: *\n"
			fi
		else
			# Selecting
			CurText="${CurText}para entradas del predeterminado a WAN${WanNICs}, escriba: default\n"
			CurText="${CurText}para todas entradas de los dispositivos que sea, escriba: all\n"
			if [ "$Mode" = "10" ] ; then
				CurText="${CurText}para entradas dedicadas a todos los dispositivos, escriba: *\n"
			fi
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " default wan to-wan " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='default' ; fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar dispositivo de entrada.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar dispositivo de salida.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "." ] ; then
		printf '%s\n' "${sERROR}E: Dispositivo especificado no valido: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Dispositivo no soportado para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
		# Output requires specific target
		printf '%s\n' "${sERROR}E: Para crear una ruta no es valido el dispositivo especificado como salida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_Protocol ()
# Syntax as a sentence: Ask_Protocol $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by input Protocol
#		11 = Creating an entry by input Protocol
#		20 = Selecting current entries by output Protocol
#		21 = Creating an entry by output Protocol
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local CurText=''
	local Value="$SpecifiedValue"
	local AllowedProtocols=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		if [ "$Mode" = "10" ] ; then
			# Selecting by input
			CurText="${CurText}${cHEADING1}Escriba el protocolo de comunicacion atendido${cRESET}. Por ejemplo: tcp\n"
		else
			if [ "$Mode" = "11" ] ; then
				# Creating for input
				CurText="${CurText}${cHEADING1}Escriba el protocolo de comunicacion a atender${cRESET}. Por ejemplo: tcp\n"
			else
				# No sense to specify output protocol because it cannot be translated
				printf '%s\n' "${sERROR}E: EX_SOFTWARE. Invalid mode to ask protocol: $Mode${fRESET}" 1>&2
				LastStatus=70 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$Mode" = "11" ] || [ "$Mode" = "21" ] ; then
				# Creating
				CurText="${CurText}para dedicar la entrada a todos los protocolos, escriba: *\n"
			else
				# Selecting
				CurText="${CurText}para todas entradas de los protocolos que sea, escriba: all\n"
				if [ "$Mode" = "10" ] ; then
					CurText="${CurText}para entradas dedicadas a todos los protocolos, escriba: *\n"
				fi
			fi
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar protocolo de entrada.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar protocolo de salida.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "." ] ; then
		printf '%s\n' "${sERROR}E: Protocolo especificado no valido: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Protocolo no valido para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
		# Output requires specific target
		printf '%s\n' "${sERROR}E: Para crear una ruta no es valido el protocolo especificado de salida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" != "*" ] && [ "$Value" != "all" ] && [ "$Value" != "" ] ; then
		# Check known protocol
		AllowedProtocols="$(cat /etc/protocols 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's| .*||g' | grep -ve '^$')"
		if [ "$AllowedProtocols" != "" ] && [ "$(printf '%s\n' "$AllowedProtocols" | grep -e "^${Value}$")" = "" ] ; then
			printf '%s\n' "${sERROR}E: No se encuentra el protocolo $Value en /etc/protocols${fRESET}"
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_Ports ()
# Syntax as a sentence: Ask_Ports $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by input RPorts
#		11 = Creating an entry by input RPorts
#		20 = Selecting current entries by destination RPorts
#		21 = Creating an entry by destination RPorts
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local CurText=''
	local Value="$SpecifiedValue"
	local RPort1=''
	local RPort9=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		if [ "$Mode" = "10" ] ; then
			# Selecting by input
			CurText="${CurText}${cHEADING1}Escriba el puerto o rango atendido${cRESET}. Por ejemplo: 22-23\n"
			CurText="${CurText}para todas las entradas por los puertos que sea, escriba: all\n"
			CurText="${CurText}para entradas dedicadas a todos los puertos, escriba: *\n"
		else
			if [ "$Mode" = "11" ] ; then
				# Creating for input
				CurText="${CurText}${cHEADING1}Escriba el puerto o rango a atender${cRESET}. Por ejemplo: 22-23\n"
				CurText="${CurText}para dedicar la entrada a todos los puertos, escriba: *\n"
			else
				if [ "$Mode" = "20" ] ; then
					# Selecting by destination
					CurText="${CurText}${cHEADING1}Escriba el puerto o rango destinado${cRESET}. Por ejemplo: 22-23\n"
					CurText="${CurText}para todas las rutas hacia los puertos que sea, escriba: all\n"
				else
					if [ "$Mode" = "21" ] ; then
						# Creating for destination
						CurText="${CurText}${cHEADING1}Escriba el puerto o rango para el destino${cRESET}. Por ejemplo: 22-23\n"
						CurText="${CurText}para los mismos puertos que de entrada, escriba: =\n"
					fi
				fi
			fi
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar puerto o rango de entrada.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar puerto o rango de salida.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Value" = "." ] || [ "$Value" = "=" ] ; then
			if [ "$Mode" = "21" ] ; then
				Value='='
			else
				printf '%s\n' "${sERROR}E: Puerto especificado no valido: $Value${fRESET}" 1>&2
				LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Puerto no valido para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
#	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
#		# Destination requires specific target but... what if input port already was "*" ?
#		printf '%s\n' "E: Para crear una ruta no es valido el puerto especificado de salida: $Value" 1>&2
#		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" != "*" ] && [ "$Value" != "all" ] && [ "$Value" != "." ] && [ "$Value" != "=" ] && [ "$Value" != "" ] ; then
		# Check value format
		Value="$(printf '%s' "$Value" | tr -s ':' '-')"
		RPort1="$(printf '%s' "$Value" | cut -f 1 -d '-')"
		RPort9="$(printf '%s' "$Value" | cut -f 2 -d '-')"
		if ! Is_IntegerNr $RPort1 || ! Is_IntegerNr $RPort9 ; then
			printf '%s\n' "${sERROR}E: Formato de los numeros de puerto invalido: $Value${fRESET}" 1>&2
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ $RPort1 -lt 0 ] || [ $RPort1 -gt 65535 ] ; then
				printf '%s\n' "${sERROR}E: Primer puerto fuera del rango 0-65535: $RPort1${fRESET}" 1>&2
				LastStatus=88 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ $RPort9 -ne $RPort1 ] ; then
					if [ $RPort9 -lt 0 ] || [ $RPort9 -gt 65535 ] ; then
						printf '%s\n' "${sERROR}E: Ultimo puerto fuera del rango 0-65535: $RPort9${fRESET}" 1>&2
						LastStatus=88 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						if [ $RPort9 -lt $RPort1 ] ; then
							printf '%s\n' "${sERROR}E: Para el rango ${RPort9}-${RPort1} no se admite el orden contrario: $Value${fRESET}" 1>&2
							LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				fi
			fi
		fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_OwnIp ()
# Syntax as a sentence: Ask_OwnIp $Mode "$SpecifiedValue" OutVar $ExampleNIC
# Expected parameters:
#	$1	10 = Selecting current entries by input Ip address
#		11 = Creating an entry by input Ip address
#		20 = Selecting current entries by output Ip address
#		21 = Creating an entry by output Ip address
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional or blank) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
#	$4	(optional) Network interface to get IP addresses from, as example values
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local ExampleNIC="$4"
	local CurText=''
	local ExampleIp=''
	local Value="$SpecifiedValue"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		if [ "$ExampleNIC" = "all" ] || [ "$ExampleNIC" = "any" ] || [ "$ExampleNIC" = "_any" ] || [ "$ExampleNIC" = "*" ] ; then
			ExampleNIC=''
		fi
		ExampleIp="$(LlistarIPpropies 0 "$ExampleNIC" "^127\." | head -n 1)"
		if [ "$ExampleIp" != "" ] ; then ExampleIp=" Por ejemplo: $ExampleIp" ; fi
		if [ "$Mode" = "10" ] ; then
			# Selecting by input
			CurText="${CurText}${cHEADING1}Escriba la IP atendida de entrada${cRESET}.${ExampleIp}\n"
		else
			if [ "$Mode" = "11" ] ; then
				# Creating for input
				CurText="${CurText}${cHEADING1}Escriba la IP de entrada a atender${cRESET}.${ExampleIp}\n"
			else
				if [ "$Mode" = "20" ] ; then
					# Selecting by output
					CurText="${CurText}${cHEADING1}Escriba la IP usada como salida${cRESET}.${ExampleIp}\n"
				else
					if [ "$Mode" = "21" ] ; then
						# Creating by output
						CurText="${CurText}${cHEADING1}Escriba la IP a usar como salida${cRESET}.${ExampleIp}\n"
					fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$Mode" = "10" ] ; then
				# Selecting by input
				CurText="${CurText}para todas las entradas de las IP que sea, escriba: all\n"
				CurText="${CurText}para las rutas que admitan todas las IP de entrada, escriba: *\n"
			else
				if [ "$Mode" = "11" ] ; then
					# Creating for input
					CurText="${CurText}para dedicar la entrada a todas las IP propias, escriba: *\n"
					CurText="${CurText}para autoseleccionar la predeterminada del dispositivo $ExampleNIC escriba: default\n"
				else
					if [ "$Mode" = "20" ] ; then
						# Selecting by output
						CurText="${CurText}para todas las salidas por las IP que sea, escriba: all\n"
					else
						if [ "$Mode" = "21" ] ; then
							# Creating by output
							CurText="${CurText}para autoseleccionar la predeterminada del dispositivo $ExampleNIC escriba: default\n"
						fi
					fi
				fi
			fi
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " default " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='default' ; fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar direccion IP propia de entrada.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar direccion IP propia de salida.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "." ] ; then
		printf '%s\n' "${sERROR}E: Direccion IP especificada no valida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Direccion IP no valida para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
		# Output requires specific target
		printf '%s\n' "${sERROR}E: Para crear una ruta no es valida la IP especificada de salida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" != "*" ] && [ "$Value" != "all" ] && [ "$Value" != "default" ] && [ "$Value" != "" ] ; then
		# Check value format
		if [ "$(EsIP "$Value")" != "1" ] ; then
			if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
				printf '%s\n' "${sERROR}E: Formato de la direccion IP de entrada invalido: $Value${fRESET}" 1>&2
			else
				printf '%s\n' "${sERROR}E: Formato de la direccion IP de salida invalido: $Value${fRESET}" 1>&2
			fi
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_OthersIp ()
# Syntax as a sentence: Ask_OthersIp $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by origin Ip address
#		11 = Creating an entry for origin Ip address
#		20 = Selecting current entries by destination Ip address
#		21 = Creating an entry for destination Ip address
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional or blank) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local CurText=''
	local Value="$SpecifiedValue"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		if [ "$Mode" = "10" ] ; then
			# Selecting by origin
			CurText="${CurText}${cHEADING1}Escriba la IP atendida de origen${cRESET}.\n"
			CurText="${CurText}para las entradas que admitan todos los origenes, escriba: *\n"
			CurText="${CurText}para todas las entradas de los origenes que sea, escriba: all\n"
		else
			if [ "$Mode" = "11" ] ; then
				# Creating for origin
				CurText="${CurText}${cHEADING1}Escriba la IP de origen a atender${cRESET}.\n"
				CurText="${CurText}para dedicar la entrada a todos los origenes, escriba: *\n"
			else
				if [ "$Mode" = "20" ] ; then
					# Selecting by destination
					CurText="${CurText}${cHEADING1}Escriba la IP de destino a seleccionar${cRESET}.\n"
					CurText="${CurText}para todas las rutas hacia el destino que sea, escriba: all\n"
				else
					if [ "$Mode" = "21" ] ; then
						# Creating by destination
						CurText="${CurText}${cHEADING1}Escriba la IP de destino a donde dirigir${cRESET}.\n"
					fi
				fi
			fi
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " default " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='default' ; fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar direccion IP de origen.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar direccion IP de destino.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "." ] ; then
		printf '%s\n' "${sERROR}E: Direccion IP especificada no valida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Direccion IP no valida para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
		# Output requires specific target
		printf '%s\n' "${sERROR}E: Para crear una ruta no es valida la IP especificada de destino: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" != "*" ] && [ "$Value" != "all" ] && [ "$Value" != "default" ] && [ "$Value" != "" ] ; then
		# Check value format
		if [ "$(EsIP "$Value")" != "1" ] ; then
			if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
				printf '%s\n' "${sERROR}E: Formato de la direccion IP de origen invalido: $Value${fRESET}" 1>&2
			else
				printf '%s\n' "${sERROR}E: Formato de la direccion IP de destino invalido: $Value${fRESET}" 1>&2
			fi
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_CIDR ()
# Syntax as a sentence: Ask_CIDR $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by CIDR
#		11 = Creating an entry for CIDR
#		20 = Selecting current entries by CIDR
#		21 = Creating an entry for CIDR
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional or blank) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local CurText=''
	local Value="$SpecifiedValue"
	local ExampleLAN=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		ValorTemp1="$(PrimeraIPEquip)"
		ValorTemp2="$(MascaraEquipIP "$ValorTemp1")"
#		ValorTemp3="$(IPBaseXarxa "$ValorTemp1" "$ValorTemp2")"
		ValorTemp2="$(BitsMascara "$ValorTemp2")"
		ValorTemp3="$(IPv4NetworkAddress "${ValorTemp1}/${ValorTemp2}")"
		if [ "$ValorTemp2" != "" ] && [ "$ValorTemp3" != "" ] && [ "$ValorTemp2" != "32" ] ; then
			ExampleLAN="${ValorTemp3}/${ValorTemp2}"
		else
			ExampleLAN="192.168.1.0/24"
		fi
		if [ "$ExampleLAN" != "" ] ; then ExampleLAN=", por ejemplo $ExampleLAN " ; fi

		if [ "$Mode" = "10" ] || [ "$Mode" = "20" ] ; then
			# Selecting by CIDR
			CurText="${CurText}${cHEADING1}Escriba la red IP atendida.${cRESET}\n"
			CurText="${CurText}Local network\n"
			CurText="${CurText}${ParO}en formato CIDR${ExampleLAN}${ParC}\n"
			CurText="${CurText}para las rutas que admiten todos los origenes, escriba: *\n"
			CurText="${CurText}para todas las rutas de los origenes que sea, escriba: all\n"
		else
			# Creating for CIDR
			CurText="${CurText}${cHEADING1}Escriba la red IP a atender${cRESET}.\n"
			CurText="${CurText}Local network\n"
			CurText="${CurText}${ParO}en formato CIDR${ExampleLAN}${ParC}\n"
#			CurText="${CurText}para dedicar la ruta a todos los origenes, escriba: *\n"  # MAYBE DANGEROUS
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
	fi
	if [ "$(printf '%s' " any _any " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] ; then Value='*' ; fi
	if [ "$(printf '%s' " all " | grep -ie " $Value ")" != "" ] && [ "$Value" != "" ] && [ "$Value" != "*" ] ; then Value='all' ; fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
			printf '%s\n' "${sERROR}E: Se debe especificar red IP de origen.${fRESET}" 1>&2
		else
			printf '%s\n' "${sERROR}E: Se debe especificar red IP de destino.${fRESET}" 1>&2
		fi
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "." ] ; then
		printf '%s\n' "${sERROR}E: Red IP especificada no valida: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "all" ] && [ "$Mode" != "10" ] && [ "$Mode" != "20" ] ; then
		# "all" is only valid for selection
		printf '%s\n' "${sERROR}E: Red IP no valida para crear rutas: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" = "*" ] && [ "$Mode" = "21" ] ; then
		# Output requires specific target
		printf '%s\n' "${sERROR}E: Para crear una ruta no es valida la red IP especificada de destino: $Value${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Value" != "*" ] && [ "$Value" != "all" ] && [ "$Value" != "default" ] && [ "$Value" != "" ] ; then
		# Check value format
		if [ "$(EsCIDR "$Value")" != "1" ] ; then
			if [ "$Mode" = "10" ] || [ "$Mode" = "11" ] ; then
				printf '%s\n' "${sERROR}E: Formato de la red IP de origen invalido: $Value${fRESET}" 1>&2
			else
				printf '%s\n' "${sERROR}E: Formato de la red IP de destino invalido: $Value${fRESET}" 1>&2
			fi
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

Ask_Comment ()
# Syntax as a sentence: Ask_Comment $Mode "$SpecifiedValue" OutVar
# Expected parameters:
#	$1	10 = Selecting current entries by comment
#		11 = Creating an entry
#		20 = Selecting current entries by comment
#		21 = Creating an entry
#	$2	(optional or blank) Value already specified by user or caller. It will be verified and parsed.
#	$3	(optional or blank) Variable name which to put resulting value to. If not specified, result value will be written to stdout.
{
	local Mode="$1"
	local SpecifiedValue="$2"
	local byref_OutVar="$3"
	local CurText=''
	local Value="$SpecifiedValue"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SpecifiedValue" = "" ] ; then
		if [ "$Mode" = "10" ] || [ "$Mode" = "20" ] ; then
			# Selecting by comment
			CurText="${CurText}${cHEADING1}Escriba texto para seleccionar rutas que lo contengan${cRESET}.\n"
		else
			printf '%s\n' "${cHEADING1}Puede escribir un nombre o comentario para esta ruta${cRESET}"
			printf '%s\n' "${ParO}opcional${ParC}:"
		fi
		if [ "$byref_OutVar" != "" ] ; then
			# stdout
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g'
		else
			# stderr to not interfere result value
			printf '%s' "$CurText" | sed -e 's|\\n|\n|g' 1>&2
		fi
		read Value
		Value="$(printf '%s\n' "$Value" | tr -s "\"" "'")"  #'
	fi
	if [ "$byref_OutVar" != "" ] ; then
		if [ "$Value" != "" ] ; then
			eval "$3=\"\${Value}\""
		else
			eval "$3="
		fi
	else
		if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	fi
	return $StatusCode
}

nat_export ()
{
	local ListaOrigen=""
	local LineaActual=""
	local DatosActual=""
	local RedLocal=""
	local SalidaDev=""
	local SalidaIp=""
	local Comment=""
	local Dir_Path=""
	local ContenidoIptables=""
	local ActualYaHabilitada=""
	local Id=""
	
	printf '%s\n' "# NAT configuration based in iptables (exported by durruter wizard)"
	ContenidoIptables="$($ITScmd)"
#	ListaOrigen="$(printf '%s\n' "$ContenidoIptables" | grep -ie "POSTROUTING" | grep -E "( -s| --source)" | grep -iE "( -j SNAT| --jump SNAT)" | grep -vE "(/32|durruter-dnat/)")"
	ListaOrigen="$(printf '%s\n' "$ContenidoIptables" | grep -ie "POSTROUTING" | grep -E "( -s| --source)" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve 'durruter-dnat/')"
	IFS="$(printf "\n\b")" ; for LineaActual in $ListaOrigen ; do unset IFS
		DatosActual="$(printf '%s\n' "$LineaActual" | sed -e "s/ -/\n-/g")"
		RedLocal="$(printf '%s\n' "$DatosActual" | grep -E "(^-s |^--source )" | cut -f 2- -d " ")"
		if [ "$RedLocal" = "" ] ; then RedLocal='*' ; fi
		SalidaDev="$(printf '%s\n' "$DatosActual" | grep -E "(^-o |^--out-interface )" | cut -f 2- -d " ")"
		if [ "$SalidaDev" = "" ] ; then SalidaDev='*' ; fi
		SalidaIp="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-source )" | cut -f 2- -d " ")"
		if [ "$SalidaIp" = "" ] ; then SalidaIp='*' ; fi
		if [ -f "${SystemConfigDir}/${SalidaDev}/nat/$(echo $RedLocal | cut -f 1 -d /)/wan-ip.txt" ] ; then
			SalidaIp="$(cat "${SystemConfigDir}/${SalidaDev}/nat/$(echo $RedLocal | cut -f 1 -d /)/wan-ip.txt" | head -n 1)"
		fi
		Comment="$(printf '%s\n' "$DatosActual" | grep -E "(^--comment )" | cut -f 2- -d " ")"
		if [ "$(printf '%s\n' "$Comment" | grep -e "^\".*\"$")" != "" ] ; then
			# Descartar comillas delimitadoras
			Comment="$(printf '%s\n' "$Comment" | sed -e "s/^\"//g" | sed -e "s/\"$//g")"
		fi
		if [ "$(printf '%s\n' "$Comment" | grep -ie "durruter-nat/.*/")" != "" ] ; then
			# Descartar identificador interno
			Comment="$(printf '%s\n' "$Comment" | cut -f 3- -d '/')"
		fi
		printf '%s\n' "nat create $(QuoteAsNeeded2 "$RedLocal" "$SalidaDev" "$SalidaIp") \"$Comment\""
	done
	FilePathsList="$(find "${SystemConfigDir}/"*"/nat/"*"/lan-mask.txt" 2>/dev/null)"
	IFS="$(printf "\n\b")" ; for CurDirLine in $FilePathsList ; do unset IFS
		ActualYaHabilitada=""
		RedLocal="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 2 | head -n 1)"
		RedLocal="${RedLocal}/$(cat "$CurDirLine")"
		if [ "$RedLocal" = "_any" ] ; then RedLocal='*' ; fi
		Dir_Path="$(dirname "$CurDirLine")"
		Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
		if [ "$Id" != "" ] ; then
			if [ "$(printf '%s\n' "$ContenidoIptables" | grep -e "durruter-nat/${Id}/")" != "" ] ; then
				ActualYaHabilitada="1"
			fi
		else
			if [ "$(OneNatAlreadyEnabled "$RedLocal")" != "" ] ; then
				ActualYaHabilitada="1"
			fi
		fi
		if [ "$ActualYaHabilitada" != "1" ] ; then
			SalidaDev="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 4 | head -n 1)"
			if [ "$SalidaDev" = "_any" ] ; then SalidaDev="'*'" ; fi
			if [ "$SalidaDev" = "." ] || [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $SalidaDev ")" != "" ] ; then
				Selected_OutDev='to-wan'
			fi
			SalidaIp="$(cat "${Dir_Path}/wan-ip.txt" 2>/dev/null)"
			if [ "$SalidaIp" = "_any" ] ; then SalidaIp="'*'" ; fi
			if [ "$SalidaIp" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $SalidaIp ")" != "" ] ; then
				IpWan='default'
			fi
			Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null | tr -s '"' "'")"
#			printf '%s\n' "nat create $RedLocal $SalidaDev $SalidaIp \"$Comment\""
			printf '%s\n' "nat create $(QuoteAsNeeded2 "$RedLocal" "$SalidaDev" "$SalidaIp") \"$Comment\""
			printf '%s\n' "nat disable $RedLocal"
		fi
	done
	ContenidoIptables="$($ITScmd | grep -e "-j REJECT")"
	if [ "$ContenidoIptables" != "" ] ; then
		# Para manifestar reglas que pueden interferir:
		printf '%s\n' ""
		printf '%s\n' "$ContenidoIptables" | sed -e 's/^/# /g'
	fi
}

nat_import ()
{
	local RutaFichero="$1"
	local TempScript=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$RutaFichero" != "" ] ; then
		if [ -f "$RutaFichero" ] ; then
			if [ "$(cat "$RutaFichero" | grep -e '^nat create ')" != "" ] ; then
				TempScript="$(mktemp)"
				chmod u=rwx,go= "$TempScript"
				printf '%s\n' '#!/bin/sh' > "$TempScript"
				printf '%s\n' 'LastStatus=0' >> "$TempScript"
				printf '%s\n' 'StatusCode=0' >> "$TempScript"
				cat "$RutaFichero" | grep -e '^nat ' | sed -e "s|^|\"${MeExecutable}\" |g" | sed -e 's/$/\nLastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi/g' >> "$TempScript"
				printf '%s\n' 'exit $StatusCode' >> "$TempScript"
				"$TempScript"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				rm "$TempScript"
			else
				printf '%s\n' "${sWARN}W: El fichero no contiene registros NAT.${fRESET}"
			fi
		else
			printf '%s\n' "${sERROR}E: Fichero no encontrado para importar.${fRESET}"
			printf '%s\n' "          $RutaFichero"
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "${sERROR}E: Hay que especificar el fichero a importar.${fRESET}"
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

nat_list ()
{
	local ListaOrigen=""
	local LineaActual=""
	local DatosActual=""
	local RedLocal=""
	local SalidaDev=""
	local SalidaIp=""
	local Comment=""
	local DirPreparacionListado=""
	local ClaveOrden=""
	local Dir_Path=""
	local ContenidoIptables=""
	local ActualYaHabilitada=""
	local Id=""
	local CurSaved=''
	local CurEnabled=''
	
	if [ "$FastLists" != "1" ] ; then
		DirPreparacionListado="${DirTemp}/durruter-nat-list.$$"
		mkdir -p "$DirPreparacionListado"
		chmod u=rwX,go= "$DirPreparacionListado"
		if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
			ListadoEncabezados "$DirPreparacionListado" "A" "ACTIVA" "RED_DE_ORIGEN" "D.SALIDA" "IP_SALIDA" "COMENTARIO"
		else
			ListadoEncabezados "$DirPreparacionListado" "A" "ACTIVE" "SOURCE_NET" "OUT_INTF." "WAN_IP" "COMMENT"
		fi
	else
		printf '%s\n' "ACTIVE	LOCAL_SOURCE_NET	OUT_INTF.	WAN_IP	COMMENT"
	fi
	ContenidoIptables="$($ITScmd)"
#	ListaOrigen="$(printf '%s\n' "$ContenidoIptables" | grep -ie 'POSTROUTING' | grep -e ' -s' -e ' --source' | grep -ie ' -j SNAT' -ie ' --jump SNAT' | grep -ve '/32' -ve 'durruter-dnat')"
#	ListaOrigen="$(printf '%s\n' "$ContenidoIptables" | grep -ie 'POSTROUTING' | grep -e ' -s' -e ' --source' | grep -ie ' -j SNAT' -ie ' --jump SNAT' | grep -ve 'durruter-dnat')"
	ListaOrigen="$(printf '%s\n' "$ContenidoIptables" | grep -ie 'FORWARD' | grep -e ' -s' -e ' --source' | grep -ie ' -j ACCEPT' -ie ' --jump ACCEPT' | grep -ve 'durruter-dnat')"
	IFS="$(printf "\n\b")" ; for LineaActual in $ListaOrigen ; do unset IFS
		DatosActual="$(printf '%s\n' "$LineaActual" | sed -e "s/ -/\n-/g")"
		RedLocal="$(printf '%s\n' "$DatosActual" | grep -E "(^-s |^--source )" | cut -f 2- -d " ")"
		DatosActual="$(printf '%s\n' "$ContenidoIptables" | grep -ie 'POSTROUTING' | grep -e " -s $RedLocal" -e " --source $RedLocal" | grep -ve 'durruter-dnat' | head -n 1)"
		DatosActual="$(printf '%s\n' "$DatosActual" | sed -e "s/ -/\n-/g")"
		SalidaDev="$(printf '%s\n' "$DatosActual" | grep -E "(^-o |^--out-interface )" | cut -f 2- -d " ")"
		SalidaIp="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-source )" | cut -f 2- -d " ")"
		Comment="$(printf '%s\n' "$DatosActual" | grep -E "(^--comment )" | cut -f 2- -d " ")"
		Dir_RDev="$Case_RDev"
		if [ "$Dir_RDev" = "*" ] ; then Dir_RDev='_any' ; fi
		Dir_RedLocal="$(printf '%s\n' "$RedLocal" | cut -f 1 -d '/')"
		if [ "$Dir_RedLocal" = "*" ] ; then Dir_RedLocal='_any' ; fi
		Dir_Path="${SystemConfigDir}/${Dir_RDev}/nat/${Dir_RedLocal}"
		if [ -d "$Dir_Path" ] ; then
			CurSaved='Yes'
			CurEnabled='YES'
		else
			CurSaved='No'
			CurEnabled='yes'
		fi
		if [ "$IptablesMetadata" != "1" ] && [ "$Comment" = "" ] && [ -f "${Dir_Path}/description.txt" ] && [ $(cat "${Dir_Path}/description.txt" | wc -l) -eq 1 ] ; then
			Comment="$(cat "${Dir_Path}/description.txt")"
		fi
		if [ "$(printf '%s\n' "$Comment" | grep -e "^\".*\"$")" != "" ] ; then
			# Descartar comillas delimitadoras
			Comment="$(printf '%s\n' "$Comment" | sed -e "s/^\"//g" | sed -e "s/\"$//g")"
		fi
		if [ "$(printf '%s\n' "$Comment" | grep -ie "durruter-nat/.*/")" != "" ] ; then
			# Descartar identificador interno
			Comment="$(printf '%s\n' "$Comment" | cut -f 3- -d '/')"
		fi
		if [ "$FastLists" != "1" ] ; then
			ClaveOrden="$RedLocal"
			ListadoNuevoRegistro "$DirPreparacionListado" "$ClaveOrden" "$CurEnabled" "$RedLocal" "$SalidaDev" "$SalidaIp" "$Comment"
		else
			printf '%s\n' "$CurEnabled	$RedLocal	$SalidaDev	$SalidaIp	$Comment"
		fi
	done
	FilePathsList="$(find "${SystemConfigDir}/"*"/nat/"*"/lan-mask.txt" 2>/dev/null)"
	IFS="$(printf "\n\b")" ; for CurDirLine in $FilePathsList ; do unset IFS
		ActualYaHabilitada=""
		RedLocal="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 2 | head -n 1)"
		RedLocal="${RedLocal}/$(cat "$CurDirLine")"
		Dir_Path="$(dirname "$CurDirLine")"
		Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
		if [ "$Id" != "" ] ; then
			if [ "$(printf '%s\n' "$ContenidoIptables" | grep -e "durruter-nat/${Id}/")" != "" ] ; then
				ActualYaHabilitada="1"
			fi
		else
			if [ "$(OneNatAlreadyEnabled "$RedLocal")" != "" ] ; then
				ActualYaHabilitada="1"
			fi
		fi
		if [ "$ActualYaHabilitada" != "1" ] ; then
			Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null)"
			Comment="$(printf '%s\n' "$Comment" | sed -e "s/\"/\'/g")"
			SalidaIp="$(cat "${Dir_Path}/wan-ip.txt" 2>/dev/null)"
			SalidaDev="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 4 | head -n 1)"
			if [ "$FastLists" != "1" ] ; then
				ClaveOrden="$RedLocal"
				ListadoNuevoRegistro "$DirPreparacionListado" "$ClaveOrden" "No" "$RedLocal" "$SalidaDev" "$SalidaIp" "$Comment"
			else
				printf '%s\n' "No	$RedLocal	$SalidaDev	$SalidaIp	$Comment"
			fi
		fi
	done
	if [ "$FastLists" != "1" ] ; then
		ListadoMostrar "$DirPreparacionListado" "   " "a"
		rm -R "$DirPreparacionListado"
	fi
	ContenidoIptables="$($ITScmd | grep -e "-j REJECT")"
	if [ "$ContenidoIptables" != "" ] ; then
		# Para manifestar reglas que pueden interferir:
		printf '%s\n' ""
		printf '%s\n' "$ContenidoIptables"
	fi
}

nat_lista ()
{
	nat_list "$@"
	return $?
}

nat_listar ()
{
	nat_list "$@"
	return $?
}

nat_ver ()
{
	nat_list "$@"
	return $?
}

nat_create ()
{
	local RedLocal="$1"
	local SalidaDev="$2"
	local SalidaIp="$3"
	local Selected_Comment="$4"
	local EffectiveDev=""
	local EffectiveIp=""
	local EstadoPrevio=""
	local LastStatus=0
	local StatusCode=0
	
	# Compatibility with versions prior to 2.3 :
	if [ "$SalidaDev" = "." ] ; then SalidaDev='default' ; fi
	if [ "$SalidaIp" = "." ] ; then SalidaIp='default' ; fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_CIDR 11 "$RedLocal" RedLocal
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_NIC 21 "$SalidaDev" SalidaDev
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OwnIp 21 "$SalidaIp" SalidaIp "$SalidaDev"		
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Comment 11 "$Selected_Comment" Selected_Comment
	fi
	
	if [ $StatusCode -eq 0 ] ; then
		Id="$(NewIdForEntry)"
		if [ "$(OneNatAlreadyEnabled "$RedLocal")" = "" ] ; then
			$ITcmd -L >/dev/null 2>&1	# Some iptables implementations load iptables on first call of some functions
			if [ "$(lsmod 2>/dev/null | grep -e 'iptable' -e 'ip.table')" = "" ] ; then
				printf '%s\n' "${sWARN}W: Kernel module for iptables doesn't seem to be loaded. iptables modules need to be loaded so $ServiceName don't fail.${fRESET}" 1>&2
			fi
#			if [ "$IptablesMetadata" = "1" ] ; then
#				$ITcmd --table nat --append POSTROUTING --source $RedLocal --out-interface $SalidaDev --jump SNAT --to-source $SalidaIp -m comment --comment "durruter-nat/${Id}/${Selected_Comment}"
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table nat --append POSTROUTING --out-interface $SalidaDev --jump SNAT --to-source $SalidaIp -m comment --comment "durruter-nat/${Id}/${Selected_Comment}"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table filter --append FORWARD --source $RedLocal --jump ACCEPT -m comment --comment "durruter-nat/${Id}/${Selected_Comment}"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table filter --append FORWARD --destination $RedLocal --jump ACCEPT -m comment --comment "durruter-nat/${Id}/${Selected_Comment}"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#			else
#				$ITcmd --table nat --append POSTROUTING --source $RedLocal --out-interface $SalidaDev --jump SNAT --to-source $SalidaIp
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table nat --append POSTROUTING --out-interface $SalidaDev --jump SNAT --to-source $SalidaIp
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table filter --append FORWARD --source $RedLocal --jump ACCEPT
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#				if [ $StatusCode -eq 0 ] ; then
#					$ITcmd --table filter --append FORWARD --destination $RedLocal --jump ACCEPT
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				fi
#			fi
			CurCommand="$ITcmd --table nat --append POSTROUTING"
			if [ "$RedLocal" != "" ] && [ "$RedLocal" != "*" ] && [ "$RedLocal" != "_any" ] ; then CurCommand="$CurCommand --source $RedLocal" ; fi

			EffectiveDev="$(Effective_NIC "$SalidaDev")"
			if [ "$EffectiveDev" != "" ] && [ "$EffectiveDev" != "*" ] ; then
				CurCommand="$CurCommand --out-interface $EffectiveDev"
			fi
			if [ "$SalidaDev" = "default" ] && [ "$EffectiveDev" = "" ] ; then
				printf '%s\n' "${sERROR}E: No se detecto por que dispositivo se sale a otras redes WAN/Internet.${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			CurCommand="$CurCommand --jump SNAT"
			
#			if [ $StatusCode -eq 0 ] ; then
#				CurCommand="$ITcmd --table nat --append POSTROUTING"
#				if [ "$SalidaDev" != "" ] && [ "$SalidaDev" != "*" ] && [ "$SalidaDev" != "_any" ] ; then CurCommand="$CurCommand --out-interface $SalidaDev" ; fi
#				CurCommand="$CurCommand --jump SNAT"
#				if [ "$SalidaIp" != "" ] && [ "$SalidaIp" != "*" ] && [ "$SalidaIp" != "_any" ] ; then CurCommand="$CurCommand --to-source $SalidaIp" ; fi
#				if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Id}/${Selected_Comment}\"" ; fi
#				eval $CurCommand
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#				if [ $LastStatus -ne 0 ] ; then
#					printf '%s\n' "Command was: $CurCommand" 1>&2
#				fi
#			fi
			
			EffectiveIp="$(Effective_IP "$EffectiveDev" "$SalidaIp")"
			if [ "$EffectiveIp" != "" ] && [ "$EffectiveIp" != "*" ] ; then
#				CurCommand="$CurCommand --to $EffectiveIp"
				CurCommand="$CurCommand --to-source $EffectiveIp"
			fi
			if [ "$SalidaIp" = "default" ] && [ "$EffectiveDev" = "" ] ; then
				printf '%s\n' "${sERROR}E: No se detecto la direccion IP predeterminada del dispositivo $EffectiveDev${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Id}/${Selected_Comment}\"" ; fi
			eval $CurCommand
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "Command was: $CurCommand" 1>&2
			fi

			if [ $StatusCode -eq 0 ] ; then
				CurCommand="$ITcmd --table filter --append FORWARD"
				if [ "$RedLocal" != "" ] && [ "$RedLocal" != "*" ] && [ "$RedLocal" != "_any" ] ; then CurCommand="$CurCommand --source $RedLocal" ; fi
				CurCommand="$CurCommand --jump ACCEPT"
				if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Id}/${Selected_Comment}\"" ; fi
				eval $CurCommand
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] ; then
					printf '%s\n' "Command was: $CurCommand" 1>&2
				fi
			fi
			
			if [ $StatusCode -eq 0 ] ; then
				CurCommand="$ITcmd --table filter --append FORWARD"
				if [ "$RedLocal" != "" ] && [ "$RedLocal" != "*" ] && [ "$RedLocal" != "_any" ] ; then CurCommand="$CurCommand --destination $RedLocal" ; fi
				CurCommand="$CurCommand --jump ACCEPT"
				if [ "$IptablesMetadata" = "1" ] ; then CurCommand="$CurCommand -m comment --comment \"durruter-nat/${Id}/${Selected_Comment}\"" ; fi
				eval $CurCommand
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] ; then
					printf '%s\n' "Command was: $CurCommand" 1>&2
				fi
			fi
			
			if [ $StatusCode -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
				if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
					printf '%s\n' "Salida NAT habilitada para la red $RedLocal"
				else
					printf '%s\n' "NAT output enabled for the net $RedLocal"
				fi
			fi
			sysctl -w net/ipv4/ip_forward=1 > /dev/null
		else
			EstadoPrevio="${EstadoPrevio}A"
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(NatYaGuardada "$RedLocal")" != "1" ] ; then
				GuardarNat "$SalidaDev" "$RedLocal" "$SalidaIp" "$Selected_Comment" "$Id"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
						printf '%s\n' "Salida NAT guardada para permitir la carga automatica."
					else
						printf '%s\n' "NAT output saved to allow automatic loading."
					fi
				fi
			else
				EstadoPrevio="${EstadoPrevio}G"
			fi
			ContenidoIptables="$($ITScmd | grep -e "-j REJECT")"
			if [ "$ContenidoIptables" != "" ] ; then
				# Para manifestar reglas que pueden interferir:
				printf '%s\n' ""
				printf '%s\n' "$ContenidoIptables"
				if [ -f /etc/sysconfig/iptables ] ; then
					ContenidoIptables="$(cat /etc/sysconfig/iptables | grep -e "-j REJECT")"
					if [ "$ContenidoIptables" != "" ] ; then
						printf '%s\n' ""
						printf '%s\n' "/etc/sysconfig/iptables"
						printf '%s\n' "$ContenidoIptables"
					fi
				fi
			fi
		fi
	fi
	if [ "$EstadoPrevio" = "AG" ] && [ "$ShowWarnings" != "0" ] ; then
		printf '%s\n' "${sWARN}W: Una salida NAT para la red $RedLocal ya estaba activa y guardada. Se evita la duplicidad.${fRESET}"
	fi
	return $StatusCode
}

nat_crear ()
{
	nat_crear "$@"
	return $?
}

nat_desactivar ()
# Sintaxis como instrucción: nat_desactivar "$RedLocal"
# Descripción:
#	Elimina las líneas cargadas en iptables para esta salida NAT.
{
	local RedLocal="$1"
	local IpRed=""
	local ListaOrigen=""
	local LineaActual=""
	local DatosActual=""
	local RedActual=""
	local DevActual=""
	local IpActual=""
	local Comment=""
	local LineaEliminacion=""
	local ListaAdicionales=""
	local LineaAdicional=""
	local LineaTemp=""
	local NrAdicionales=0
	local Descargadores=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_CIDR 10 "$RedLocal" RedLocal
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		IpRed="$(printf '%s' "$RedLocal" | cut -f 1 -d '/')"
		if [ "$IpRed" = "*" ] ; then
			Descargadores="$(ls -1 /etc/durruter/*/nat/_any/if-down_fast.sh)"
		else
			if [ "$IpRed" = "all" ] ; then
				Descargadores="$(ls -1 /etc/durruter/*/nat/*/if-down_fast.sh)"
			else
				Descargadores="$(ls -1 /etc/durruter/*/nat/${IpRed}/if-down_fast.sh)"
			fi
		fi
		if [ "$Descargadores" != "" ] ; then
			IFS="$(printf "\n\b")" ; for LineaActual in $Descargadores ; do unset IFS
				# Los descargadores con identificador no requieren que se especifique dispositivo e IP
				LogProgram 4 "$LineaActual"
				eval "$LineaActual"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Buscamos tambien salidas NAT que no sean de durruter.
#		ListaOrigen="$($ITScmd | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -s | --source )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "/32$" -ve "/32 " -ve "durruter-dnat/")"
		ListaOrigen="$($ITScmd | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -s | --source )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "durruter-dnat/")"
		IFS="$(printf "\n\b")" ; for LineaActual in $ListaOrigen ; do unset IFS
			DatosActual="$(printf '%s\n' "$LineaActual" | sed -e "s/ -/\n-/g")"
			RedActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-s |^--source )" | cut -f 2- -d " ")"
			DevActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-o |^--out-interface )" | cut -f 2- -d " ")"
			IpActual="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-source )" | cut -f 2- -d " ")"
			Comment="$(printf '%s\n' "$DatosActual" | grep -E "(^--comment )" | cut -f 2- -d " ")"
			if [ "$(printf '%s\n' "$Comment" | grep -e "^\".*\"$")" != "" ] ; then
				# Descartar comillas delimitadoras
				Comment="$(printf '%s\n' "$Comment" | sed -e "s/^\"//g" | sed -e "s/\"$//g")"
			fi
			if [ "$RedLocal" = "$RedActual" ] || [ "$RedLocal" = "all" ] ; then
				LineaEliminacion="$(printf '%s\n' "$LineaActual" | sed -e "s/^-A //g")"
#				EjecutarLinea "$ITcmd --table nat --delete $LineaEliminacion"
				eval $ITcmd --table nat --delete $LineaEliminacion
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] ; then
					printf '%s\n' "Command was: $ITcmd --table nat --delete $LineaEliminacion" 1>&2
				fi
#				LineaTemp="$($ITScmd | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -ie ' -j SNAT' -ie ' --jump SNAT' | grep -ve "/32$" -ve "/32 " -ve "durruter-dnat/")"
				LineaTemp="$($ITScmd | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -ie ' -j SNAT' -ie ' --jump SNAT' | grep -ve "durruter-dnat/")"
#				ListaAdicionales="$($ITScmd --table nat | grep -ie "POSTROUTING | tr -s '\t' ' '" | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "/32$" -ve "/32 " -ve "durruter-dnat/" | grep -e "$Comment")"
				ListaAdicionales="$($ITScmd --table nat | grep -ie "POSTROUTING | tr -s '\t' ' '" | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "durruter-dnat/" | grep -e "$Comment")"
				if [ "$ListaAdicionales" = "" ] ; then
#					ListaAdicionales="$($ITScmd --table nat | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "/32$" -ve "/32 " -ve "durruter-dnat/")"
					ListaAdicionales="$($ITScmd --table nat | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${DevActual}$| --out-interface ${DevActual}$| -o ${DevActual} | --out-interface ${DevActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -ve "durruter-dnat/")"
				fi
				IFS="$(printf "\n\b")" ; for LineaAdicional in $ListaAdicionales ; do unset IFS
					NrAdicionales=$(($NrAdicionales + 1))
				done
				if [ $StatusCode -eq 0 ] && [ $NrAdicionales -gt 0 ] ; then
					if [ "$LineaTemp" = "" ] || [ $NrAdicionales -gt 1 ] ; then
						# Si hay retornos (to-source) de otras salidas, sólo eliminamos si hay duplicidad.
						LineaEliminacion="$(printf '%s\n' "$ListaAdicionales" | head -n 1 | sed -e "s/^-A //g")"
#						EjecutarLinea "$ITcmd --table nat --delete $LineaEliminacion"
						eval $ITcmd --table nat --delete $LineaEliminacion
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] ; then
							printf '%s\n' "Command was: $ITcmd --table nat --delete $LineaEliminacion" 1>&2
						fi
					fi
				fi
				ListaAdicionales="$($ITScmd --table filter | grep -ie "FORWARD" | grep -E "( -s $RedActual| --source $RedActual)" | grep -iE "( -j ACCEPT| --jump ACCEPT)" | grep -e "$Comment")"
				if [ "$ListaAdicionales" = "" ] ; then
					ListaAdicionales="$($ITScmd --table filter | grep -ie "FORWARD" | grep -E "( -s $RedActual| --source $RedActual)" | grep -iE "( -j ACCEPT| --jump ACCEPT)")"
				fi
				IFS="$(printf "\n\b")" ; for LineaAdicional in $ListaAdicionales ; do unset IFS
					NrAdicionales=$(($NrAdicionales + 1))
				done
				if [ $StatusCode -eq 0 ] && [ $NrAdicionales -gt 0 ] ; then
					if [ "$LineaTemp" = "" ] || [ $NrAdicionales -gt 1 ] ; then
						# Si hay aceptaciones de otras salidas, sólo eliminamos si hay duplicidad.
						LineaEliminacion="$(printf '%s\n' "$ListaAdicionales" | head -n 1 | sed -e "s/^-A //g")"
#						EjecutarLinea "$ITcmd --table filter --delete $LineaEliminacion"
						eval $ITcmd --table filter --delete $LineaEliminacion
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] ; then
							printf '%s\n' "Command was: $ITcmd --table filter --delete $LineaEliminacion" 1>&2
						fi
					fi
				fi
				ListaAdicionales="$($ITScmd --table filter | grep -ie "FORWARD" | grep -E "( -d $RedActual| --destination $RedActual)" | grep -iE "( -j ACCEPT| --jump ACCEPT)" | grep -e "$Comment")"
				if [ "$ListaAdicionales" = "" ] ; then
					ListaAdicionales="$($ITScmd --table filter | grep -ie "FORWARD" | grep -E "( -d $RedActual| --destination $RedActual)" | grep -iE "( -j ACCEPT| --jump ACCEPT)")"
				fi
				IFS="$(printf "\n\b")" ; for LineaAdicional in $ListaAdicionales ; do unset IFS
					NrAdicionales=$(($NrAdicionales + 1))
				done
				if [ $StatusCode -eq 0 ] && [ $NrAdicionales -gt 0 ] ; then
					if [ "$LineaTemp" = "" ] || [ $NrAdicionales -gt 1 ] ; then
						# Si hay aceptaciones de otras salidas, sólo eliminamos si hay duplicidad.
						LineaEliminacion="$(printf '%s\n' "$ListaAdicionales" | head -n 1 | sed -e "s/^-A //g")"
#						EjecutarLinea "$ITcmd --table filter --delete $LineaEliminacion"
						eval $ITcmd --table filter --delete $LineaEliminacion
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] ; then
							printf '%s\n' "Command was: $ITcmd --table filter --delete $LineaEliminacion" 1>&2
						fi
					fi
				fi
			fi
		done
		if [ "$Descargadores" = "" ] && [ "$LineaEliminacion" = "" ] ; then
			printf '%s\n' "${sERROR}E: No se encontro en la tabla nat una salida para las redes: $RedLocal${fRESET}" 1>&2
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

nat_disable ()
{
	nat_desactivar "$@"
	return $?
}

nat_deshabilitar ()
{
	nat_desactivar "$@"
	return $?
}

nat_inhabilitar ()
{
	nat_desactivar "$@"
	return $?
}

nat_enable ()
# Sintaxis como instrucción: nat_enable "$RedLocal"
# Descripción:
#	Re/establece una salida NAT en iptables, según los datos guardados.
{
	local RedLocal="$1"
	local CargadorActual=""
	local Cargadores=""
	local DispositivoActual=""
	local IPSalidaActual=""
	local RedActual=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_CIDR 10 "$RedLocal" RedLocal
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		IpRed="$(printf '%s' "$RedLocal" | cut -f 1 -d '/')"
		if [ "$IpRed" = "*" ] ; then
			Cargadores="$(ls -1 /etc/durruter/*/nat/_any/if-up_fast.sh)"
		else
			if [ "$IpRed" = "all" ] ; then
				Cargadores="$(ls -1 /etc/durruter/*/nat/*/if-up_fast.sh)"
			else
				Cargadores="$(ls -1 /etc/durruter/*/nat/${IpRed}/if-up_fast.sh)"
			fi
		fi
		if [ "$Cargadores" != "" ] ; then
			IFS="$(printf "\n\b")" ; for CargadorActual in $Cargadores ; do unset IFS
				DispositivoActual="$(printf '%s\n' "$CargadorActual" | tr -s '/' '\n' | tail -n 4 | head -n 1)"
				if [ "$DispositivoActual" = "_any" ] ; then DispositivoActual='*' ; fi
				if [ "$DispositivoActual" != "*" ] ; then
					if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $DispositivoActual ")" != "" ] ; then
						DispositivoActual="$(DefaultNICs | head -n 1)"
					fi
				fi
				IPSalidaActual=""
				if [ -f "$(dirname "$CargadorActual")/wan-ip.txt)" ] ; then
					IPSalidaActual="$(cat "$(dirname "$CargadorActual")/wan-ip.txt)")"
				fi
				if [ "$IPSalidaActual" != "*" ] && [ "$(EsIP "$IPSalidaActual")" != "1" ] ; then
					IPSalidaActual="$(LlistarIPpropies 1 "$DispositivoActual")"
				fi
				RedActual="$(printf '%s\n' "$CargadorActual" | tr -s '/' '\n' | tail -n 2 | head -n 1)/"
				if [ -f "$(dirname "$CargadorActual")/lan-mask.txt" ] ; then
					RedActual="${RedActual}$(cat "$(dirname "$CargadorActual")/lan-mask.txt")"
				fi
				nat_desactivar "$RedActual" >/dev/null
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				LogProgram 4 "$CargadorActual $DispositivoActual $IPSalidaActual"
				"$CargadorActual" "$DispositivoActual" "$IPSalidaActual"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] ; then
					printf '%s\n' "Loader script was: $CargadorActual" 1>&2
				fi
			done
		else
			printf '%s\n' "${sERROR}E: No hay guardada ninguna salida NAT para las redes $RedLocal${fRESET}" 1>&2
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

nat_habilitar ()
{
	nat_enable "$@"
	return $?
}

nat_activar ()
{
	nat_enable "$@"
	return $?
}

nat_remove ()
{
	local RedLocal="$1"
	local NetIp=""
	local RespuestaDev=""
	local RespuestaIp=""
	local ValorTemp1=""
	local ValorTemp2=""
	local ValorTemp3=""
	local SalidasEncontradas=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_CIDR 10 "$RedLocal" RedLocal
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	
	if [ "$(printf '%s\n' " all todo toda todos todas " | grep -ie " $RedLocal ")" != "" ] && [ "$RedLocal" != "" ] ; then
#		RedLocal=""
		IpRed=""
		nat_desactivar "$RedLocal"
		if [ "$ShowInfo" != "0" ] ; then
			printf '%s\n' "Eliminando salidas NAT:"
			ls -1d "${SystemConfigDir}/"*"/nat/"*"/" 2>/dev/null | cut -f 4-6 -d '/' | sed -e "s|^|${Tab}|g"
		fi
		rm -r "${SystemConfigDir}/"*"/nat/"*
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		RebuildNatLoaders '' ''
	else
		if [ "$(EsCIDR "$RedLocal")" = "1" ] ; then
			nat_desactivar "$RedLocal"
			IpRed="$(printf '%s\n' "$RedLocal" | cut -f 1 -d '/')"
			if [ "$ShowInfo" != "0" ] ; then
				ls -1d  "${SystemConfigDir}/"*"/nat/${IpRed}" 2>/dev/null | cut -f 4- -d '/'
			fi
			rm -r "${SystemConfigDir}/"*"/nat/${IpRed}"
			RebuildNatLoaders '' "$IpRed"
		else
			printf '%s\n' "${sERROR}E: La especificacion de red $RedLocal no parece valida.${fRESET}" 1>&2
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

nat_delete ()
{
	nat_remove "$@"
	return $?
}

nat_eliminar ()
{
	nat_remove "$@"
	return $?
}

nat ()
{
	local AccionLlamada="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
				printf '%s\n' "Acciones para dar salida a red externa:"
				printf '%s\n' ""
#				printf '%s\n' "	nat listar      (Ver las salidas NAT existentes)"
#				printf '%s\n' "	nat crear       (Agregar una salida NAT)"
#				printf '%s\n' "	nat eliminar    (Eliminar una de las salidas NAT)"
#				printf '%s\n' "	nat activar     (Habilitar una salida NAT guardada)"
#				printf '%s\n' "	nat desactivar  (Deshabilitar una salida de iptables)"
				printf '%s\n' "	nat list        (Ver las salidas NAT existentes)"
				printf '%s\n' "	nat create      (Agregar una salida NAT)"
				printf '%s\n' "	nat delete      (Eliminar una de las salidas NAT)"
				printf '%s\n' "	nat enable      (Habilitar una salida NAT guardada)"
				printf '%s\n' "	nat disable     (Deshabilitar una salida de iptables)"
				printf '%s\n' "	nat export      (Mostrar salidas NAT en formato portable)"
				printf '%s\n' "	nat import      (Crear salidas NAT desde un fichero portado)"
			else
				printf '%s\n' "Actions to give outflow to the outside network:"
				printf '%s\n' ""
				printf '%s\n' "	nat list       (See the existing NAT outputs)"
				printf '%s\n' "	nat create     (Add a NAT output)"
				printf '%s\n' "	nat delete     (Remove one of the NAT outputs)"
				printf '%s\n' "	nat enable     (Load a saved NAT output)"
				printf '%s\n' "	nat disable    (Unload a NAT output from iptables)"
				printf '%s\n' "	nat export     (Show NAT outputs in portable format)"
				printf '%s\n' "	nat import     (Create NAT outputs from a ported file)"
			fi
			;;
		* )
			nat_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

DnatAlreadyEnabled ()
# Sintaxis como función: $(DnatAlreadyEnabled "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp")
# Descripción:
#	Returns (echo) iptables DNAT entries filtered with specified parameters, with space-separated data.
#	One line per entry: RDev Protocol RPorts InputIp DestIp DestPorts Comment
# TO DO: Filter by range with matching some ports or part of range.
# Notes:
#	- Each empty parameter "" means "any"
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Effective_RDev=''
	local Effective_InputIp=''
	local Selected_RPort1=''
	local ListaOrigen=""
	local LineaActual=""
	local DatosActual=""
	local EntradaDevActual=""
	local ProtocolActual=""
	local ReceptionPort1Actual=""
	local ReceptionPort2Actual=""
	local ReceptionPortsActual=""
	local EntradaIPActual=""
	local DestinoIPActual=""
	local DestPort1Actual=""
	local DestPort9Actual=""
	local DestPortsActual=""
	local CurComment=""
	local CurMatches=0
	
#	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
#	if [ "$Selected_RDev" != "*" ] ; then
#		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then
#			Selected_RDev="$(DefaultNICs | head -n 1)"
#		fi
#	fi
#	if [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='' ; fi
#	if [ "$Selected_InputIp" != "*" ] && [ "$Selected_InputIp" != "" ] ; then
##		if [ "$Selected_InputIp" = "" ] || [ "$Selected_InputIp" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
#		if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
#			if [ "$Selected_RDev" = "*" ] ; then
#				Selected_InputIp=''
#			else
#				Selected_InputIp="$(IpDeDispositivo "$Selected_RDev")"
#			fi
#		fi
#	fi
	Effective_RDev="$(Effective_NIC "$Selected_RDev")"
	Effective_InputIp="$(Effective_IP "$Effective_RDev" "$Selected_InputIp")"
	Selected_RPort1="$(printf '%s' "$Selected_RPorts" | cut -f 1 -d ':' | cut -f 1 -d '-')"
#	ListaOrigen="$($ITScmd | grep -ie "PREROUTING" | grep -E "( -i | --in-interface )" | grep -iE "( -j DNAT| --jump DNAT)")"
	ListaOrigen="$($ITScmd | grep -ie "PREROUTING" | grep -iE "( -j DNAT| --jump DNAT)")"
	IFS="$(printf "\n\b")" ; for LineaActual in $ListaOrigen ; do unset IFS
		DatosActual="$(echo "$LineaActual" | sed -e "s/ -/\n-/g")"
		EntradaDevActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-i |^--in-interface )" | cut -f 2- -d " ")"
		EntradaDevActual="$(echo aaa$EntradaDevActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		ProtocolActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-p |^--protocol )" | cut -f 2- -d " ")"
		ProtocolActual="$(echo aaa$ProtocolActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		ReceptionPortsActual="$(printf '%s\n' "$DatosActual" | grep -E "(^--dport |^--destination-port )" | cut -f 2- -d " ")"
		ReceptionPortsActual="$(echo aaa$ReceptionPortsActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		ReceptionPort1Actual="$(printf '%s\n' "$ReceptionPortsActual" | cut -f 1 -d ':')"
		ReceptionPort9Actual="$(printf '%s\n' "$ReceptionPortsActual" | cut -sf 2 -d ':')"
		if ! Is_IntegerNr $ReceptionPort9Actual ; then
			ReceptionPort9Actual=$ReceptionPort1Actual
		fi
		ReceptionPortsActual="${ReceptionPort1Actual}-${ReceptionPort9Actual}"
		EntradaIPActual="$(printf '%s\n' "$DatosActual" | grep -E "(^-d |^--destination )" | cut -f 2- -d " " | cut -f 1 -d "/")"
		EntradaIPActual="$(echo aaa$EntradaIPActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		DestinoIPActual="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-destination )" | cut -f 2- -d " " | cut -f 1 -d ":")"
		DestinoIPActual="$(echo aaa$DestinoIPActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		DestPortsActual="$(printf '%s\n' "$DatosActual" | grep -E "(^--to-destination )" | cut -f 2- -d " " | cut -f 2 -d ":")"
		DestPortsActual="$(echo aaa$DestPortsActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		if ! Is_IntegerNr $DestPortsActual && [ "$(printf '%s' "$DestPortsActual" | grep -e '.:.' -e '.-.')" = "" ] ; then DestPortsActual='' ; fi
		if [ "$DestPortsActual" = "" ] || [ "$DestPortsActual" = "-" ] ; then Case_DestPorts='*' ; fi
		DestPort1Actual="$(printf '%s\n' "$DestPortsActual" | tr -s ':' '-' | cut -f 1 -d '-')"
		if ! Is_IntegerNr $DestPort1Actual ; then
			DestPortsActual="$ReceptionPortsActual"
			DestPort1Actual="$ReceptionPort1Actual"
		fi
		DestPort9Actual="$(printf '%s\n' "$DestPortsActual" | cut -sf 2 -d ':')"
		if ! Is_IntegerNr $DestPort9Actual ; then
			DestPort9Actual=$DestPort1Actual
		fi
		DestPortsActual="${DestPort1Actual}-${DestPort9Actual}"
		CurComment="$(printf '%s\n' "$DatosActual" | grep -E "(^--comment )" | cut -f 2- -d " ")"
		if [ "$(printf '%s\n' "$CurComment" | grep -e "^\".*\"$")" != "" ] ; then
			# Descartar comillas delimitadoras
			CurComment="$(printf '%s\n' "$CurComment" | sed -e "s/^\"//g" | sed -e "s/\"$//g")"
		fi
		if [ "$EntradaIPActual" = "" ] ; then
			EntradaIPActual="$($ITScmd --table nat | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${EntradaDevActual}$| --out-interface ${EntradaDevActual}$| -o ${EntradaDevActual} | --out-interface ${EntradaDevActual} )" | grep -E "( -s ${DestinoIPActual}/| --source ${DestinoIPActual}/| -s ${DestinoIPActual}$| --source ${DestinoIPActual}$| -s ${DestinoIPActual} | --source ${DestinoIPActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -e "$CurComment")"
			if [ "$EntradaIPActual" = "" ] ; then
				# Sin descripción
				EntradaIPActual="$($ITScmd --table nat | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${EntradaDevActual}$| --out-interface ${EntradaDevActual}$| -o ${EntradaDevActual} | --out-interface ${EntradaDevActual} )" | grep -E "( -s ${DestinoIPActual}/| --source ${DestinoIPActual}/| -s ${DestinoIPActual}$| --source ${DestinoIPActual}$| -s ${DestinoIPActual} | --source ${DestinoIPActual} )" | grep -iE "( -j SNAT| --jump SNAT)")"
			fi
			if [ "$EntradaIPActual" = "" ] ; then
				# Sin destino
				EntradaIPActual="$($ITScmd --table nat | grep -ie "POSTROUTING" | tr -s '\t' ' ' | grep -E "( -o ${EntradaDevActual}$| --out-interface ${EntradaDevActual}$| -o ${EntradaDevActual} | --out-interface ${EntradaDevActual} )" | grep -iE "( -j SNAT| --jump SNAT)" | grep -e "$CurComment")"
			fi
			EntradaIPActual="$(printf '%s\n' "$EntradaIPActual" | head -n 1)"
			EntradaIPActual="$(printf '%s\n' "$EntradaIPActual" | sed -e "s/ -/\n-/g")"
			EntradaIPActual="$(printf '%s\n' "$EntradaIPActual" | grep -E "(^--to-source )" | cut -f 2- -d " " | cut -f 1 -d "/")"
			EntradaIPActual="$(echo aaa$EntradaIPActual | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
		fi
		if [ "$(printf '%s\n' "$CurComment" | grep -ie "durruter-nat/")" != "" ] ; then
			# Descartar identificador interno, después de ser usado para buscar
			CurComment="$(printf '%s\n' "$CurComment" | sed -e "s/durruter-dnat\///g")"
		fi
		CurMatches=1
		if [ "$Effective_RDev" != "$EntradaDevActual" ] && [ "$Selected_RDev" != "" ] ; then CurMatches=0 ; fi
		if [ "$Effective_RDev" = "*" ] && [ "$EntradaDevActual" != "" ] ; then CurMatches=0 ; fi
		if [ "$Selected_Protocol" != "$ProtocolActual" ] && [ "$Selected_Protocol" != "" ] ; then CurMatches=0 ; fi
		if [ "$Selected_Protocol" = "*" ] && [ "$ProtocolActual" != "" ] ; then CurMatches=0 ; fi
		# TO DO: Filter by range with matching some ports or part of range.
		if [ "$Selected_RPort1" != "$ReceptionPort1Actual" ] && [ "$Selected_RPort1" != "" ] ; then CurMatches=0 ; fi
		if [ "$Selected_RPort1" = "*" ] && [ "$ReceptionPort1Actual" != "" ] ; then CurMatches=0 ; fi
		if [ "$Effective_InputIp" != "$EntradaIPActual" ] && [ "$Effective_InputIp" != "" ] ; then CurMatches=0 ; fi
		if [ "$Effective_InputIp" = "*" ] && [ "$EntradaIPActual" != "" ] ; then CurMatches=0 ; fi
		if  [ $CurMatches -eq 1 ] ; then
			if [ "$EntradaDevActual" = "" ] ; then EntradaDevActual="+" ; fi
			if [ "$ProtocolActual" = "" ] ; then ProtocolActual="+" ; fi
			if [ "$ReceptionPort1Actual" = "" ] ; then ReceptionPortsActual="+" ; fi
			if [ "$EntradaIPActual" = "" ] ; then EntradaIPActual="+" ; fi
			if [ "$DestinoIPActual" = "" ] ; then DestinoIPActual="+" ; fi
			if [ "$DestPort1Actual" = "" ] ; then DestPortsActual="+" ; fi
#			if [ "$CurComment" = "" ] ; then CurComment="." ; fi
			printf '%s\n' "$EntradaDevActual $ProtocolActual $ReceptionPortsActual $EntradaIPActual $DestinoIPActual $DestPortsActual $CurComment"
		fi
	done
}

Enable1Dnat ()
# Crea incondicionalmente 1 entrada DNAT en iptables.
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Id="$8"
	local Effective_RDev=''
	local Effective_InputIp=''
	local Effective_DestPorts=''
	local RespuestaIpE=""
	local ValorTemp1=""
	local CurCommand=''
	local LastStatus=0
	local StatusCode=0
	
	Effective_RDev="$(Effective_NIC "$Selected_RDev")"
	Effective_InputIp="$(Effective_IP "$Effective_RDev" "$Selected_InputIp")"
	Effective_DestPorts="$(Effective_Ports "$Selected_RPorts" "$Selected_DestPorts")"
#	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
#	if [ "$Selected_RDev" != "*" ] ; then
#		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then
#			Selected_RDev="$(DefaultNICs | head -n 1)"
#		fi
#	fi
#	if [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='' ; fi
#	if [ "$Selected_InputIp" != "*" ] && [ "$Selected_InputIp" != "" ] ; then
##		if [ "$Selected_InputIp" = "" ] || [ "$Selected_InputIp" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
#		if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
#			if [ "$Selected_RDev" = "*" ] ; then
#				Selected_InputIp=''
#			else
#				Selected_InputIp="$(IpDeDispositivo "$Selected_RDev")"
#			fi
#		fi
#	fi
	$ITcmd -L >/dev/null 2>&1	# Some iptables implementations load iptables on first call of some functions
	if [ "$(lsmod 2>/dev/null | grep -e 'iptable' -e 'ip.table')" = "" ] ; then
		printf '%s\n' "${sWARN}W: Kernel module for iptables doesn't seem to be loaded.${fRESET}" 1>&2
	fi
	Selected_Comment="$(printf '%s\n' "$Selected_Comment" | sed -e 's|-|##45;|g')"	# Encode "-" symbol to not interfere with -parameters separation
#	if [ "$IptablesMetadata" = "1" ] ; then
#		if [ $StatusCode -eq 0 ] ; then
#			$ITcmd --table nat --append PREROUTING --destination $Selected_InputIp --protocol $Selected_Protocol --dport $(printf '%s\n' "$Selected_RPorts" | tr -s '-' ':') --in-interface $Selected_RDev --jump DNAT --to-destination ${Selected_DestIp}:${Selected_DestPorts} -m comment --comment "durruter-dnat/${Selected_Id}/${Selected_Comment}"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		fi
#		if [ $StatusCode -eq 0 ] ; then
#			$ITcmd --table nat --append POSTROUTING --source $Selected_DestIp --protocol $Selected_Protocol --out-interface $Selected_RDev --jump SNAT --to $Selected_InputIp -m comment --comment "durruter-dnat/${Selected_Id}/${Selected_Comment}"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		fi
#	else
#		if [ $StatusCode -eq 0 ] ; then
#			$ITcmd --table nat --append PREROUTING --destination $Selected_InputIp --protocol $Selected_Protocol --dport $(printf '%s\n' "$Selected_RPorts" | tr -s '-' ':') --in-interface $Selected_RDev --jump DNAT --to-destination ${Selected_DestIp}:${Selected_DestPorts}
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		fi
#		if [ $StatusCode -eq 0 ] ; then
#			$ITcmd --table nat --append POSTROUTING --source $Selected_DestIp --protocol $Selected_Protocol --out-interface $Selected_RDev --jump SNAT --to $Selected_InputIp
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		fi
#	fi
	CurCommand="$ITcmd --table nat --append PREROUTING"
	if [ "$Effective_InputIp" != "" ] && [ "$Effective_InputIp" != "*" ] ; then CurCommand="$CurCommand --destination $Effective_InputIp" ; fi
	if [ "$Selected_Protocol" != "" ] && [ "$Selected_Protocol" != "*" ] ; then CurCommand="$CurCommand --protocol $Selected_Protocol" ; fi
	if [ "$Selected_RPorts" != "" ] && [ "$Selected_RPorts" != "*" ] ; then
		if [ "$Selected_Protocol" != "" ] && [ "$Selected_Protocol" != "*" ] ; then
			CurCommand="$CurCommand --dport $(printf '%s\n' "$Selected_RPorts" | tr -s '-' ':')"
		else
			printf '%s\n' "${sERROR}E: Protocol not specified, and it's required for specific ports.${fRESET}" 1>&2
			LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$Effective_RDev" != "" ] && [ "$Effective_RDev" != "*" ] ; then CurCommand="$CurCommand --in-interface $Effective_RDev" ; fi
	CurCommand="$CurCommand --jump DNAT"
	if [ "$Effective_DestPorts" != "" ] && [ "$Effective_DestPorts" != "*" ] && [ "$Effective_DestPorts" != "." ] && [ "$Effective_DestPorts" != "=" ] ; then
		CurCommand="$CurCommand --to-destination ${Selected_DestIp}:${Effective_DestPorts}"
	else
		CurCommand="$CurCommand --to-destination ${Selected_DestIp}"
	fi
	if [ "$IptablesMetadata" = "1" ] ; then
		CurCommand="$CurCommand -m comment --comment \"durruter-dnat/${Selected_Id}/${Selected_Comment}\""
	fi
	if [ $StatusCode -eq 0 ] ; then
		eval $CurCommand
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $LastStatus -ne 0 ] ; then
		LogProgram 1 "Command was: $CurCommand"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Effective_InputIp" != "" ] && [ "$Effective_InputIp" != "*" ] ; then
			CurCommand="$ITcmd --table nat --append POSTROUTING --source $Selected_DestIp"
			if [ "$Selected_Protocol" != "" ] && [ "$Selected_Protocol" != "*" ] ; then CurCommand="$CurCommand --protocol $Selected_Protocol" ; fi
			if [ "$Effective_RDev" != "" ] && [ "$Effective_RDev" != "*" ] ; then CurCommand="$CurCommand --out-interface $Effective_RDev" ; fi
			CurCommand="$CurCommand --jump SNAT --to $Effective_InputIp"
			if [ "$IptablesMetadata" = "1" ] ; then
				CurCommand="$CurCommand -m comment --comment \"durruter-dnat/${Selected_Id}/${Selected_Comment}\""
			fi
			eval $CurCommand
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Remove1Dnat ()
# Syntax as a sentence: Remove1Dnat "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_Id"
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_Id="$4"
	local Selected_RPort1=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
	if [ "$Selected_RDev" != "*" ] ; then
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then
			Selected_RDev="to-wan"
		fi
	fi
	Selected_RPort1="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
	Dir_RDev="$Selected_RDev"
	if [ "$Dir_RDev" = "*" ] ; then Dir_RDev='_any' ; fi
	Dir_RPort1="$Selected_RPort1"
	if [ "$Dir_RPort1" = "*" ] ; then Dir_RPort1='_any' ; fi
	Dir_Protocol="$Selected_Protocol"
	if [ "$Dir_Protocol" = "*" ] ; then Dir_Protocol='_any' ; fi
	Dir_Path="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/${Dir_Protocol}"
	Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
	Selected_NrCase=0
	Dir_CasesNr=0
	for Case_Id in $Dir_Id ; do
		# Each case is intended for each IP on same reception device.
		Dir_CasesNr=$(($Dir_CasesNr + 1))
		if [ "$Case_Id" = "$Selected_Id" ] ; then Selected_NrCase=$Dir_CasesNr ; fi
	done
	if [ $Selected_NrCase -gt 0 ] && [ $Dir_CasesNr -eq 1 ] ; then
		# The only case in the directory; wipe away.
		rm -fr "$Dir_Path"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		if [ $Dir_CasesNr -eq 0 ] ; then
			# No cases in the directory; wipe away any wreckage.
			rm -fr "$Dir_Path"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ $Selected_NrCase -gt 0 ] ; then
				# There are other cases to respect; let's clean only case's lines.
				Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
				LineReplaced "$Dir_Id" $Selected_NrCase "." '' > "${Dir_Path}/id.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Dir_InputIp="$(cat "${Dir_Path}/input-ip.txt" 2>/dev/null)"
				LineReplaced "$Dir_InputIp" $Selected_NrCase "" '' > "${Dir_Path}/input-ip.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Dir_RPorts="$(cat "${Dir_Path}/reception-ports.txt" 2>/dev/null)"
				LineReplaced "$Dir_RPorts" $Selected_NrCase "" '' > "${Dir_Path}/reception-ports.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Dir_DestIp="$(cat "${Dir_Path}/destination-ip.txt" 2>/dev/null)"
				LineReplaced "$Dir_DestIp" $Selected_NrCase "" '' > "${Dir_Path}/destination-ip.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Dir_DestPorts="$(cat "${Dir_Path}/destination-ports.txt" 2>/dev/null)"
				LineReplaced "$Dir_DestPorts" $Selected_NrCase "" '' > "${Dir_Path}/destination-ports.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Dir_Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null)"
				LineReplaced "$Dir_Comment" $Selected_NrCase "" '' > "${Dir_Path}/description.txt"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					RebuildDnatLoaders "$Selected_RDev" "$Selected_RPorts" "$Selected_Protocol"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				if [ "$Selected_RDev" != "" ] && [ "$Selected_Protocol" != "" ] && [ "$Selected_RPorts" != "" ] ; then
					if [ -d "$Dir_Path" ] ; then
						rm -fr "$Dir_Path"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						printf '%s\n' "${sERROR}E: DNAT entry not found for RDev=${Selected_RDev} Protocol=${Selected_Protocol} RPorts=${Selected_RPorts}${fRESET}" 1>&2
						LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ "$Selected_Id" != "" ] ; then
						printf '%s\n' "${sERROR}E: DNAT entry not found with identifier \"${Selected_Id}\".${fRESET}" 1>&2
						LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						printf '%s\n' "${sERROR}E: DNAT entry not found for RDev=${Selected_RDev} Protocol=${Selected_Protocol} RPorts=${Selected_RPorts}${fRESET}" 1>&2
						LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

Save1Dnat ()
# Almacena incondicionalmente 1 entrada DNAT
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Id="$8"
	local Selected_RPort1=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
	if [ "$Selected_RDev" != "*" ] ; then
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then	#"
			Selected_RDev="to-wan"
		fi
	fi
	if [ "$Selected_InputIp" = "" ] || [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='*' ; fi
	if [ "$Selected_InputIp" != "*" ] ; then
#		if [ "$Selected_InputIp" = "" ] || [ "$Selected_InputIp" = "." ] || [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
		if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
			Selected_InputIp="default"
		fi
	fi
	if ! Is_IntegerNr $Selected_Id ; then Selected_Id=$(NewIdForEntry) ; fi
	Selected_RPort1="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
	Dir_RDev="$Selected_RDev"
	if [ "$Dir_RDev" = "*" ] ; then Dir_RDev='_any' ; fi
	Dir_RPort1="$Selected_RPort1"
	if [ "$Dir_RPort1" = "*" ] ; then Dir_RPort1='_any' ; fi
	Dir_Protocol="$Selected_Protocol"
	if [ "$Dir_Protocol" = "*" ] ; then Dir_Protocol='_any' ; fi
	Dir_Path="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/${Dir_Protocol}"
	Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
	Selected_NrCase=0
	Dir_CasesNr=0
	for Case_Id in $Dir_Id ; do
		# Each case is intended for each IP on same reception device.
		Dir_CasesNr=$(($Dir_CasesNr + 1))
		if [ "$Case_Id" = "$Selected_Id" ] ; then Selected_NrCase=$Dir_CasesNr ; fi
	done
	if [ $Selected_NrCase -gt 0 ] && [ $Dir_CasesNr -eq 1 ] ; then
		# The only case is the one we are saving; wipe away previous directory.
		rm -fr "$Dir_Path"
	fi
	if [ $Dir_CasesNr -eq 0 ] ; then
		# No cases in the directory; wipe away any wreckage.
		rm -fr "$Dir_Path"
	fi
	mkdir -p "$Dir_Path"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ $Selected_NrCase -eq 0 ] ; then Selected_NrCase=$(($Dir_CasesNr + 1)) ; fi
		Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
		LineReplaced "$Dir_Id" $Selected_NrCase "$Selected_Id" '' > "${Dir_Path}/id.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_InputIp="$(cat "${Dir_Path}/input-ip.txt" 2>/dev/null)"
		LineReplaced "$Dir_InputIp" $Selected_NrCase "$Selected_InputIp" '' > "${Dir_Path}/input-ip.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_RPorts="$(cat "${Dir_Path}/reception-ports.txt" 2>/dev/null)"
		LineReplaced "$Dir_RPorts" $Selected_NrCase "$Selected_RPorts" '' > "${Dir_Path}/reception-ports.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_DestIp="$(cat "${Dir_Path}/destination-ip.txt" 2>/dev/null)"
		LineReplaced "$Dir_DestIp" $Selected_NrCase "$Selected_DestIp" '' > "${Dir_Path}/destination-ip.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_DestPorts="$(cat "${Dir_Path}/destination-ports.txt" 2>/dev/null)"
		LineReplaced "$Dir_DestPorts" $Selected_NrCase "$Selected_DestPorts" '' > "${Dir_Path}/destination-ports.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Dir_Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null)"
		LineReplaced "$Dir_Comment" $Selected_NrCase "$Selected_Comment" '' > "${Dir_Path}/description.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			RebuildDnatLoaders "$Selected_RDev" "$Selected_RPorts" "$Selected_Protocol"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

DnatAlreadySaved ()
# Sintaxis como función: $(DnatAlreadySaved "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts")
# Descripción:
#	Devuelve (echo) "1" en caso de que la entrada DNAT especificada ya esté registrada en el cargador
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_RPort1=""
	local Valor="0"
	
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='' ; fi
	if [ "$Selected_RDev" != "*" ] ; then
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then	#"
			Selected_RDev="to-wan"
		fi
	fi
	if [ "$Selected_RDev" = "*" ] ; then Selected_RDev='_any' ; fi
	if [ "$Selected_Protocol" = "*" ] ; then Selected_Protocol='_any' ; fi
	if [ "$Selected_RPorts" = "*" ] ; then Selected_RPorts='_any' ; fi
	if [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='' ; fi
	if [ "$Selected_InputIp" = "*" ] ; then
		Selected_InputIp='_any'
		Selected_InputIp_Regexp='_any'
	else
		if [ "$Selected_InputIp" = "" ] ; then
			Selected_InputIp_Regexp='.*'
		else
			if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
				Selected_InputIp="default"
			fi
			Selected_InputIp_Regexp="$(printf '%s\n' "$Selected_InputIp" | sed -e 's|\.|\\.|g')"
		fi
	fi
	Selected_RPort1="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
	if [ "$(cat "${SystemConfigDir}/${Selected_RDev}/dnat/${Selected_RPort1}/${Selected_Protocol}/input-ip.txt" 2>/dev/null | grep -e "^${Selected_InputIp_Regexp}$")" ] ; then	#"
		Valor="1"
	fi
	printf '%s\n' "$Valor"
}

DnatSortKey ()
# Syntax as a function: "$(DnatSortKey "$Selected_RDev" "$Selected_Protocol" "$Selected_RPort1" "$Selected_InputIp" "$Selected_Id")"
# Description: Returns (stdout) good string to serve as a unique and sortable value for specified DNAT entry
# Note: "printf '%3s\n'" is not working here (eg:Debian9).
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPort1="$3"
	local Selected_InputIp="$4"
	local Selected_Id="$5"
	local Selected_InputIp_b1=''
	local Selected_InputIp_b2=''
	local Selected_InputIp_b3=''
	local Selected_InputIp_b4=''
	local Value=''
	
	Selected_InputIp_b1="$(printf '%s\n' "$Selected_InputIp" | cut -f 1 -d '.')"
	Selected_InputIp_b2="$(printf '%s\n' "$Selected_InputIp" | cut -sf 2 -d '.')"
	Selected_InputIp_b3="$(printf '%s\n' "$Selected_InputIp" | cut -sf 3 -d '.')"
	Selected_InputIp_b4="$(printf '%s\n' "$Selected_InputIp" | cut -sf 4 -d '.')"
	while [ ${#Selected_InputIp_b1} -lt 3 ] ; do Selected_InputIp_b1="0${Selected_InputIp_b1}" ; done
	while [ ${#Selected_InputIp_b2} -lt 3 ] ; do Selected_InputIp_b2="0${Selected_InputIp_b2}" ; done
	while [ ${#Selected_InputIp_b3} -lt 3 ] ; do Selected_InputIp_b3="0${Selected_InputIp_b3}" ; done
	while [ ${#Selected_InputIp_b4} -lt 3 ] ; do Selected_InputIp_b4="0${Selected_InputIp_b4}" ; done
	Value="${Value}${Selected_InputIp_b1}"
	Value="${Value}${Selected_InputIp_b2}"
	Value="${Value}${Selected_InputIp_b3}"
	Value="${Value}${Selected_InputIp_b4}"
	Value="${Value}${Selected_RDev}"
	while [ ${#Selected_RPort1} -lt 5 ] ; do Selected_RPort1="0${Selected_RPort1}" ; done
	Value="${Value}${Selected_RPort1}"
	Value="${Value}${Selected_Protocol}"
	while [ ${#Selected_Id} -lt 5 ] ; do Selected_Id="0${Selected_Id}" ; done
	Value="${Value}.${Selected_Id}"
	printf '%s\n' "$Value"
}

DnatFilteredAction ()
# Syntax as a sentence: DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id" "$Action"
# Called by dnat_export(), dnat_list(), dnat_disable(), dnat_enable(), dnat_remove()
# Expected parameters:
#	$1	If not empty, filter results requiring to match this reception device name (regexp alowed)
#	$2	If not empty, filter results requiring to match this protocol name
#	$3	If not empty, filter results requiring to match this reception ports (Single, range or open range: 80, 80-81 or 80- or -80)
#	$4	If not empty, filter results requiring to match this input IP (simple asterisk * wildcards allowed)
#	$5	If not empty, filter results requiring to match this destination IP (simple asterisk * wildcards allowed)
#	$6	If not empty, filter results requiring to match this destination ports (Single, range or open range: 80, 80-81 or 80- or -80)
#	$7	If not empty, filter results requiring to contain this comment (regular expressions allowed)
#	$8	If not empty, filter results only enabled entries (yes) or disabled ones (no)
#	$9	If not empty, filter results requiring to match this internal identifier
#	$10	Action for each matching case: "humanlist" "export" "disable" "enable" "remove"
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local Action="${10}"
	local Selected_RPort1=''
	local Selected_RPort9=''
	local Selected_DestPort1=''
	local Selected_DestPort9=''
	local LinesGroup=''
	local CurrentLine=''
	local DirPreparacionListado=''
	local IptablesContent=''
	local OutputLog=''
	local MatchedNr=0
	
	# Data formats fixing
	Selected_Id="$(echo aaa$Selected_Id | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_Id" = "." ] || [ "$Selected_Id" = "all" ] || [ "$Selected_Id" = "any" ] ; then Selected_Id="" ; fi
	if [ "$Selected_Id" = "." ] ; then Selected_Id="" ; fi
	Selected_RDev="$(echo aaa$Selected_RDev | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_RDev" = "." ] || [ "$Selected_RDev" = "all" ] || [ "$Selected_RDev" = "any" ] ; then Selected_RDev="" ; fi
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev="" ; fi
	if [ "$Selected_RDev" != "*" ] ; then
		if [ "$(printf '%s\n' " default towan to-wan wan internet " | grep -ie " $Selected_RDev ")" != "" ] ; then
			Selected_RDev="to-wan"
		fi
	fi
	if [ "$Selected_RDev" = "_any" ] || [ "$Selected_RDev" = "any" ] || [ "$Selected_RDev" = "*" ] ; then Selected_RDev='*' ; fi
	Selected_Protocol="$(echo aaa$Selected_Protocol | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_Protocol" = "." ] || [ "$Selected_Protocol" = "all" ] || [ "$Selected_Protocol" = "any" ] ; then Selected_Protocol="" ; fi
	if [ "$Selected_Protocol" = "." ] ; then Selected_Protocol="" ; fi
	if [ "$Selected_Protocol" = "_any" ] || [ "$Selected_Protocol" = "any" ] || [ "$Selected_Protocol" = "*" ] ; then Selected_Protocol='*' ; fi
	if [ "$Selected_RPorts" != "" ] ; then
		Selected_RPorts="$(echo aaa$Selected_RPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#		if [ "$Selected_RPorts" = "." ] || [ "$Selected_RPorts" = "all" ] || [ "$Selected_RPorts" = "any" ] ; then Selected_RPorts="" ; fi
		if [ "$Selected_RPorts" = "." ] ; then Selected_RPorts="" ; fi
		Selected_RPorts="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-')"
		if [ "$Selected_RPorts" = "_any" ] || [ "$Selected_RPorts" = "any" ] || [ "$Selected_RPorts" = "*" ] ; then Selected_RPorts='*' ; fi
		if [ "$(printf '%s\n' "$Selected_RPorts" | grep -e '^-')" != "" ] ; then
			Selected_RPort1=0
		else
			Selected_RPort1="$(printf '%s\n' "$Selected_RPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
			Selected_RPort1="$(echo aaa$Selected_RPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
		fi
		if [ "$(printf '%s\n' "$Selected_RPorts" | grep -e '-$')" != "" ] ; then
			Selected_RPort9=65535
		else
			Selected_RPort9="$(printf '%s\n' "$Selected_RPorts" | cut -sf 2 -d '-')"
			Selected_RPort9="$(echo aaa$Selected_RPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
			if ! Is_IntegerNr $Selected_RPort9 && Is_IntegerNr $Selected_RPort1 ; then Selected_RPort9=$Selected_RPort1 ; fi
		fi
	fi
	Selected_InputIp="$(echo aaa$Selected_InputIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_InputIp" = "" ] || [ "$Selected_InputIp" = "all" ] || [ "$Selected_InputIp" = "any" ] ; then Selected_InputIp="" ; fi
	if [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='' ; fi
	if [ "$Selected_InputIp" != "*" ] && [ "$Selected_InputIp" != "" ] ; then
		if [ "$(printf '%s\n' " default current present assigned " | grep -ie " $Selected_InputIp ")" != "" ] ; then
			Selected_InputIp="default"
		fi
	fi
	if [ "$Selected_InputIp" = "_any" ] || [ "$Selected_InputIp" = "any" ] || [ "$Selected_InputIp" = "*" ] ; then Selected_InputIp='*' ; fi
	Selected_DestIp="$(echo aaa$Selected_DestIp | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_DestIp" = "." ] || [ "$Selected_DestIp" = "all" ] || [ "$Selected_DestIp" = "any" ] ; then Selected_DestIp="" ; fi
	if [ "$Selected_DestIp" = "." ] ; then Selected_DestIp="" ; fi
	if [ "$Selected_DestPorts" != "" ] ; then
		Selected_DestPorts="$(echo aaa$Selected_DestPorts | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#		if [ "$Selected_DestPorts" = "." ] || [ "$Selected_DestPorts" = "all" ] || [ "$Selected_DestPorts" = "any" ] ; then Selected_DestPorts="" ; fi
		if [ "$Selected_DestPorts" = "." ] || [ "$Selected_DestPorts" = "=" ] ; then Selected_DestPorts="" ; fi
		Selected_DestPorts="$(printf '%s\n' "$Selected_DestPorts" | tr -s ':' '-')"
		if [ "$(printf '%s\n' "$Selected_DestPorts" | grep -e '^-')" != "" ] ; then
			Selected_DestPort1=0
		else
			Selected_DestPort1="$(printf '%s\n' "$Selected_DestPorts" | tr -s ':' '-' | cut -f 1 -d '-')"
			Selected_DestPort1="$(echo aaa$Selected_DestPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
		fi
		if [ "$(printf '%s\n' "$Selected_DestPorts" | grep -e '-$')" != "" ] ; then
			Selected_DestPort9=65535
		else
			if ! Is_IntegerNr $Selected_DestPort1 && Is_IntegerNr $Selected_RPort1 ; then
				Selected_DestPort1=$Selected_RPort1
				Selected_DestPort9=$Selected_RPort9	# Selected_RPort1 could not be range-consistent with the case of Dir_DestPorts
			else
				Selected_DestPort1="$(echo aaa$Selected_DestPort1 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
				Selected_DestPort9="$(printf '%s\n' "$Selected_DestPorts" | cut -sf 2 -d '-')"
				Selected_DestPort9="$(echo aaa$Selected_DestPort9 | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" # Trim and first word.
				if ! Is_IntegerNr $Selected_DestPort9 && Is_IntegerNr $Selected_DestPort1 ; then Selected_DestPort9=$Selected_DestPort1 ; fi
			fi
		fi
	fi
	Selected_Comment="$(echo aaa$Selected_Comment | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
	# ./all/any not applicable, to allow regular expressions and these words use.
	if [ "$(printf '%s\n' "$Selected_Comment" | grep -e '".*"$')" != "" ] ; then
		# Discard delimiter quotes: double
		Selected_Comment="$(printf '%s\n' "$Selected_Comment" | sed -e 's|^"||' | sed -e 's|"$||')"
	else
		if [ "$(printf '%s\n' "$Selected_Comment" | grep -e "'.*'$")" != "" ] ; then
			# Discard delimiter quotes: simple
			Selected_Comment="$(printf '%s\n' "$Selected_Comment" | sed -e "s|^'||" | sed -e "s|'$||")"
		fi
	fi
	if [ "$(printf '%s\n' "$Selected_Comment" | grep -e "^durruter-dnat/.*/")" != "" ] ; then
		# Discard internal identifiers
		Selected_Comment="$(printf '%s\n' "$Selected_Comment" | cut -f 3- -d '/')"
	fi
	Selected_Enabled="$(echo aaa$Selected_Enabled | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"	# Trim
#	if [ "$Selected_Enabled" = "." ] || [ "$Selected_Enabled" = "all" ] || [ "$Selected_Enabled" = "any" ] ; then Selected_Enabled="" ; fi
	if [ "$Selected_Enabled" = "." ] ; then Selected_Enabled="" ; fi
	if [ "$Selected_Enabled" != "" ] ; then
		Selected_Enabled="$(printf '%s\n' "$Selected_Enabled" | cut -c 1)"
		Selected_Enabled="$(Lowercase "$Selected_Enabled")"
	fi
	# /Data formats fixing

#	OutputLog="FirstLineToAvoid"
	case "$Action" in
		"humanlist" )
			if [ "$FastLists" != "1" ] ; then
				DirPreparacionListado="${DirTemp}/durruter-dfa.$$"
				mkdir -p "$DirPreparacionListado"
				chmod u=rwX,go= "$DirPreparacionListado"
				if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
					ListadoEncabezados "$DirPreparacionListado" "A" "ACTIVA" "D.ENTRADA" "PROTC." "_PORTS" "IP_ENTRADA" "IP_DESTINO:PUERTOS" "COMENTARIO"
				else
					ListadoEncabezados "$DirPreparacionListado" "A" "ACTIVE" "IN.INTERF." "PROTC." "_PORTS" "INPUT_IP" "TARGET_IP:PORTS" "COMMENT"
				fi
			else
#				OutputLog="ACTIVE	IN.INTERF.	PROTC.	PORT	INPUT_IP	TARGET_IP:PORT	COMMENT"
				printf '%s\n' "ACTIVE	IN.INTERF.	PROTC.	PORT	INPUT_IP	TARGET_IP:PORT	COMMENT"
			fi
			;;
		"export" )
#			OutputLog="# PAT configuration based in iptables (exported by $ServiceName helper)"
			printf '%s\n' "# PAT configuration based in iptables (exported by $ServiceName helper)"
			;;
	esac
	IptablesContent="$($ITScmd)"
	if [ "$Selected_Enabled" = "" ] || [ "$Selected_Enabled" = "y" ] ; then
#		LinesGroup="$(printf '%s\n' "$IptablesContent" | grep -ie "PREROUTING" | grep -E "( -i | --in-interface )" | grep -iE "( -j DNAT| --jump DNAT)")"
		LinesGroup="$(printf '%s\n' "$IptablesContent" | grep -ie "PREROUTING" | grep -iE "( -j DNAT| --jump DNAT)")"
		IFS="$(printf "\n\b")" ; for CurrentLine in $LinesGroup ; do unset IFS
			Case_Enabled="yes"
			Case_Saved="No"
			DnatIptablesLineValues "$CurrentLine"
			if [ "$Case_Id" != "" ] ; then
				if [ "$(cat /etc/durruter/*/dnat/*/*/id.txt 2>/dev/null | grep -e "^${Case_Id}$")" != "" ] ; then
					Case_Saved="Yes"
					Case_Enabled="YES"
				fi
			fi
#			if [ "$Case_Saved" = "No" ] || [ "$(printf '%s\n' "$Action" | grep -e '^export$')" = "" ] ; then
			if [ "$Case_Enabled" != "No" ] || [ "$Case_Saved" = "No" ] || [ "$(printf '%s\n' "$Action" | grep -e '^export$')" = "" ] ; then
				# If saved, export is better from saved data with generic values
				if [ "$(DnatCaseMatchesFilter "$Selected_RDev" "$Selected_Protocol" "$Selected_RPort1" "$Selected_RPort9" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPort1" "$Selected_DestPort9" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id")" = "1" ] ; then
					MatchedNr=$(($MatchedNr + 1))
					Dir_RDev="$Case_RDev"
					if [ "$Dir_RDev" = "*" ] ; then Dir_RDev='_any' ; fi
					Dir_RPort1="$Case_RPort1"
					if [ "$Dir_RPort1" = "*" ] ; then Dir_RPort1='_any' ; fi
					Dir_Protocol="$Case_Protocol"
					if [ "$Dir_Protocol" = "*" ] ; then Dir_Protocol='_any' ; fi
					Dir_Path="${SystemConfigDir}/${Dir_RDev}/dnat/${Dir_RPort1}/${Dir_Protocol}"
					if [ "$IptablesMetadata" != "1" ] && [ "$Case_Comment" = "" ] && [ -f "${Dir_Path}/description.txt" ] && [ $(cat "${Dir_Path}/description.txt" | wc -l) -eq 1 ] ; then
						Case_Comment="$(cat "${Dir_Path}/description.txt")"	#"
					fi
					if [ "$Case_InputIp" = "" ] ; then Case_InputIp="default" ; fi
					case "$Action" in
						"humanlist" )
							if [ "$FastLists" != "1" ] ; then
								SortKey="$(DnatSortKey "$Case_RDev" "$Case_Protocol" "$Case_RPort1" "$Case_InputIp" "$Case_Id")"	#"
								ListadoNuevoRegistro "$DirPreparacionListado" "$SortKey" "$Case_Enabled" "$Case_RDev_Current" "$Case_Protocol" "$Case_RPorts_Show" "$Case_InputIp" "${Case_DestIp}:${Case_DestPorts_Show}" "$Case_Comment"
							else
#								OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "$Case_Enabled	$Case_RDev_Current	$Case_Protocol	$Case_RPorts	$Case_InputIp	${Case_DestIp}:${Case_DestPorts_Show}	$Case_Comment")"	#"
								printf '%s\n' "$Case_Enabled	$Case_RDev_Current	$Case_Protocol	$Case_RPorts_Show	$Case_InputIp	${Case_DestIp}:${Case_DestPorts_Show}	$Case_Comment"
							fi
							;;
						"export" )
#							OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "dnat create $Case_RDev $Case_Protocol $Case_RPorts_Portable $Case_InputIp $Case_DestIp $Case_DestPorts_Portable $Case_Comment_Export")"	#"
#							printf '%s\n' "dnat create $Case_RDev_Portable $Case_Protocol_Portable $Case_RPorts_Portable $Case_InputIp_Portable $Case_DestIp $Case_DestPorts_Portable $Case_Comment_Export"
							printf '%s\n' "dnat create $(QuoteAsNeeded2 "$Case_RDev_Portable" "$Case_Protocol_Portable" "$Case_RPorts_Portable" "$Case_InputIp_Portable" "$Case_DestIp" "$Case_DestPorts_Portable") \"${Case_Comment}\""
							;;
						"disable" )
#							Desactivar1Dnat "$Case_RDev_Current" "$Case_Protocol" "$Case_RPort1" "$Case_InputIp"
							LineToRemove="$(printf '%s\n' "$CurrentLine" | sed -e 's|^-A ||g')"
							eval $ITcmd --table nat --delete $LineToRemove
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if Is_IntegerNr $Case_Id ; then
								# Clean its POSTROUTING-SNAT lines; this method doesn't include non-durruter additional lines.
								AdditionalLines="$(printf '%s\n' "$IptablesContent" | grep -e "durruter-dnat/${Case_Id}/")"	#"
								IFS="$(printf "\n\b")" ; for AdditionalLine in $AdditionalLines ; do unset IFS	#"
									LineToRemove="$(printf '%s\n' "$AdditionalLine" | sed -e 's|^-A ||g')"
									# Repeated-identical lines are deleted with only 1 iptables command.
									eval $ITcmd --table nat --delete $LineToRemove 2>/dev/null
								done
							fi
							if [ $LastStatus -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
								if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} deshabilitada para ${Case_RDev_Current}/${Case_InputIp_Current}")"	#"
									printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} deshabilitada para ${Case_RDev_Current}/${Case_InputIp_Current}"
								else
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} disabled for ${Case_RDev_Current}/${Case_InputIp_Current}")"		#do
									printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} disabled for ${Case_RDev_Current}/${Case_InputIp_Current}"
								fi
							fi
							;;
						"remove" )
#							Desactivar1Dnat "$Case_RDev_Current" "$Case_Protocol" "$Case_RPort1" "$Case_InputIp"
							LineToRemove="$(printf '%s\n' "$CurrentLine" | sed -e 's|^-A ||g')"
							eval $ITcmd --table nat --delete $LineToRemove
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if Is_IntegerNr $Case_Id ; then
								# Clean its POSTROUTING-SNAT lines; this method doesn't include non-durruter additional lines.
								AdditionalLines="$(printf '%s\n' "$IptablesContent" | grep -e "durruter-dnat/${Case_Id}/")"	#"
								IFS="$(printf "\n\b")" ; for AdditionalLine in $AdditionalLines ; do unset IFS	#"
									LineToRemove="$(printf '%s\n' "$AdditionalLine" | sed -e 's|^-A ||g')"
									eval $ITcmd --table nat --delete $LineToRemove 2>/dev/null
								done
							fi
							if [ $LastStatus -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
								if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} deshabilitada para ${Case_RDev_Current}/${Case_InputIp_Current}")"	#"
									printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} deshabilitada para ${Case_RDev_Current}/${Case_InputIp_Current}"
								else
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} disabled for ${Case_RDev_Current}/${Case_InputIp_Current}")"		#do
									printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} disabled for ${Case_RDev_Current}/${Case_InputIp_Current}"
								fi
							fi
							Remove1Dnat "$Case_RDev" "$Case_Protocol" "$Case_RPorts" "$Case_Id"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
								if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} eliminada para ${Case_RDev}/${Case_InputIp}")"	#"
									printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} eliminada para ${Case_RDev}/${Case_InputIp}"
								else
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} deleted for ${Case_RDev}/${Case_InputIp}")"		#do
									printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} deleted for ${Case_RDev}/${Case_InputIp}"
								fi
							fi
							;;
					esac
				fi
			fi
			if [ "$Action" != "export" ] ; then
				printf '.' 1>&2
			fi
		done
	fi
	if [ "$Selected_Enabled" = "" ] || [ "$Selected_Enabled" = "n" ] ; then
		FilePathsList="$(find "${SystemConfigDir}/"*"/dnat/"*"/"*"/destination-ip.txt" 2>/dev/null)"
		IFS="$(printf "\n\b")" ; for CurDirLine in $FilePathsList ; do unset IFS
			Dir_Path="$(dirname "$CurDirLine")"
			Dir_RDev="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 5 | head -n 1)"
			Dir_Protocol="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 2 | head -n 1)"
			Dir_RPort1="$(printf '%s\n' "$CurDirLine" | tr -s '/' '\n' | tail -n 3 | head -n 1)"
			Dir_RPorts="$(cat "${Dir_Path}/reception-ports.txt" 2>/dev/null)"
			Dir_InputIp="$(cat "${Dir_Path}/input-ip.txt" 2>/dev/null)"
			Dir_DestIp="$(cat "${Dir_Path}/destination-ip.txt" 2>/dev/null)"
			Dir_DestPorts="$(cat "${Dir_Path}/destination-ports.txt" 2>/dev/null)"
			Dir_Comment="$(cat "${Dir_Path}/description.txt" 2>/dev/null)"
			Dir_Id="$(cat "${Dir_Path}/id.txt" 2>/dev/null)"
			NrCase=0
			for Case_Id in $Dir_Id ; do
				# Each case is intended for each IP on same reception device.
				NrCase=$(($NrCase + 1))
				if Is_IntegerNr $Case_Id ; then	# Non-numeric cases can be because of removed lines in id.txt
					Case_Enabled="No"
					Case_Saved="Yes"
					if [ "$IptablesMetadata" = "1" ] ; then
						if [ "$(printf '%s\n' "$IptablesContent" | grep -ie "PREROUTING.*durruter-dnat/${Case_Id}/")" != "" ] ; then Case_Enabled="YES" ; fi
					else
						DnatDirCaseValues $NrCase "$Dir_RDev" "$Dir_Protocol" "$Dir_RPort1" "$Dir_RPorts" "$Dir_InputIp" "$Dir_DestIp" "$Dir_DestPorts" "$Dir_Comment" "$Dir_Id"
						if [ "$(DnatAlreadyEnabled "$Case_RDev_Current" "$Case_Protocol" "$Case_RPort1" "$Case_InputIp")" != "" ] ; then Case_Enabled="YES" ; fi
					fi
					if [ "$Case_Enabled" = "No" ] || [ "$(printf '%s\n' "$Action" | grep -e '^humanlist$' -e '^enable$')" = "" ] ; then
						# Entry not in iptables (not already processed) || Not to delegate to enabled matching ones (humanlist already listed, enable only if not already enabled)
						if [ "$IptablesMetadata" = "1" ] ; then
							DnatDirCaseValues $NrCase "$Dir_RDev" "$Dir_Protocol" "$Dir_RPort1" "$Dir_RPorts" "$Dir_InputIp" "$Dir_DestIp" "$Dir_DestPorts" "$Dir_Comment" "$Dir_Id"
						fi
						Case_Enabled="No"
						if [ "$(DnatCaseMatchesFilter "$Selected_RDev" "$Selected_Protocol" "$Selected_RPort1" "$Selected_RPort9" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPort1" "$Selected_DestPort9" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id")" = "1" ] ; then
							MatchedNr=$(($MatchedNr + 1))
							if [ "$Case_InputIp" = "" ] ; then Case_InputIp="default" ; fi
							case "$Action" in
								"humanlist" )
									if [ "$FastLists" != "1" ] ; then
										SortKey="$(DnatSortKey "$Case_RDev_Current" "$Case_Protocol" "$Case_RPort1" "$Case_InputIp" "$Case_Id")"	#"
										ListadoNuevoRegistro "$DirPreparacionListado" "$SortKey" "$Case_Enabled" "$Case_RDev" "$Case_Protocol" "$Case_RPorts_Show" "$Case_InputIp" "${Case_DestIp}:${Case_DestPorts_Show}" "$Case_Comment"
									else
#										OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "$Case_Enabled 	$Case_RDev	$Case_Protocol	$Case_RPorts	$Case_InputIp	${Case_DestIp}:${Case_DestPorts_Show}	$Case_Comment")"
										printf '%s\n' "$Case_Enabled 	$Case_RDev	$Case_Protocol	$Case_RPorts_Show	$Case_InputIp	${Case_DestIp}:${Case_DestPorts_Show}	$Case_Comment"
									fi
									;;
								"export" )
#									OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "dnat create $Case_RDev $Case_Protocol $Case_RPorts_Portable $Case_InputIp $Case_DestIp $Case_DestPorts_Portable $Case_Comment_Export")"	#"
#									printf '%s\n' "dnat create $Case_RDev_Portable $Case_Protocol_Portable $Case_RPorts_Portable $Case_InputIp_Portable $Case_DestIp $Case_DestPorts_Portable $Case_Comment_Export"
									printf '%s\n' "dnat create $(QuoteAsNeeded2 "$Case_RDev_Portable" "$Case_Protocol_Portable" "$Case_RPorts_Portable" "$Case_InputIp_Portable" "$Case_DestIp" "$Case_DestPorts_Portable") \"${Case_Comment}\""
									#if [ "$Case_Enabled" = "No" ] ; then
#										OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "dnat disable $Case_RDev $Case_Protocol $Case_RPorts_Portable $Case_InputIp")"	#"
									#	printf '%s\n' "dnat disable $Case_RDev $Case_Protocol $Case_RPorts_Portable $Case_InputIp"
									#fi
									;;
								"enable" )
									Enable1Dnat "$Case_RDev_Current" "$Case_Protocol" "$Case_RPorts" "$Case_InputIp" "$Case_DestIp" "$Case_DestPorts" "$Case_Comment" "$Case_Id"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
									if [ $LastStatus -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
										if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
#											OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} habilitada para ${Case_RDev_Current}/${Case_InputIp_Current}")"	#"
											printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} habilitada para ${Case_RDev_Current}/${Case_InputIp_Current}"
										else
#											OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} enabled for ${Case_RDev_Current}/${Case_InputIp_Current}")"		# do
											printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} enabled for ${Case_RDev_Current}/${Case_InputIp_Current}"
										fi
									fi
									;;
								"remove" )
									Remove1Dnat "$Case_RDev" "$Case_Protocol" "$Case_RPorts" "$Case_Id"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
									if [ $LastStatus -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
										if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
#											OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} eliminada para ${Case_RDev}/${Case_InputIp}")"	#"
											printf '%s\n' "Entrada DNAT ${Case_Protocol}/${Case_RPorts_Show} eliminada para ${Case_RDev}/${Case_InputIp}"
										else
#											OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} deleted for ${Case_RDev}/${Case_InputIp}")"		#do
											printf '%s\n' "DNAT input ${Case_Protocol}/${Case_RPorts_Show} deleted for ${Case_RDev}/${Case_InputIp}"
										fi
									fi
									;;
							esac
						fi
						if [ "$Action" != "export" ] ; then
							printf '.' 1>&2
						fi
					fi
				fi
			done
		done
	fi
	if [ $MatchedNr -gt 0 ] ; then
		if [ "$Action" != "export" ] ; then
			printf '%s\n' "" 1>&2
		fi
	fi
	if [ $MatchedNr -eq 0 ] && [ "$ShowInfo" != "0" ] ; then
		if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
			AboutMatchesLog="No ha coincidido ninguna de las entradas DNAT."
		else
			AboutMatchesLog="No DNAT entry matched."
		fi
	fi
	case "$Action" in
		"humanlist" )
			if [ "$FastLists" != "1" ] ; then
#				OutputLog="$(printf '%s\n' "$OutputLog" ; ListadoMostrar "$DirPreparacionListado" "  " "a")"	#"
				ListadoMostrar "$DirPreparacionListado" "  " "a"
				rm -R "$DirPreparacionListado"
			fi
			LinesGroup="$(printf '%s\n' "$IptablesContent" | grep -e "-j REJECT")"	#"
			if [ "$LinesGroup" != "" ] ; then
				# Para manifestar reglas que pueden interferir:
#				OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "")"
				printf '%s\n' ""
#				OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "$LinesGroup")"	#"
				printf '%s\n' "$LinesGroup"
			fi
			AboutMatchesLog=""
			;;
		"export" )
			LinesGroup="$(printf '%s\n' "$IptablesContent" | grep -e "-j REJECT")"	#"
			if [ "$LinesGroup" != "" ] ; then
				# Para manifestar reglas que pueden interferir:
#				OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "")"
				printf '%s\n' ""
#				OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "$LinesGroup" | sed -e 's|^|# |g')"
				printf '%s\n' "$LinesGroup" | sed -e 's|^|# |g'
			fi
			AboutMatchesLog=""
			;;
	esac
	if [ "$AboutMatchesLog" != "" ] ; then
#		OutputLog="$(printf '%s\n' "$OutputLog" ; printf '%s\n' "$AboutMatchesLog")"
		printf '%s\n' "$AboutMatchesLog" 1>&2
	fi
#	OutputLog="$(printf '%s\n' "$OutputLog" | grep -ve '^FirstLineToAvoid$')"
#	if [ "$OutputLog" != "" ] ; then
#		printf '%s\n' "$OutputLog"
#	fi
}

dnat_export ()
# Expected parameters: Same as $1-$9 for DnatFilteredAction()
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local LastStatus=0
	local StatusCode=0
	
	DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id" 'export'
	return $StatusCode
}

dnat_import ()
{
	local RutaFichero="$1"
	local TempScript=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$RutaFichero" != "" ] ; then
		if [ -f "$RutaFichero" ] ; then
			if [ "$(cat "$RutaFichero" | grep -e '^dnat create ')" != "" ] ; then
				TempScript="$(mktemp)"
				chmod u=rwx,go= "$TempScript"
				printf '%s\n' '#!/bin/sh' > "$TempScript"
				printf '%s\n' 'LastStatus=0' >> "$TempScript"
				printf '%s\n' 'StatusCode=0' >> "$TempScript"
				cat "$RutaFichero" | grep -e '^dnat ' | sed -e "s|^|\"${MeExecutable}\" |g" | sed -e 's/$/\nLastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi/g' >> "$TempScript"
				printf '%s\n' 'exit $StatusCode' >> "$TempScript"
				"$TempScript"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				rm "$TempScript"
			else
				printf '%s\n' "${sWARN}W: El fichero no contiene registros DNAT/PAT.${fRESET}"
			fi
		else
			printf '%s\n' "${sERROR}E: Fichero no encontrado para importar.${fRESET}"
			printf '%s\n' "          $RutaFichero"
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "${sERROR}E: Hay que especificar el fichero a importar.${fRESET}"
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

dnat_list ()
# Expected parameters: Same as $1-$9 for DnatFilteredAction()
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local LastStatus=0
	local StatusCode=0
	
	DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Selected_Enabled" "$Selected_Id" 'humanlist'
	return $StatusCode
}

dnat_lista ()
{
	dnat_list "$@"
	return $?
}

dnat_listar ()
{
	dnat_list "$@"
	return $?
}

dnat_ver ()
{
	dnat_list "$@"
	return $?
}

dnat_disable ()
# Expected parameters: Same as $1-$9 for DnatFilteredAction()
# Borra de iptables las entradas DNAT que coincidan con los datos especificados.
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local AllowedProtocols=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_NIC 10 "$Selected_RDev" Selected_RDev
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Protocol 10 "$Selected_Protocol" Selected_Protocol
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Ports 10 "$Selected_RPorts" Selected_RPorts
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OwnIp 10 "$Selected_InputIp" Selected_InputIp "$Ask_NIC"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "Yes" "$Selected_Id" 'disable'
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

dnat_deshabilitar ()
{
	dnat_disable "$@"
	return $?
}

dnat_desactivar ()
{
	dnat_disable "$@"
	return $?
}

dnat_enable ()
# Expected parameters: Same as $1-$9 for DnatFilteredAction()
# Descripción:
#	Re/carga en iptables las entradas DNAT que coincidan con los datos especificados,
# de entre las que hay guardadas.
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local AllowedProtocols=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_NIC 10 "$Selected_RDev" Selected_RDev
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Protocol 10 "$Selected_Protocol" Selected_Protocol
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Ports 10 "$Selected_RPorts" Selected_RPorts
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OwnIp 10 "$Selected_InputIp" Selected_InputIp "$Ask_NIC"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "No" "$Selected_Id" 'enable'
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

dnat_habilitar ()
{
	dnat_enable "$@"
	return $?
}

dnat_activar ()
{
	dnat_enable "$@"
	return $?
}

dnat_create ()
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local ReceptionPort9=""
	local DestPort1=""
	local DestPort9=""
	local EstadoPrevio=""
	local LastStatus=0
	local StatusCode=0
	
	# Compatibility with versions prior to 2.3 :
	if [ "$Selected_RDev" = "." ] ; then Selected_RDev='default' ; fi
	if [ "$Selected_Protocol" = "." ] ; then Selected_Protocol='*' ; fi
	if [ "$Selected_RPorts" = "." ] ; then Selected_RPorts='*' ; fi
	if [ "$Selected_InputIp" = "." ] ; then Selected_InputIp='default' ; fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_NIC 11 "$Selected_RDev" Selected_RDev
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Protocol 11 "$Selected_Protocol" Selected_Protocol
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Ports 11 "$Selected_RPorts" Selected_RPorts
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Selected_RPorts" != "" ] && [ "$Selected_RPorts" != "*" ] ; then
			if [ "$Selected_Protocol" != "" ] && [ "$Selected_Protocol" != "*" ] ; then
				sleep 0
			else
				printf '%s\n' "${sERROR}E: Protocol not specified, and it's required for specific ports.${fRESET}" 1>&2
				LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OwnIp 11 "$Selected_InputIp" Selected_InputIp "$Ask_NIC"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OthersIp 21 "$Selected_DestIp" Selected_DestIp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Selected_DestPorts" = "." ] || [ "$Selected_DestPorts" = "=" ] ; then
		# To be filtered by Ask_Ports
		if [ "$Selected_RPorts" != "*" ] ; then Selected_DestPorts="$Selected_RPorts" ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Ports 21 "$Selected_DestPorts" Selected_DestPorts
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Selected_DestPorts" = "." ] || [ "$Selected_DestPorts" = "=" ] ; then
		# To act after Ask_Ports
		if [ "$Selected_RPorts" != "*" ] ; then Selected_DestPorts="$Selected_RPorts" ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Comment 11 "$Selected_Comment" Selected_Comment
	fi
	
	if [ $StatusCode -eq 0 ] ; then
		Id="$(NewIdForEntry)"
		if [ "$(DnatAlreadyEnabled "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp")" = "" ] ; then
			Enable1Dnat "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Id"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] &&  [ "$ShowInfo" != "0" ] ; then
				if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
					printf '%s\n' "Entrada DNAT ${Selected_Protocol}/${Selected_RPorts} habilitada para ${Selected_RDev}/${Selected_InputIp}"
				else
					printf '%s\n' "DNAT input ${Selected_Protocol}/${Selected_RPorts} enabled for ${Selected_RDev}/${Selected_InputIp}"
				fi
			fi
			sysctl -w net/ipv4/ip_forward=1 > /dev/null
		else
			EstadoPrevio="${EstadoPrevio}A"
		fi
		if [ "$(DnatAlreadySaved "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp")" != "1" ] ; then
#		if [ "$(DnatAlreadySaved "$RespuestaDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp")" != "1" ] ; then
			Save1Dnat "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Id"
#			Save1Dnat "$RespuestaDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "$Id"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
					printf '%s\n' "Entrada DNAT guardada para permitir la carga automatica."
				else
					printf '%s\n' "DNAT input saved to allow automatic loading."
				fi
			fi
		else
			EstadoPrevio="${EstadoPrevio}G"
		fi
		ContenidoIptables="$($ITScmd | grep -e "-j REJECT")"
		if [ "$ContenidoIptables" != "" ] ; then
			# Para manifestar reglas que pueden interferir:
			printf '%s\n' ""
			printf '%s\n' "$ContenidoIptables"
			if [ -f /etc/sysconfig/iptables ] ; then
				ContenidoIptables="$(cat /etc/sysconfig/iptables | grep -e "-j REJECT")"
				if [ "$ContenidoIptables" != "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "/etc/sysconfig/iptables"
					printf '%s\n' "$ContenidoIptables"
				fi
			fi
		fi
	fi
	if [ "$EstadoPrevio" = "AG" ] && [ "$ShowWarnings" != "0" ] ; then
		printf '%s\n' "${sWARN}W: Una entrada DNAT ${Selected_Protocol}/${Selected_RPorts} para $Selected_RDev ya estaba activa y guardada. Se evita la duplicidad.${fRESET}"
	fi
	return $StatusCode
}

dnat_crear ()
{
	dnat_create "$@"
	return $?
}

dnat_remove ()
# Expected parameters: Same as $1-$9 for DnatFilteredAction()
# Borra de iptables las entradas DNAT que coincidan con los datos especificados.
{
	local Selected_RDev="$1"
	local Selected_Protocol="$2"
	local Selected_RPorts="$3"
	local Selected_InputIp="$4"
	local Selected_DestIp="$5"
	local Selected_DestPorts="$6"
	local Selected_Comment="$7"
	local Selected_Enabled="$8"
	local Selected_Id="$9"
	local AllowedProtocols=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Ask_NIC 10 "$Selected_RDev" Selected_RDev
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Protocol 10 "$Selected_Protocol" Selected_Protocol
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_Ports 10 "$Selected_RPorts" Selected_RPorts
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Ask_OwnIp 10 "$Selected_InputIp" Selected_InputIp "$Ask_NIC"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		DnatFilteredAction "$Selected_RDev" "$Selected_Protocol" "$Selected_RPorts" "$Selected_InputIp" "$Selected_DestIp" "$Selected_DestPorts" "$Selected_Comment" "" "$Selected_Id" 'remove'
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

dnat_delete ()
{
	dnat_remove "$@"
	return $?
}

dnat_eliminar ()
{
	dnat_remove "$@"
	return $?
}

dnat ()
{
	local AccionLlamada="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
				printf '%s\n' "Acciones para dar entrada a un equipo local:"
				printf '%s\n' ""
#				printf '%s\n' "	dnat listar      (Ver las entradas DNAT existentes)"
#				printf '%s\n' "	dnat crear       (Agregar una entrada DNAT)"
#				printf '%s\n' "	dnat eliminar    (Eliminar una de las entradas DNAT)"
#				printf '%s\n' "	dnat activar     (Habilitar una entrada guardada)"
#				printf '%s\n' "	dnat desactivar  (Deshabilitar una entrada sin borrar la guardada)"
				printf '%s\n' "	dnat list        (Ver las entradas DNAT existentes)"
				printf '%s\n' "	dnat create      (Agregar una entrada DNAT)"
				printf '%s\n' "	dnat delete      (Eliminar una de las entradas DNAT)"
				printf '%s\n' "	dnat enable      (Habilitar una entrada guardada)"
				printf '%s\n' "	dnat disable     (Deshabilitar una entrada sin borrar la guardada)"
				printf '%s\n' "	dnat export      (Mostrar entradas DNAT en formato portable)"
				printf '%s\n' "	dnat import      (Crear entradas DNAT desde un fichero portado)"
			else
				printf '%s\n' "Actions to give entrance to a local computer:"
				printf '%s\n' ""
				printf '%s\n' "	dnat list      (See the existing DNAT inputs)"
				printf '%s\n' "	dnat create    (Add a DNAT input)"
				printf '%s\n' "	dnat delete    (Remove one of the DNAT inputs)"
				printf '%s\n' "	dnat enable    (Activate a saved entry)"
				printf '%s\n' "	dnat disable   (Deactivate an entry without removing the saved)"
				printf '%s\n' "	dnat export    (Show DNAT inputs in portable format)"
				printf '%s\n' "	dnat import    (Create DNAT inputs from a ported file)"
			fi
			;;
		* )
			dnat_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

shaping ()
{
	local AccionLlamada="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
				printf '%s\n' "Acciones para dar entrada a un equipo local:"
				printf '%s\n' ""
#				printf '%s\n' "	frenos listar		(Ver las moderaciones existentes)"
#				printf '%s\n' "	frenos crear		(Establecer moderacion para un dispositivo/IP)"
#				printf '%s\n' "	frenos eliminar		(Eliminar una moderacion)"
#				printf '%s\n' "	frenos activar		(Habilitar una moderacion guardada)"
#				printf '%s\n' "	frenos desactivar	(Deshabilitar una moderacion sin borrar la guardada)"
				printf '%s\n' "	frenos list     (Ver las moderaciones existentes)"
				printf '%s\n' "	frenos create   (Establecer moderacion para un dispositivo/IP)"
				printf '%s\n' "	frenos delete   (Eliminar una moderacion)"
				printf '%s\n' "	frenos enable   (Habilitar una moderacion guardada)"
				printf '%s\n' "	frenos disable  (Deshabilitar una moderacion sin borrar la guardada)"
			else
				printf '%s\n' "Actions to give entrance to a local computer:"
				printf '%s\n' ""
				printf '%s\n' "	shaping list		(See the existing moderations)"
				printf '%s\n' "	shaping create		(Set moderation for a device/IP)"
				printf '%s\n' "	shaping delete		(Remove one moderation)"
				printf '%s\n' "	shaping enable		(Activate a saved moderation)"
				printf '%s\n' "	shaping disable		(Deactivate a moderation without removing the saved)"
			fi
			;;
		* )
			shaping_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

LimpiarTablas ()
# Eliminar todo de iptables
{
	local LastStatus=0
	local StatusCode=0
	
	$ITcmd --flush
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	$ITcmd --table nat --flush
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	$ITcmd --delete-chain
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	$ITcmd --table nat --delete-chain
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
			printf '%s\n' "Todas las reglas en iptables borradas."
		else
			printf '%s\n' "All rules in iptables deleted."
		fi
	fi
	return $StatusCode
}

UpScriptPath ()
{
# Sintaxis como función: $(UpScriptPath "$Mejorar")
# Descripción:
#	Devuelve la ruta adecuada del script a ser ejecutado al levantar una interfaz
#	de red, si se encuentra posible que éste intercepte el evento.
# Parámetros esperados:
#	$1	(opcional) "1" para modificar el sistema de eventos (ifup/boot) si
#		es necesario para implementar el programillo de la forma adecuada.

	local Mejorar="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Value=""
	local ToSearch=""
	local NewPath=""
	local RunParts=""
	local Comment=""
	
	ToSearch="
			:/etc/network/if-post-up.d:
			:/etc/network/if-up.d:
			:/etc/sysconfig/network-scripts/if-post-up.d:
			:/etc/sysconfig/network-scripts/if-up.d:
			:/etc/sysconfig/network-scripts/ifup-post.d:
			:/etc/sysconfig/network-scripts/ifup.d:
			:/etc/sysconfig/networking/if-post-up.d:
			:/etc/sysconfig/networking/if-up.d:
			:/etc/sysconfig/networking/ifup-post.d:
			:/etc/sysconfig/networking/ifup.d:
			:/etc/sysconfig/network/if-post-up.d:
			:/etc/sysconfig/network/if-up.d:
			:/etc/sysconfig/network/ifup-post.d:
			:/etc/sysconfig/network/ifup.d:
			"
	NewPath="$(PrimerElementoEncontrado -d "$ToSearch")"
	if [ "$NewPath" != "" ] ; then
		Value="${NewPath}/${ServiceName}"
	else
		if [ "$Mejorar" = "1" ] ; then
			ToSearch="
					:/etc/sysconfig/network-scripts/ifup-post:
					:/etc/sysconfig/network-scripts/ifup:
					:/etc/sysconfig/networking/ifup-post:
					:/etc/sysconfig/networking/ifup:
					:/etc/sysconfig/network/ifup-post:
					:/etc/sysconfig/network/ifup:
					"
			NewPath="$(PrimerElementoEncontrado +fx "$ToSearch")"
			if [ "$NewPath" != "" ] ; then
				RunPartsPasaArgs="$(run-parts --help 2>&1 | grep -e "--arg=")"
				LastStatus=$?
				if [ "$RunPartsPasaArgs" = "" ] || [ $LastStatus -ne 0 ] ; then RunPartsPasaArgs="" ; fi
				Comment="# Patched by $ServiceName"
				if [ "$(cat "$NewPath" | tr -s '\t' ' ' | grep -e "^REALDEVICE=" -e " REALDEVICE=")" != "" ] ; then
					Comment="if [ \"\$IFACE\" = \"\" ] ; then export IFACE=\"\$REALDEVICE\" ; fi	$Comment"
				else
					if [ "$(cat "$NewPath" | tr -s '\t' ' ' | grep -e "^DEVICE=" -e " DEVICE=")" != "" ] ; then
						Comment="if [ \"\$IFACE\" = \"\" ] ; then export IFACE=\"\$DEVICE\" ; fi	$Comment"
					fi
				fi
				AddRunpartsToScript "$NewPath" e "${NewPath}.d" "$Comment" "\"\$1\"" "\"\$2\"" "\"\$3\"" "\"\$4\"" "\"\$5\"" "\"\$6\""
				Value="${NewPath}.d/${ServiceName}"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

DownScriptPath ()
{
# Sintaxis como función: $(DownScriptPath "$Mejorar")
# Descripción:
#	Devuelve la ruta adecuada del script a ser ejecutado al detener una interfaz
#	de red, si se encuentra posible que éste intercepte el evento.
# Parámetros esperados:
#	$1	(opcional) "1" para modificar el sistema de eventos (ifdown/halt) si
#		es necesario para implementar el programillo de la forma adecuada.

	local Mejorar="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Value=""
	local ToSearch=""
	local NewPath=""
	local RunParts=""
	
	ToSearch="
			:/etc/network/if-pre-down.d:
			:/etc/network/if-down.d:
			:/etc/sysconfig/network-scripts/if-pre-down.d:
			:/etc/sysconfig/network-scripts/if-down.d:
			:/etc/sysconfig/network-scripts/ifdown-pre.d:
			:/etc/sysconfig/network-scripts/ifdown.d:
			:/etc/sysconfig/networking/if-pre-down.d:
			:/etc/sysconfig/networking/if-down.d:
			:/etc/sysconfig/networking/ifdown-pre.d:
			:/etc/sysconfig/networking/ifdown.d:
			:/etc/sysconfig/network/if-pre-down.d:
			:/etc/sysconfig/network/if-down.d:
			:/etc/sysconfig/network/ifdown-pre.d:
			:/etc/sysconfig/network/ifdown.d:
			"
	NewPath="$(PrimerElementoEncontrado -d "$ToSearch")"
	if [ "$NewPath" != "" ] ; then
		Value="${NewPath}/${ServiceName}"
	else
		if [ "$Mejorar" = "1" ] ; then
			ToSearch="
					:/etc/sysconfig/network-scripts/ifdown-pre:
					:/etc/sysconfig/network-scripts/ifdown:
					:/etc/sysconfig/networking/ifdown-pre:
					:/etc/sysconfig/networking/ifdown:
					:/etc/sysconfig/network/ifdown-pre:
					:/etc/sysconfig/network/ifdown:
					"
			NewPath="$(PrimerElementoEncontrado +fx "$ToSearch")"
			if [ "$NewPath" != "" ] ; then
				RunPartsPasaArgs="$(run-parts --help 2>&1 | grep -e "--arg=")"
				LastStatus=$?
				if [ "$RunPartsPasaArgs" = "" ] || [ $LastStatus -ne 0 ] ; then RunPartsPasaArgs="" ; fi
				Comment="# Patched by $ServiceName"
				if [ "$(cat "$NewPath" | tr -s '\t' ' ' | grep -e "^REALDEVICE=" -e " REALDEVICE=")" != "" ] ; then
					Comment="if [ \"\$IFACE\" = \"\" ] ; then export IFACE=\"\$REALDEVICE\" ; fi	$Comment"
				else
					if [ "$(cat "$NewPath" | tr -s '\t' ' ' | grep -e "^DEVICE=" -e " DEVICE=")" != "" ] ; then
						Comment="if [ \"\$IFACE\" = \"\" ] ; then export IFACE=\"\$DEVICE\" ; fi	$Comment"
					fi
				fi
				AddRunpartsToScript "$NewPath" e "${NewPath}.d" "$Comment" "\"\$1\"" "\"\$2\"" "\"\$3\"" "\"\$4\"" "\"\$5\"" "\"\$6\""
				Value="${NewPath}.d/${ServiceName}"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

DevicesUpDown ()
# Expected parameters:
#	$1	"up" or "down"
#	(rest)	NIC names to configure, or nothing to configure all enabled devices
{
	local Mode="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local GatewayDev=''
	local InterfacesList=''
	local LastStatus=0
	local StatusCode=0
	
	GatewayDev="$(DefaultNICs | head -n 1)"
	if [ "$GatewayDev" = "" ] && [ $# -eq 1 ] && [ "$PHASE" = "post-down" ] ; then
		# Patch for NetworkManager calling if-post-down
		GatewayDev="$1"
	fi
	InterfacesList="$(ListaDispositivosRed)"
	CacheScripts="$(ls -1 "${SystemConfigDir}/"*"/if-${Mode}_fast.sh" 2>/dev/null)"
	IFS="$(printf "\n\b")" ; for CurScript in $CacheScripts ; do unset IFS
		CurDev="$(printf '%s\n' "$CurScript" | sed -e "s|^${SystemConfigDir}/||g" | sed -e 's|^/||g' | cut -f 1 -d '/')"
		CurIp=''
		CurDo=0
		if [ $# -gt 0 ] ; then
			for OneSpecifiedDev in $* ; do
				if [ "$CurDev" = "$OneSpecifiedDev" ] ; then
					CurDo=1
				else
					if [ "$CurDev" = "to-wan" ] || [ "$CurDev" = "gateway" ] ; then
						if [ "$OneSpecifiedDev" = "$GatewayDev" ] && [ "$GatewayDev" != "" ] ; then
							CurDo=1
						fi
					fi
				fi
			done
		else
			if [ "$(printf '%s\n' " $InterfacesList " | grep -e " $CurDev ")" != "" ] ; then
				CurDo=1
			else
				if [ "$CurDev" = "_any" ] ; then
					if [ "$Mode" = "up" ] ; then
						CurDo=1
					else
						if [ $(printf '%s\n' "$InterfacesList" | wc -l) -eq 1 ] ; then
							# any down: Only if it's the only device.
							CurDo=1
						fi
					fi
				else
					if [ "$CurDev" = "to-wan" ] || [ "$CurDev" = "gateway" ] ; then
						if [ "$(printf '%s\n' " $InterfacesList " | grep -e " $GatewayDev ")" != "" ] && [ "$GatewayDev" != "" ] ; then
							CurDo=1
						fi
					fi
				fi
			fi
			if [ $CurDo -eq 1 ] ; then
				if [ "$CurDev" = "to-wan" ] || [ "$CurDev" = "gateway" ] ; then
					CurIp="$(IpDeDispositivo $GatewayDev)"
				else
					CurIp="$(IpDeDispositivo $CurDev)"
				fi
				if [ "$CurIp" != "" ] ; then
					CurDo=1
				else
					CurDo=0
				fi
			fi
		fi
		if [ $CurDo -eq 1 ] ; then
			if [ "$CurIp" = "" ] ; then
				if [ "$CurDev" = "to-wan" ] || [ "$CurDev" = "gateway" ] ; then
					CurIp="$(IpDeDispositivo $GatewayDev)"
				else
					CurIp="$(IpDeDispositivo $CurDev)"
				fi
			fi
			if [ "$CurDev" = "to-wan" ] || [ "$CurDev" = "gateway" ] ; then
				printf '%s\n' "Device ${Mode}: $GatewayDev ${ParO}${CurIp}${ParC} $CurDev"
				LogProgram 4 "$CurScript $GatewayDev $CurIp"
				"$CurScript" $GatewayDev $CurIp
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				printf '%s\n' "Device ${Mode}: $CurDev ${ParO}${CurIp}${ParC}"
				LogProgram 4 "$CurScript $CurDev $CurIp"
				"$CurScript" $CurDev $CurIp
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	done
	return $StatusCode
}

UpDownScript ()
{
	local Event="$1"
	local Action=''
	printf '%s\n' '#!/bin/sh'
	printf '%s\n' "# $ShortDescription ${ParO}$ServiceName${ParC}"
	printf '%s\n' "# Trigger for NIC $Event event."
	printf '%s\n' ''
	printf '%s\n' 'Interface="$1"'
	printf '%s\n' 'WanIp="$2"'
	if [ -d /sys/class/net ] ; then
		printf '%s\n' 'if [ "$Interface" = "" ] && [ "$IFACE" != "" ] && [ -e "/sys/class/net/${IFACE}" ] ; then Interface="$IFACE" ; fi'
		printf '%s\n' 'if [ "$Interface" = "" ] && [ "$DEVICE_IFACE" != "" ] && [ -e "/sys/class/net/${DEVICE_IFACE}" ] ; then Interface="$DEVICE_IFACE" ; fi'
		printf '%s\n' 'if [ "$Interface" = "" ] && [ "$LOGICAL" != "" ] && [ -e "/sys/class/net/${LOGICAL}" ] ; then Interface="$LOGICAL" ; fi'
	else
		printf '%s\n' 'if [ "$Interface" = "" ] ; then Interface="$IFACE" ; fi'
		printf '%s\n' 'if [ "$Interface" = "" ] ; then Interface="$DEVICE_IFACE" ; fi'
		printf '%s\n' 'if [ "$Interface" = "" ] ; then Interface="$LOGICAL" ; fi'
	fi
	printf '%s\n' 'if [ "$WanIp" = "" ] && [ "$Interface" != "" ] ; then'
	printf '%s\n' '	WanIp="$(ip address show "$Interface" | grep -e "inet " | sed -e "s|.*inet ||" | cut -f 1 -d "/")"'
	printf '%s\n' 'fi'
	if [ "$Event" = "up" ] ; then
		Action='start'
	else
		Action='stop'
	fi
	printf '%s\n' 'DatetimeStamp="$(date '+%Y-%m-%dT%T%z')"'
	printf '%s\n' "printf '%s\n' \"\$DatetimeStamp \$ \\\"${ProgramExecutablePath}\\\" $Action \"\$Interface\" \"\$WanIp\"\" >> \"${MainControllerLog}\""
	printf '%s\n' "\"${ProgramExecutablePath}\" $Action \"\$Interface\" \"\$WanIp\" 2>&1 | tee -a \"${MainControllerLog}\""
	printf '%s\n' 'exit $?'
}

Service_autostart ()
{
#	local Modalidad="$1"	Constante provisional hasta desarrollar if-up_strict.sh y if-down_strict.sh
	local Modalidad="fast"
	local TheScriptUp=""
	local TheScriptDown=""
	local LastStatus=0
	local StatusCode=0
	
	case "$Modalidad" in
		"rapido" )	Modalidad="fast" ;;
		"rapida" )	Modalidad="fast" ;;
		"fast" )	Modalidad="fast" ;;
		"estricto" )	Modalidad="strict" ;;
		"estricte" )	Modalidad="strict" ;;
		"estricta" )	Modalidad="strict" ;;
		"strict" )	Modalidad="strict" ;;
		* )		Modalidad="strict" ;;
	esac
	TheScriptUp="$(UpScriptPath 1)"
	TheScriptDown="$(DownScriptPath 1)"
	if [ "$TheScriptUp" != "" ] && [ "$TheScriptDown" != "" ] && [ "$TheScriptDown" != "$TheScriptUp" ] ; then
		mkdir -p "$(dirname "$TheScriptUp")"
		mkdir -p "$(dirname "$TheScriptDown")"
		if [ "$TheScriptDown" != "$TheScriptUp" ] ; then
			if [ -f "$TheScriptUp" ] || [ -L "$TheScriptUp" ] ; then
				printf '%s\n' "Se reescribe el lanzador: $TheScriptUp"
			else
				printf '%s\n' "Se crea el lanzador: $TheScriptUp"
			fi
			UpDownScript up > "$TheScriptUp"
			chmod +x "$TheScriptUp"
			if [ -f "$TheScriptDown" ] || [ -L "$TheScriptDown" ] ; then
				printf '%s\n' "Se reescribe el lanzador: $TheScriptDown"
			else
				printf '%s\n' "Se crea el lanzador: $TheScriptDown"
			fi
			UpDownScript down > "$TheScriptDown"
			chmod +x "$TheScriptDown"
		fi
		if [ "$(printf '%s\n' "$TheScriptUp" | grep -e '\.d/')" != "" ] || [ "$(printf '%s\n' "$TheScriptDown" | grep -e '\.d/')" != "" ] ; then
			if ! Is_Executable run-parts ; then
				printf '%s\n' "${sWARN}W: No se encuentra el programa run-parts para que se levante o detenga el servicio con la red.${fRESET}" 1>&2
			fi
		fi
	else
		EnableObject 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	# Tested (NM 0.9+ on Ubuntu since 11.10 upto 16.04):
	#	/etc/default/NetworkManager
	#	/etc/NetworkManager/nm-system-settings.conf
	#	/etc/NetworkManager/NetworkManager.conf
	# With no differences on most Ubuntu versions: up/down events seem to be triggered with any ifupdown/managed configuration
	# and /etc/default/NetworkManager doesn't override directly /etc/NetworkManager/*.conf (pending to test docummented OPTS variable)
	NMConf="/etc/NetworkManager/nm-system-settings.conf"
	if [ ! -f "$NMConf" ] ; then NMConf="/etc/NetworkManager/NetworkManager.conf" ; fi
	if [ -f "$NMConf" ] ; then
		if [ "$(IniVarValue "$NMConf" managed ifupdown '' = '')" != "true" ] ; then
			printf '%s\n' "Setting NetworkManager to trigger ifupdown events" 1>&2
			SetIniVarValue "$NMConf" managed ifupdown true = "# ifupdown configured by ${ServiceName}"
			if [ "$(PsOutputValues comm | grep -ie '^NetworkManager')" != "" ] ; then
				printf '%s\n' "${sWARN}W: NetworkManager seems to be running; NM restart is needed to ifupdown events be triggered.${fRESET}" 1>&2
				NMVersion="$(NetworkManager --version 2>/dev/null | cut -f 1 -d '.')"
				if ! Is_IntegerNr $NMVersion ; then
					# NM prior to v0.9 didn't support --version parameter
					printf '%s\n' "${sERROR}E: NetworkManager version seems to not conform ifupdown events.${fRESET}" 1>&2
					printf '%s\n' "   You probably will need to enable/disable NAT/DNAT entries manually." 1>&2
					LastStatus=56 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ "$TheScriptDown" = "/etc/network/if-down.d/${ServiceName}" ] && [ -d "/etc/network/if-post-down.d" ] ; then
			printf '%s\n' "Patching if-down to NM uses if-post-down." 1>&2
			rm -f "/etc/network/if-post-down.d/${ServiceName}"
			ln -s "$TheScriptDown" "/etc/network/if-post-down.d/${ServiceName}"
		fi
	fi
	return $StatusCode
}

Service_autoiniciar ()
{
	Service_autostart "$@"
	return $?
}

Service_autoinicio ()
{
	Service_autostart "$@"
	return $?
}

Service_manual ()
{
	local SwitchedSignalFile="$1"	# (optional) File path to create if sutostart was removed (nothing if there wasn't autostart set)
	local TheScriptUp=""
	local TheScriptDown=""
	
	TheScriptUp="$(UpScriptPath 0)"
	TheScriptDown="$(DownScriptPath 0)"
	if [ -f "$TheScriptUp" ] ; then
		printf '%s\n' "Se elimina el lanzador: $TheScriptUp"
		rm "$TheScriptUp"
		if [ "$SwitchedSignalFile" != "" ] ; then date '+%Y-%m-%dT%T%z' > "$SwitchedSignalFile" ; fi
	fi
	if [ -f "$TheScriptDown" ] ; then
		printf '%s\n' "Se elimina el lanzador: $TheScriptDown"
		rm "$TheScriptDown"
		if [ "$SwitchedSignalFile" != "" ] ; then date '+%Y-%m-%dT%T%z' > "$SwitchedSignalFile" ; fi
	fi
	if [ -f "/etc/network/if-post-down.d/${ServiceName}" ] ; then
		# Patch link for NetworkManager
		printf '%s\n' "Se elimina el lanzador: /etc/network/if-post-down.d/${ServiceName}"
		rm "/etc/network/if-post-down.d/${ServiceName}"
		if [ "$SwitchedSignalFile" != "" ] ; then date '+%Y-%m-%dT%T%z' > "$SwitchedSignalFile" ; fi
	fi
	if [ "$TheScriptUp" != "" ] || [ "$TheScriptDown" != "" ] ; then
		DisableObject 0
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		DisableObject 1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
}

Service_noautoiniciar ()
{
	Service_manual "$@"
	return $?
}

Service_noautoinicio ()
{
	Service_manual "$@"
	return $?
}

Service_noiniciar ()
{
	Service_manual "$@"
	return $?
}

Service_noautostart ()
{
	Service_manual "$@"
	return $?
}

Service_nostart ()
{
	Service_manual "$@"
	return $?
}

Service_noinicio ()
{
	Service_manual "$@"
	return $?
}

Service_export ()
{
	local LastStatus=0
	local StatusCode=0
	
	nat_export "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	printf '%s\n' ""
	dnat_export "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Service_import ()
{
	local RutaFichero="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$RutaFichero" != "" ] ; then
		if [ -f "$RutaFichero" ] ; then
			nat_import "$RutaFichero"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			dnat_import "$RutaFichero"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "${sERROR}E: Fichero no encontrado para importar.${fRESET}"
			printf '%s\n' "          $RutaFichero"
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "${sERROR}E: Hay que especificar el fichero a importar.${fRESET}"
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Service ()
# Word "service" in spanish to not conflict with "service" OS command.
{
	local AccionLlamada="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			if [ "$TheUserLanguage" = "ca" ] || [ "$TheUserLanguage" = "es" ] ; then
				printf '%s\n' "Acciones sobre el router entero:"
				printf '%s\n' ""
#				printf '%s\n' "	service autoinicio   (Establecer que cargue $ServiceName con la red)"
				printf '%s\n' "	service autostart    (Establecer que cargue $ServiceName con la red)"
#				printf '%s\n' "	service enarranque rapido	(Cargar con el arranque, pero sin tanta verificacion)"
#				printf '%s\n' "	service noinicio     (Establecer que $ServiceName no cargue con la red)"
#				printf '%s\n' "	service nostart      (Establecer que $ServiceName no cargue con la red)"
				printf '%s\n' "	service manual       (Establecer que $ServiceName no cargue con la red)"
				printf '%s\n' "	service export       (Mostrar reglas NAT y DNAT en formato portable)"
				printf '%s\n' "	service import       (Crear reglas NAT y DNAT desde un fichero portado)"
			else
				printf '%s\n' "Actions over the entire router:"
				printf '%s\n' ""
				printf '%s\n' "	service autostart   (Set to load $ServiceName with the net)"
#				printf '%s\n' "	service onboot fast	(Load with the boot, but without many verifications)"
#				printf '%s\n' "	service nostart     (Set $ServiceName to don't load with the net)"
				printf '%s\n' "	service manual      (Set $ServiceName to don't load with the net)"
				printf '%s\n' "	service export      (Show NAT&DNAT rules in portable format)"
				printf '%s\n' "	service import      (Create NAT&DNAT rules from a ported file)"
			fi
			;;
		* )
			Service_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

#service ()	Word conflicts with System command
#{
#	Service "$@"
#	return $?
#}

servicio ()
{
	Service "$@"
	return $?
}

Unban4Fail ()
# This function is developed in ctctl and replicated to durruter
# To make fail2ban ignore addres(ses) use file /etc/fail2ban/filter.d/whitelist.conf
# https://www.fail2ban.org/wiki/index.php/Whitelist
# Depends on functions: ParentPstree PsOutputValues EsIP Is_Executable PrepareExitcodeToFile ExecuteString GetFiledExitcode
# Recommends other functions: RunInLxc
{
	local IpToUnban="$1"
	local ContainersList=''
	local CurVps=''
	local CurFail2ban=''
	local Jails=''
	local CurJail=''
	local ExitcodesFile=''
	local JailsNr=0
	local CallsTree=''
	local CurPid=''
	local CurTty=''
	local SomeTty=''
	local CurIP=''
	local MaxLongNames=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	ExitcodesFile="${DirTemp}/Unban4Fail.result"
	if [ "$IpToUnban" = "me" ] ; then
		CallsTree="$(ParentPstree $$ | tr -s ' ' '\n' | tac)"
		for CurPid in $CallsTree ; do
			if [ "$(PsOutputValues comm $CurPid | grep -e 'ssh')" != "" ] ; then
				CurTty="$(PsOutputValues cmd $CurPid | tr -s '\t' ' ' | tr -s ' ' '\n' | grep -ie '.@pts')"
				if [ "$CurTty" != "" ] ; then
					printf '%s\n' "Served session: $CurTty"
					CurTty="$(printf '%s' "$CurTty" | cut -f 2 -d '@')"
					SomeTty="$CurTty"
					CurIP="$(w -h | tr -s '\t' ' ' | grep -e ". $CurTty " | grep -ve " .* $CurTty " | tail -n 1 | cut -f 3 -d ' ')"
					if [ "$(EsIP "$CurIP")" = "1" ] ; then
						printf '%s\n' "Processing your arrival IP: $CurIP"
						IpToUnban="$CurIP"
					else
						CurIP=''
					fi
				fi
			fi
		done
		if [ "$CurIP" = "" ] ; then
			if [ "$SomeTty" != "" ] ; then
				printf '%s\n' "${sERROR}E: Arrival IP not detected from your session.${fRESET}" 1>&2
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				printf '%s\n' "${sERROR}E: No served pts session detected to get your arrival IP.${fRESET}" 1>&2
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(EsIP "$IpToUnban")" = "1" ] ; then
			if Is_Executable vzctl && Is_OpenvzPresentRunning ; then
				ContainersList="$(vzlist -H -o vpsid)"
				for CurVps in $ContainersList ; do
					printf '%s\n' "== Container: $CurVps =="
					CurFail2ban="$(vzctl exec "$CurVps" which fail2ban-client)"
					if [ "$CurFail2ban" != "" ] ; then
						JailsNr=0
						Jails="$(vzctl exec "$CurVps" fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
						for CurJail in $Jails ; do
							if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
						done
						for CurJail in $Jails ; do
							JailsNr=$((JailsNr + 1))
							printf "[%-${MaxLongNames}s] " "$CurJail"
#							CommandExitToFile "$ExitcodesFile" $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
#							LastStatus=$(cat "$ExitcodesFile")
#							if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#							if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							PrepareExitcodeToFile
							ExecuteString $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
							GetFiledExitcode $?
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						done
						if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
					else
						printf '%s\n' "No fail2ban service found."
					fi
				done
			fi
			if Is_Executable lxc-create && Is_LxcPresentRunning 2>/dev/null ; then
#				On LXC v4, only "--nesting=0" shows top containers.
#				ContainersList="$(sudo -iu "$UnprivilegedUser" -- lxc-ls --nesting=9 -1 -F NAME)"
				ContainersList="$(sudo -iu "$UnprivilegedUser" -- lxc-ls -1 -F NAME)"
				for CurVps in $ContainersList ; do
					printf '%s\n' "== Container: $CurVps =="
					CurFail2ban="$(RunInLxc "$CurVps" '' which fail2ban-client)"
					if [ "$CurFail2ban" != "" ] ; then
						JailsNr=0
						Jails="$(RunInLxc "$CurVps" '' fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
						for CurJail in $Jails ; do
							if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
						done
						for CurJail in $Jails ; do
							JailsNr=$((JailsNr + 1))
							printf "[%-${MaxLongNames}s] " "$CurJail"
#							CommandExitToFile "$ExitcodesFile" RunInLxc "$CurVps" '' fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
#							LastStatus=$(cat "$ExitcodesFile")
#							if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#							if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							PrepareExitcodeToFile
							ExecuteString RunInLxc "$CurVps" '' fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
							GetFiledExitcode $?
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						done
						if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
					else
						printf '%s\n' "No fail2ban service found."
					fi
				done
			fi
			printf '%s\n' "== Host: $(hostname) =="
			JailsNr=0
			Jails="$(fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
			for CurJail in $Jails ; do
				if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
			done
			for CurJail in $Jails ; do
				JailsNr=$((JailsNr + 1))
				printf "[%-${MaxLongNames}s] " "$CurJail"
#				CommandExitToFile "$ExitcodesFile" fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
#				LastStatus=$(cat "$ExitcodesFile")
#				if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				PrepareExitcodeToFile
				ExecuteString fail2ban-client set "$CurJail" unbanip $IpToUnban 2>&1 | grep -ve 'NOK:'
				GetFiledExitcode $?
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
			if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
			printf '%s\n' ""
		else
			printf '%s\n' "${sERROR}E: An IPv4 must be specified to be removed from fail2ban blacklists.${fRESET}" 1>&2
			LastStatus=65 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	rm -f "$ExitcodesFile"
	return $StatusCode
}

ListBans4Fail ()
# THIS FUNCTION is developed in software project: ctctl
# THIS FUNCTION is also embedded to: durruter
# Depends on functions: ParentPstree PsOutputValues EsIP Is_Executable PrepareExitcodeToFile ExecuteString GetFiledExitcode
{
	local FilterBans="$1"
	local FilterBansRegexp=''
	local ContainersList=''
	local CurVps=''
	local CurFail2ban=''
	local Jails=''
	local CurJail=''
	local CurBans=''
	local ExitcodesFile=''
	local JailsNr=0
	local CallsTree=''
	local CurPid=''
	local CurTty=''
	local SomeTty=''
	local CurIP=''
	local MaxLongNames=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ "$FilterBans" = "me" ] ; then
		CallsTree="$(ParentPstree $$ | tr -s ' ' '\n' | tac)"
		for CurPid in $CallsTree ; do
			if [ "$(PsOutputValues comm $CurPid | grep -e 'ssh')" != "" ] ; then
				CurTty="$(PsOutputValues cmd $CurPid | tr -s '\t' ' ' | tr -s ' ' '\n' | grep -ie '.@pts')"
				if [ "$CurTty" != "" ] ; then
					printf '%s\n' "Served session: $CurTty"
					CurTty="$(printf '%s' "$CurTty" | cut -f 2 -d '@')"
					SomeTty="$CurTty"
					CurIP="$(w -h | tr -s '\t' ' ' | grep -e ". $CurTty " | grep -ve " .* $CurTty " | tail -n 1 | cut -f 3 -d ' ')"
					if [ "$(EsIP "$CurIP")" = "1" ] ; then
						printf '%s\n' "Processing your arrival IP: $CurIP"
						FilterBans="$CurIP"
					else
						CurIP=''
					fi
				fi
			fi
		done
		if [ "$CurIP" = "" ] ; then
			if [ "$SomeTty" != "" ] ; then
				printf '%s\n' "${sERROR}E: Arrival IP not detected from your session.${fRESET}" 1>&2
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				printf '%s\n' "${sERROR}E: No served pts session detected to get your arrival IP.${fRESET}" 1>&2
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		FilterBansRegexp="$(printf '%s\n' "$FilterBans" | sed -e 's|\.|\\.|g')"
		ExitcodesFile="${DirTemp}/listbans4fail.result"
		if Is_Executable vzctl && Is_OpenvzPresentRunning ; then
			ContainersList="$(vzlist -H -o vpsid)"
			for CurVps in $ContainersList ; do
				printf '%s\n' "== Container: $CurVps =="
				CurFail2ban="$(vzctl exec "$CurVps" which fail2ban-client)"
				if [ "$CurFail2ban" != "" ] ; then
					JailsNr=0
					Jails="$(vzctl exec "$CurVps" fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
					for CurJail in $Jails ; do
						if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
					done
					for CurJail in $Jails ; do
						JailsNr=$((JailsNr + 1))
						printf "[%-${MaxLongNames}s] " "$CurJail"
						PrepareExitcodeToFile
						if [ "$FilterBans" != "" ] ; then
#							CurBans="$(CommandExitToFile "$ExitcodesFile" $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
							CurBans="$(ExecuteString $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
							CurBans="$(echo TrimAndSingle $CurBans | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
							CurBans="$(printf '%s' "$CurBans" | tr -s ' ' '\n' | grep -e "^${FilterBansRegexp}$")"
							if [ "$CurBans" != "" ] ; then
								printf '%s\n' "Banned IP: ${sINFO}${CurBans}${fRESET}"
							else
								printf '%s\n' "Specified IP not banned."
							fi
						else
#							CommandExitToFile "$ExitcodesFile" $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
							ExecuteString $Command $Parameters vzctl exec2 "$CurVps" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
						fi
#						LastStatus=$(cat "$ExitcodesFile")
#						if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#						if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					done
					if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
				else
					printf '%s\n' "No fail2ban service found."
				fi
			done
		fi
		if Is_Executable lxc-create && Is_LxcPresentRunning 2>/dev/null ; then
#			On LXC v4, only "--nesting=0" shows top containers.
#			ContainersList="$(sudo -iu "$UnprivilegedUser" -- lxc-ls --nesting=9 -1 -F NAME)"
			ContainersList="$(sudo -iu "$UnprivilegedUser" -- lxc-ls -1 -F NAME)"
			for CurVps in $ContainersList ; do
				printf '%s\n' "== Container: $CurVps =="
				CurFail2ban="$(RunInLxc "$CurVps" '' which fail2ban-client)"
				if [ "$CurFail2ban" != "" ] ; then
					JailsNr=0
					Jails="$(RunInLxc "$CurVps" '' fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
					for CurJail in $Jails ; do
						if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
					done
					for CurJail in $Jails ; do
						JailsNr=$((JailsNr + 1))
						printf "[%-${MaxLongNames}s] " "$CurJail"
						PrepareExitcodeToFile
						if [ "$FilterBans" != "" ] ; then
#							CurBans="$(CommandExitToFile "$ExitcodesFile" RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
#							CurBans="$(ExecuteString RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
							CurBans="$(RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
							CurBans="$(echo TrimAndSingle $CurBans | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
							CurBans="$(printf '%s' "$CurBans" | tr -s ' ' '\n' | grep -e "^${FilterBansRegexp}$")"
							if [ "$CurBans" != "" ] ; then
								printf '%s\n' "Banned IP: ${sINFO}${CurBans}${fRESET}"
							else
								printf '%s\n' "Specified IP not banned."
							fi
						else
#							CommandExitToFile "$ExitcodesFile" RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
#							ExecuteString RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
							RunInLxc "$CurVps" '' fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
						fi
#						LastStatus=$(cat "$ExitcodesFile")
#						if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#						if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						GetFiledExitcode $?
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					done
					if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
				else
					printf '%s\n' "No fail2ban service found."
				fi
			done
		fi
		printf '%s\n' "== Host: $(hostname) =="
		JailsNr=0
		Jails="$(fail2ban-client status | grep -ie 'jail list.*:' | cut -f 2 -d ':' | tr -s ',' ' ')"
		for CurJail in $Jails ; do
			if [ ${#CurJail} -gt $MaxLongNames ] ; then MaxLongNames=${#CurJail} ; fi
		done
		for CurJail in $Jails ; do
			JailsNr=$((JailsNr + 1))
			printf "[%-${MaxLongNames}s] " "$CurJail"
			PrepareExitcodeToFile
			if [ "$FilterBans" != "" ] ; then
#				CurBans="$(CommandExitToFile "$ExitcodesFile" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
				CurBans="$(ExecuteString fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:' | grep -ie 'IP list:' | cut -f 2- -d ':')"
				CurBans="$(echo TrimAndSingle $CurBans | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				CurBans="$(printf '%s' "$CurBans" | tr -s ' ' '\n' | grep -e "^${FilterBansRegexp}$")"
				if [ "$CurBans" != "" ] ; then
					printf '%s\n' "Banned IP: ${sINFO}${CurBans}${fRESET}"
				else
					printf '%s\n' "Specified IP not banned."
				fi
			else
#				CommandExitToFile "$ExitcodesFile" fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
				ExecuteString fail2ban-client status "$CurJail" 2>&1 | grep -ve 'NOK:'
			fi
#			LastStatus=$(cat "$ExitcodesFile")
#			if [ $LastStatus -eq 255 ] ; then LastStatus=0 ; fi  # 255 seems to only mean "False"
#			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			GetFiledExitcode $?
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
		if [ $JailsNr -eq 0 ] ; then printf '%s\n' "No fail2ban jails found." ; fi
		rm -f "$ExitcodesFile"
	fi
	return $StatusCode
}

Monitor ()
{
	Object="$1"
	if [ "$Object" != "" ] ; then
		ip monitor dev "$Object"
	else
		printf '%s\n' "I: You can specify a NIC device to restrict monitoring to."
		ip monitor
	fi
}

Via ()
{
	local NIC=''
	local NicIP=''
	local RawRules=''
	local CurLine=''
	local CurPriority=''
	local CurTableNum=''
	local CurTableName=''
	local CurRuleString=''
	
	if [ "$1" != "" ] ; then
		if [ "$1" = "tables" ] || [ "$1" = "rules" ] ; then
			ListadoEncabezados "via.$$" 0 "PRIORITY" "PACKET_SELECTION" "RULE_TABLE" "TABLE.num"
			if [ "$2" = "" ] ; then
				RawRules="$(ip rule list)"
			else
				if [ "$(EsIP "$2")" = "1" ] ; then
					RawRules="$(ip rule list from $2 ; ip rule list to $2)"
					RawRules="$(printf '%s' "$RawRules" | sort -u)"
				else
					RawRules="$(ip rule list table "$2")"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			IFS="$(printf "\n\b")" ; for CurLine in $RawRules ; do unset IFS
				CurLine="$(echo TrimAndSingle $CurLine | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				CurPriority="$(printf '%s' "$CurLine" | cut -f 1 -d ':')"
				CurTableName="$(printf '%s' "$CurLine" | sed -e 's|.* lookup ||g')"
				CurTableName="$(echo TrimAndSingle $CurTableName | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				CurTableNum="$(cat /etc/iproute2/rt_tables 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | grep -e ". ${CurTableName}$" | grep -ve " .* ${CurTableName}$" | cut -f 1 -d ' ')"
				CurRuleString="$(printf '%s' "$CurLine" | cut -f 2- -d ':' | sed -e "s| lookup ${CurTableName}$||")"
				CurRuleString="$(echo TrimAndSingle $CurRuleString | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
				ListadoNuevoRegistro "via.$$" "${CurPriority}.${CurRuleString}" "$CurPriority" "$CurRuleString" "$CurTableName" "$CurTableNum"
			done
			ListadoMostrar "via.$$" ' ' a 'p'
		else
			if [ "$1" = "table" ] || [ "$1" = "rule" ] ; then
				if [ "$2" != "" ] ; then
					ip route show table "$2"
				else
					printf '%s\n' "${sERROR}E: Table not specified.${fRESET}" 1>&2
					LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				if [ "$(EsIP "$1")" = "1" ] ; then
					if [ "$2" = "" ] ; then
						NIC="$(ip route get to $1 | sed -e 's|.* dev |dev |g' | grep -e '^dev ' | cut -f 2 -d ' ')"
						NicIP="$(ip route get to $1 | sed -e 's|.* src |src |g' | grep -e '^src ' | cut -f 2 -d ' ')"
						printf '%s\n' "To reach $1 target, local packets will use device \"${NIC}\" with its address \"${NicIP}\""
					else
						if [ "$(EsIP "$2")" = "1" ] ; then
							NIC="$(ip route get from $1 to $2)"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $StatusCode -eq 0 ] ; then
								NIC="$(printf '%s' "$NIC" | sed -e 's|.* dev |dev |g' | grep -e '^dev ' | cut -f 2 -d ' ')"
								printf '%s\n' "From output address $1 , to reach $2 target packets will use device: $NIC"
							else
								if [ $LastStatus -eq 2 ] ; then
									printf '%s\n' "${sWARN}Probably $1 is not a local address.${fRESET}"
								fi
							fi
						else
							printf '%s\n' "${sERROR}E: Syntax error; Unknown address ${2}${fRESET}" 1>&2
							LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					fi
				else
					printf '%s\n' "${sERROR}E: Syntax error; Unknown object ${1}${fRESET}" 1>&2
					LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	fi
	if [ "$1" = "" ] || [ $StatusCode -ne 0 ] ; then
		if [ $StatusCode -ne 0 ] ; then printf '\n' ; fi
		printf '%s\n' "Directions queries:"
		printf '%s\n' ""
		printf '%s\n' "$ServiceName via [IP]                 (Device and IP to be used to reach specified address)"
		printf '%s\n' "$ServiceName via [LocalIP] [RemoteIP] (Device to be used to reach from address to address)"
		printf '%s\n' "$ServiceName via rules                (Rule tables list ; max. priority = 0)"
		printf '%s\n' "$ServiceName via rules [table]        (Rule list for a single table)"
		printf '%s\n' "$ServiceName via rules [IP]           (Rule tables list, filtering from&to a single address)"
		printf '%s\n' "$ServiceName via rule [table]         (Routes content of one rule table)"
		printf '%s\n' ""
		printf '%s\n' "\"rule\" synonym: table"
		printf '%s\n' "$ServiceName via tables               (Rule tables list ; max. priority = 0)"
		printf '%s\n' "$ServiceName via tables [table]       (Rule list for a single table)"
		printf '%s\n' "$ServiceName via tables [IP]          (Rule tables list, filtering from&to a single address)"
		printf '%s\n' "$ServiceName via table [table]        (Routes content of one rule table)"
	fi
}

ReportAddresses ()
{
	local NICs=''
	local CurNIC=''
	local NicAddresses=''
	local CurAddress=''
	local CurIP=''
	local CurIP_Show=''
	local CurMask=''
	local CurName=''
	local CurName_Show=''
	local CurType=''
	local CurKey=''
	local GeoCommand=''
	local GeoCode=''
	local CurNameResolution=''
	local CurIPRegex=''
	local CurIP_IsPublic=0
	
	ListadoEncabezados "$ServiceName.ReportAddresses.$$" A NIC IPv4 MASK TYPE GEO INTER/NET_NAME
	if Is_Executable geoiplookup ; then GeoCommand=geoiplookup ; fi
	NICs="$(ListaDispositivosRed)"
	for CurNIC in $NICs ; do
		NicAddresses="$(IPCDIRFromDevice "$CurNIC")"
		for CurAddress in $NicAddresses ; do
			CurIP="$(printf '%s' "$CurAddress" | cut -f 1 -d '/')"
			CurIP_Show="$CurIP"
			CurMask="$(printf '%s' "$CurAddress" | cut -sf 2 -d '/')"
			GeoCode='-'
			if Is_PublicIPv4 $CurIP ; then
				CurIP_IsPublic=1
			else
				CurIP_IsPublic=0
			fi
			if [ $CurIP_IsPublic -eq 1 ] ; then
				GeoCode='?'
				CurType='public'
				if [ "$GeoCommand" != "" ] ; then
					GeoCode="$(geoiplookup "$CurIP" | cut -f 2 -d ':' | cut -f 1 -d ',' | head -n 1)"
					GeoCode="$(echo TrimAndSingle $GeoCode | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ "$GeoCode" = "" ] ; then GeoCode='?' ; fi
				fi
				# Really short timeout because PublicIP() makes various tries, and unrouted addresses make it to spend all timeout time on each try.
				if [ "$(PublicIP "$CurIP" 1)" != "$CurIP" ] ; then CurIP_Show="${sWARN}${CurIP_Show}!${fRESET}" ; fi
			else
				if Is_PrivateIPv4 $CurIP ; then
					if [ "$(printf '%s' "$CurIP" | grep -e '^127\.')" != "" ] ; then
						CurType='private/myself'
					else
						if [ "$(printf '%s' "$CurIP" | grep -e '^10\.')" != "" ] ; then
							CurType='private/class-A'
						else
							if [ "$(printf '%s' "$CurIP" | grep -e '^172\.')" != "" ] ; then
								CurType='private/class-B'
							else
								if [ "$(printf '%s' "$CurIP" | grep -e '^192\.')" != "" ] ; then
									CurType='private/class-C'
								else
									if [ "$(printf '%s' "$CurIP" | grep -e '^169\.254\.')" != "" ] ; then
										CurType='private/Link-local'
									else
										CurType='private'
									fi
								fi
							fi
						fi
					fi
				else
					CurType='carrier'
				fi
			fi
			CurName="$(host $CurIP | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			if [ "$(printf '%s' "$CurName" | grep -e '(')" != "" ] ; then CurName='' ; fi
			CurName_Show="$CurName"
			CurNameResolution=''
			if [ "$CurName" != "" ] ; then
#				CurNameResolution="$(host "$CurName" | grep -e ' has address ' | tr ' ' '\n' | tail -n 1 | grep -e '..*\...*\...*\..')"
#				if [ "$CurNameResolution" != "$CurIP" ] ; then CurName_Show="${sWARN}${CurName_Show}!${fRESET}" ; fi
				if [ $CurIP_IsPublic -eq 1 ] ; then
					CurNameResolution="$(NetnameDestinations "$CurName")"
				else
					CurNameResolution="$(NetnameDestinations "$CurName" -)"
				fi
				CurIPRegex="$(printf '%s\n' "$CurIP" | sed -e 's|\.|\\.|g')"
				if [ "$(printf '%s' "$CurNameResolution" | grep -e "^${CurIPRegex}$")" = "" ] ; then CurName_Show="${sWARN}${CurName_Show}!${fRESET}" ; fi
			fi
			CurKey="$CurNIC.$(echo $(printf '%s\n' "$CurName" | tr '.' '\n' | tac) | tr ' ' '.' | cut -f 2- -d '.').$CurAddress"
			if [ "$CurNIC" = "lo" ] ; then CurKey="00AA.${CurKey}" ; fi
			ListadoNuevoRegistro "$ServiceName.ReportAddresses.$$" "$CurKey" "$CurNIC" "$CurIP_Show" "/$CurMask" "$CurType" "$GeoCode" "$CurName_Show"
			printf '.' 1>&2
		done
	done
	ListadoMostrar "$ServiceName.ReportAddresses.$$" ' ' a p
	printf '\n'
	CurIP="$(PublicIP)"
	CurIP_Show="$CurIP"
	GeoCode='?'
	if [ "$GeoCommand" != "" ] && [ "$CurIP" != "" ] ; then
		GeoCode="$(geoiplookup "$CurIP" | cut -f 2 -d ':' | cut -f 1 -d ',' | head -n 1)"
		GeoCode="$(echo TrimAndSingle $GeoCode | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if [ "$GeoCode" = "" ] ; then GeoCode='?' ; fi
	fi
	CurName=''
	if [ "$CurIP" != "" ] ; then
		CurName="$(host $CurIP | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
	fi
	if [ "$(printf '%s' "$CurName" | grep -e '(')" != "" ] ; then CurName='' ; fi
	CurName_Show="$CurName"
	CurNameResolution=''
	if [ "$CurName" != "" ] ; then
#		CurNameResolution="$(host "$CurName" | grep -e ' has address ' | tr ' ' '\n' | tail -n 1 | grep -e '..*\...*\...*\..')"
#		if [ "$CurNameResolution" != "$CurIP" ] ; then CurName_Show="${sWARN}${CurName_Show}!${fRESET}" ; fi
		CurNameResolution="$(NetnameDestinations "$CurName")"
		CurIPRegex="$(printf '%s\n' "$CurIP" | sed -e 's|\.|\\.|g')"
		if [ "$(printf '%s' "$CurNameResolution" | grep -e "^${CurIPRegex}$")" = "" ] ; then CurName_Show="${sWARN}${CurName_Show}!${fRESET}" ; fi
	fi
	printf '%s\n' "Default public IP: $CurIP_Show ${ParO}${GeoCode}${ParC} $CurName_Show"
}

#[/durruter]


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

ListChildReport ()
# Used to get a better line with child brief for a list.
{
	local ChildId="$1"
	local ChildStatus=''
	local CurrentPidFile=""
	local CurrentStatus=0
	
	if [ "$(EnabledChilds_IDs | grep -e "^${ChildId}$")" != "" ] ; then ChildStatus="${ChildStatus}E" ; fi
	PidfileStatus "${ChildsPidsDir}/${ChildId}.pid" # 0:Program is running 1:Program is not running and the pid file exists 3:Program is not running 4:Unable to determine program status
	CurrentStatus=$?
	if [ $CurrentStatus -eq 0 ] ; then ChildStatus="${ChildStatus}R" ; fi
	printf '%s\n' "$ChildId [${ChildStatus}]"
}

ChildDetails ()
{
	local ChildId="$1"
	local CurrentChild=""
	local ChildProfile_Available=''
	local ChildProfile_Enabled=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$ChildId" != "" ] ; then
		if [ "$ChildProfileExtension" != "" ] ; then
			TheExtension=".${ChildProfileExtension}"
		else
			TheExtension=""
		fi
		for CurrentChild in "$@" ; do
			ChildProfile_Available="${Childs_path_available}/${CurrentChild}${TheExtension}"
			ChildProfile_Enabled="${Childs_path_enabled}/${CurrentChild}${TheExtension}"
			if [ -e "$ChildProfile_Enabled" ] ; then
				printf '%s\n' "$ChildSingularName \"${CurrentChild}\" ${ParO}enabled${ParC} has configuration profile at: $ChildProfile_Available"
			else
				if [ -e "$ChildProfile_Available" ] ; then
					printf '%s\n' "$ChildSingularName \"${CurrentChild}\" ${ParO}disabled${ParC} has configuration profile at: $ChildProfile_Available"
				else
					printf '%s\n'  "${sERROR}E: ${ParO}$(id -un)${ParC} no $ChildSingularName with name \"${CurrentChild}\" found.${fRESET}" 1>&2
					LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			if [ $LastStatus -eq 0 ] ; then
				Status "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				Details_More "$CurrentChild"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			printf '%s\n' ""
		done
	else
		printf '%s\n' "${sERROR}E: $ChildSingularName must be specified.${fRESET}" 1>&2
		LastStatus=82 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Install_precp_Pre ()
# Example: Install_precp_Pre /cdrom/service
# Will be run before stopping/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/service
# Will be run after stopping/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, service registered, and just before re-starting service if it was a live-upgrade.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

#[durruter]
	local CacheScripts=''
	local CurScript=''
	local ITcmd_old=''
	local ITcmd_new=''
	local ITScmd_old=''
	local ITScmd_new=''
	local ITRcmd_old=''
	local ITRcmd_new=''
	if [ -f /tmp/durruter.uninstall.autostart ] ; then
		# To restore autostart when upgrading
		Service_autostart
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -eq 0 ] ; then
			rm /tmp/durruter.uninstall.autostart
		fi
	else
		printf '%s\n' "To load $ServiceName with network run: $ServiceName service autostart"
	fi
	if [ "$($ITcmd -m comment 2>&1 | grep -e '--comment')" = "" ] ; then
		# Comments unsupported as of iptables v1.2
		printf '%s\n' "${sWARN}W: With this $($ITcmd --version) entries IDs can't be managed. IptablesMetadata variable at $SystemConfigFile inhibites the feature.${fRESET}" 1>&2
	fi
	$ITcmd -L >/dev/null 2>&1	# Some iptables implementations load iptables on first call of some functions
	if [ "$(lsmod 2>/dev/null | grep -e 'iptable' -e 'ip.table')" = "" ] ; then
		printf '%s\n' "${sWARN}W: Kernel module for iptables doesn't seem to be loaded. iptables modules need to be loaded so $ServiceName don't fail.${fRESET}" 1>&2
	fi
	IpRoute="$(WhereProgram ip)"
	if [ ! -x "$IpRoute" ] ; then IpRoute="/sbin/ip" ; fi
	if [ ! -x "$IpRoute" ] ; then IpRoute="/bin/ip" ; fi
	if [ ! -x "$IpRoute" ] ; then IpRoute="/usr/sbin/ip" ; fi
	if [ ! -x "$IpRoute" ] ; then IpRoute="/usr/bin/ip" ; fi
	Test="$("$IpRoute" address show 2>/dev/null)"
	LastStatus=$?
	if [ "$Test" = "" ] && [ $LastStatus -ne 0 ] ; then
		printf '%s\n' "${sWARN}W: iproute doesn't seem to work, and it's necessary to detect devices and addresses. Check the command: ip address show${fRESET}" 1>&2
	fi
	if [ $StatusCode -eq 0 ] ; then
		CacheScripts="$(find "$SystemConfigDir" 2>/dev/null | grep -e '_fast\.sh$')"
		if [ "$CacheScripts" != "" ] ; then
			if [ "$(command -v iptables-legacy 2>/dev/null)" != "" ] ; then
				printf '%s\n' "Fixing cache scripts to $ITcmd"
			fi
			if [ "$(command -v iptables-legacy 2>/dev/null)" != "" ] ; then
				ITcmd_old='iptables'
				ITcmd_new='iptables-legacy'
			else
				ITcmd_old='iptables-legacy'
				ITcmd_new='iptables'
			fi
			if [ "$(command -v iptables-legacy-save 2>/dev/null)" != "" ] ; then
				ITScmd_old='iptables-save'
				ITScmd_new='iptables-legacy-save'
			else
				ITScmd_old='iptables-legacy-save'
				ITScmd_new='iptables-save'
			fi
			if [ "$(command -v iptables-legacy-restore 2>/dev/null)" != "" ] ; then
				ITRcmd_old='iptables-restore'
				ITRcmd_new='iptables-legacy-restore'
			else
				ITRcmd_old='iptables-legacy-restore'
				ITRcmd_new='iptables-restore'
			fi
			IFS="$(printf "\n\b")" ; for CurScript in $CacheScripts ; do unset IFS
				sed -i "s|${ITcmd_old} |${ITcmd_new} |g" "$CurScript"
				sed -i "s|${ITScmd_old}|${ITScmd_new}|g" "$CurScript"
				sed -i "s|${ITRcmd_old}|${ITRcmd_new}|g" "$CurScript"
			done
		fi
	fi
#[/durruter]
	return $StatusCode
}

Uninstall_predel_More ()
# Will be run after stopping service and before program files removal.
{
# [durruter]
	local LastStatus=0
	local StatusCode=0

	Service_manual /tmp/durruter.uninstall.autostart
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
# [/durruter]
}

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

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

#[durruter]
	printf '%s\n' "I: Deshabilitando entradas DNAT"
	dnat disable all all all all 2>/dev/null
#	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	printf '%s\n' "I: Deshabilitando salidas NAT"
	nat disable all 2>/dev/null
#	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ -f "$BackupDir/iptables.backup" ] ; then
		printf '%s\n' "I: Restaurando contenido iptables original"
		$ITRcmd "$BackupDir/iptables.backup"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
#[/durruter]
	return $StatusCode
}

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

	return $StatusCode
}

ProcessStatus_More ()
# Called in main service report; and for each child if DaemonizeChilds==1 && StatusProcecessShow==1
{
	local AskedPidStatus=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Status_More ()
# Called at end of report for whole service; and after each child report when reporting childs.
{
	local AskedPidStatus=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Details_More ()
# Called at end of each ChildDetails() report. Not to inform about processes.
{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Stop_Pre ()

# TEMPLATE WARNING: This function is now called also for each Child stop

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Stop_Post ()

# TEMPLATE WARNING: This function is now called also for each Child stop

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Start_Pre ()

# TEMPLATE WARNING: This function is now called also for each Child start

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Start_Post ()
# Start_Pre() and Start_Post() will be the only calls on main start if service is for one-shot (MainControllerStays==0)

# TEMPLATE WARNING: This function is now called also for each Child start

{
	local ChildId="$1"
	local LastStatus=0
	local StatusCode=0

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

Configuration_Early_More ()
# To minimize load on fast fork. Convenient to not write to disk.
{
#[durruter]
	ITcmd='iptables'
	if [ "$(command -v iptables-legacy 2>/dev/null)" != "" ] ; then
		ITcmd='iptables-legacy'
	fi
	ITScmd='iptables-save'
	if [ "$(command -v iptables-legacy-save 2>/dev/null)" != "" ] ; then
		ITScmd='iptables-legacy-save'
	fi
	ITRcmd='iptables-restore'
	if [ "$(command -v iptables-legacy-restore 2>/dev/null)" != "" ] ; then
		ITRcmd='iptables-legacy-restore'
	fi
#[/durruter]
}

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
#[durruter]
	BackupDir="$SystemConfigDir"
	if [ -f /etc/durruter/durruter.conf ] && [ $ReadOnly -eq 0 ] ; then
		# Migrate old config file
		mkdir -p "$(Dirname "$SystemConfigFile")"
		chmod u=rwX,go=rX "$(Dirname "$SystemConfigFile")"
		cat /etc/durruter/durruter.conf >> "$SystemConfigFile"
		mv /etc/durruter/durruter.conf /etc/durruter/durruter.conf.bak
	fi
	if [ $ReadOnly -eq 0 ] ; then
		if [ ! -d "$BackupDir" ] ; then
			mkdir -p "$BackupDir"
			chmod u=rwX,go=rX "$BackupDir"
		fi
	fi
#[/durruter]
}

Configuration_Saved_Post ()
# More configurations at end of Configuration()
# Use Configuration_Early_More() for any configuration needed in forking operation.
{
	local ReadOnly=0
	local LastStatus=0
	local StatusCode=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; fi
	
	Configuration_Early_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#[durruter]
	TheUserLanguage="$(UserLanguage)"
	Default_IptablesMetadata=1
	if [ "$($ITcmd -m comment 2>&1 | grep -e '--comment')" = "" ] ; then
		# Comments unsupported as of iptables v1.2
		Default_IptablesMetadata=0
	fi
	if [ $ReadOnly -eq 0 ] ; then
		LoadVarsValues "$SystemConfigFile" 'ShowWarnings ShowInfo FastLists IptablesMetadata' 2 "IptablesMetadata=$Default_IptablesMetadata"
		LastStatus=$?
		if [ $LastStatus -eq 104 ] ; then
			ShowWarnings="$(GetOrSetIniVarValue "$SystemConfigFile" ShowWarnings '' 1 = '' "# ShowWarnings: About not re-creating entries and similars.")"
			ShowInfo="$(GetOrSetIniVarValue "$SystemConfigFile" ShowInfo '' 1 = '' "# ShowInfo: To show actions information ${ParO}1${ParC} or be quiet ${ParO}0${ParC}")"
			FastLists="$(GetOrSetIniVarValue "$SystemConfigFile" FastLists '' 0 = '' "# FastLists: To list entries in raw format ${ParO}1${ParC} or sorted & well-aligned ${ParO}0${ParC}")"
			IptablesMetadata="$(GetOrSetIniVarValue "$SystemConfigFile" IptablesMetadata '' $Default_IptablesMetadata = '' "# IptablesMetadata: Allow ${ParO}1${ParC} or not ${ParO}0${ParC} the use of --comment parameter with iptables v1.4+ ; important to manage entries IDs")"
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ ! -f "$BackupDir/iptables.backup" ] ; then
			# Para recuperar una situacion anterior (fiable, limpia, o no)
			$ITScmd > "$BackupDir/iptables.backup"
		fi
	else
#		ShowWarnings="$(IniVarValue "$SystemConfigFile" ShowWarnings '' 1 = '')"
#		ShowInfo="$(IniVarValue "$SystemConfigFile" ShowInfo '' 1 = '')"
#		FastLists="$(IniVarValue "$SystemConfigFile" FastLists '' 0 = '')"
#		IptablesMetadata="$(IniVarValue "$SystemConfigFile" IptablesMetadata '' $Default_IptablesMetadata = '')"
		LoadVarsValues "$SystemConfigFile" 'ShowWarnings ShowInfo FastLists IptablesMetadata' 0 "IptablesMetadata=$Default_IptablesMetadata"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Default_IptablesMetadata" = "1" ] && [ "$IptablesMetadata" != "1" ] ; then
		printf '%s\n' "${sWARN}W: With this $($ITcmd --version) you should set IptablesMetadata=1 at $SystemConfigFile to allow entries ID's management!${fRESET}" 1>&2
	fi
	if [ "$TERM" != "" ] ; then
		perl -e 'exit(-p STDOUT ? 1 : 0);'
		RedirectedStdout=$?
	fi
#	cRESET="$(tput sgr0 2>&1)"
#	if [ $RedirectedStdout -eq 0 ] && [ "$(printf '%s' "$cRESET" | grep -ie tput:)" != "" ] ; then
#		# Detect message: tput: No value for $TERM and no -T specified
#		RedirectedStdout=254
#	fi
	if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
		cRESET="$(tput sgr0)"
		cWHITECYAN="$(tput setaf 7)$(tput setab 6)$(tput bold)"
		cBLACKWHITE="$(tput setaf 0)$(tput setab 7)$(tput bold)"
		cYELLOW="$(tput setaf 3)$(tput bold)"
		cWHITEBLACK="$(tput setaf 7)$(tput setab 0)$(tput bold)"
		cCYANBLACK="$(tput setaf 6)$(tput setab 0)$(tput bold)"
		cPROMPT="${cYELLOW}$(tput bel)"
		cHEADING0="$(tput smso)"
		cHEADING1="${cCYANBLACK}"
	else
		cRESET=''
		cWHITECYAN=''
		cBLACKWHITE=''
		cYELLOW=''
		cWHITEBLACK=''
		cCYANBLACK=''
		cPROMPT=''
		cHEADING0=''
		cHEADING1=''
	fi
#[/durruter]
	return $StatusCode
}

CreateChild ()
{
	sleep 0
}

MainController ()
# If service is for one-shot (MainControllerStays==0), is better to put Main-kernel code at Start_Post() instead; childs are loaded here anyway.
{
	local MaxChildLoopsNr="$1"  # -1 for unlimited (default)
	local DebugLog='/dev/null'
	local LastStatus=0
	local StatusCode=0
	
	if [ $LogLevel -ge 4 ] ; then DebugLog="$MainControllerLog" ; fi
	Start_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$ChildsPluralName" != "" ] ; then
		LoadChilds 1 "$MaxChildLoopsNr"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $Childs_MoreLoadsInterval -gt 0 ] && [ "$MainControllerStays" != "0" ] ; then
			printf '%s\n' "Begin to control enabled $ChildsPluralName in intervals of $Childs_MoreLoadsInterval seconds." | tee -a "$DebugLog"
			while [ ! -f "$MainControllerStopFile" ] ; do
				# This loop will be the main task
				WaitStoppable $Childs_MoreLoadsInterval "$MainControllerStopFile" 1
				LoadChilds 0 "$MaxChildLoopsNr"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
	fi
	if [ "$ChildsPluralName" = "" ] && [ "$MainControllerStays" != "0" ] ; then
		# Traditional main loop if not previous one.
		if [ -f "$MainControllerStopFile" ] ; then LogProgram 2 "W: File $MainControllerStopFile avoids main bucle." ; fi
		while [ ! -f "$MainControllerStopFile" ] ; do
			printf '%s\n' "main task not yet developed." | tee -a "$DebugLog" 1>&2
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			WaitStoppable 10 "$MainControllerStopFile" 0.5
		done
	fi
	Start_Post "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}


##### 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
if [ -r /lib/lsb/init-functions ] ; then
	. /lib/lsb/init-functions
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)"
cd / # avoid blocking mount points from being unmounted
if [ "$LogLevel" = "" ] ; then LogLevel=3 ; fi	# A nested call can export LogLevel
LogProgram 4 '$' "$0" "$@"
Action="$1"
if [ $# -gt 0 ] ; then shift ; fi

# Uppercase actions allow passthrough init manager parsers (eg. systemctl, service)
Action="$(Lowercase "$Action")"
LowerChildSingularName="$(Lowercase "$ChildSingularName")"
LowerChildsPluralName="$(Lowercase "$ChildsPluralName")"
FastConfiguration=0
case "$Action" in
	"remove" ) Action="uninstall" ; ActionMode="remove" ;;
	"purge" ) Action="uninstall" ; ActionMode="purge" ;;
	"force-reload" ) Action="reload" ;;
	"-h" ) Action="--help" ;;
	"-V" ) Action="--version" ;;
	"$LowerChildSingularName" ) if [ "$ChildSingularName" != "" ] ; then Action="child-foreground" ; fi ;;
	"${LowerChildSingularName}-foreground" ) if [ "$ChildSingularName" != "" ] ; then Action="child-foreground" ; fi ;;
	"$LowerChildsPluralName" ) if [ "$ChildsPluralName" != "" ] ; then Action="childs-foreground" ; fi ;;
	"${LowerChildsPluralName}-foreground" ) if [ "$ChildsPluralName" != "" ] ; then Action="childs-foreground" ; fi ;;
	"child" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; else Action="child-foreground" ; fi ;;
	"child-foreground" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"create" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"delete" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"list" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
	"details" ) if [ "$ChildSingularName" = "" ] ; then Action="'${Action}'" ; fi ;;
#[durruter]
	"service" ) Action="Service" ;;
	"main" ) Action="'${Action}'" ;;
	"status" ) Action="'${Action}'" ;;
	"listbans4fail" ) Action="bans" ;;
	"addr" ) Action="addresses" ;;
	"address" ) Action="addresses" ;;
#[/durruter]
esac
case "$Action" in
	"main" )  # For a foreground execution, use 'start-foreground' action
		if [ "$INIT_SCRIPT_StartForked" = "1" ] ; then LogProgram 4 "I: Forked process for start and load" ; fi
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			MainController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"childs-foreground" )
		# Everything foreground and sequential
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			DaemonizeChilds=0
			MainController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"child-foreground" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ChildController "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"create" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			CreateChild "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"delete" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			DeleteChild "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"list" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ListChilds "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"report" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ReportChilds "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"details" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			ChildDetails "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"enable" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
#[durruter]
#			EnableObject 1 "$@"
			Service_autostart "$@"
#[/durruter]
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"disable" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
#[durruter]
#			DisableObject 1 "$@"
			Service_manual "$@"
#[/durruter]
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"start" )
		if [ "$INIT_SCRIPT_StartForked" != "1" ] ; then
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$INIT_SCRIPT_InitCall" != "0" ] ; then rm -f "$MainControllerStopFile" ; fi
			if [ $(id -u) -eq 0 ] && [ "$INIT_SCRIPT_InitCall" = "0" ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
				printf '%s\n' "${sWARN}W: It's better for init system to use its program to start services:" 1>&2
				printf '%s\n' "   $(RecommendedInvocation start "$@")${fRESET}" 1>&2	#"
				if [ "$MainControllerStays" = "0" ] && [ "$DaemonizeOnStart" != "0" ] ; then
					printf '%s\n' "Launching main process foreground." 1>&2
					DaemonizeOnStart=0
				fi
			fi
			if [ $# -eq 1 ] ; then
				# Don't wait for system descending load when an explicit single child is launched
				Child_LoadWithCalm=-1
			fi
			if [ $StatusCode -eq 0 ] ; then
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			# Fork inmediately to not delay Init wait
			FastConfiguration=1
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$INIT_SCRIPT_InitCall" != "0" ] ; then rm -f "$MainControllerStopFile" ; fi
			LogProgram 4 "I: Early forking for start and load"
			export INIT_SCRIPT_InitCall=$INIT_SCRIPT_InitCall
			export INIT_SCRIPT_StartForked=1
			DaemonizeCommand "$MainControllerPidFile" "$MeCallFile" main "$@"
		fi
		;;
	"start-foreground" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$1" != "@" ] ; then
				DaemonizeOnStart=0
				Start "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
				if [ "$DaemonizeChilds" = "1" ] ; then
					CurrentStopFile="${ChildsPidsDir}/@.stop"
					while [ ! -f "$CurrentStopFile" ] ; do
						sleep 2
					done
				fi
			fi
		fi
		;;
	"stop" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
			printf '%s\n' "${sWARN}W: It's better for init system to use its program to stop services:" 1>&2
			printf '%s\n' "   $(RecommendedInvocation stop "$@")${fRESET}" 1>&2	#"
		fi
		Stop "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"restart" )
		Configuration "$@"
		if [ $(id -u) -eq 0 ] && [ $INIT_SCRIPT_InitCall -eq 0 ] && [ "$InitToolForStartStop" != "" ] && SystemProgramIsEnabled ; then
			printf '%s\n' "${sWARN}W: It's better for init system to use its program to restart services:" 1>&2
			printf '%s\n' "   $(RecommendedInvocation restart "$@")${fRESET}" 1>&2	#"
		fi
		if [ $StatusCode -eq 0 ] ; then
			RestartCall=1
			Stop "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			sleep 1
			Start "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"reload" )
		if [ "$1" != "@" ] ; then				# Workaround for Systemd bug https://github.com/systemd/systemd/pull/4992 (@ = no instance)
			Configuration "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				if [ "$ChildsPluralName" != "" ] ; then
					ReloadChilds 1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					"$MeExecutable" restart "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		;;
	"status" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Status "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"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
				ServiceHelp 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 service:" 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 service:" 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 $ServiceName $(ServiceVersion)"
			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 $ServiceName"
				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 ] ; then
#[durruter]
#					if ! SystemProgramIsEnabled ; then
#						printf '%s\n' ""
#						printf '%s\n' "${sWARN}W: $ServiceName will not start as system service until you run: $(RecommendedInvocation enable)${fRESET}" 1>&2
#					fi
#[/durruter]
					if [ "$PackageManager_Call" = "" ] ; then
						printf '%s\n' ""
						printf '%s\n' "For more options, run: ${ServiceName} --help"
					fi
				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 $ServiceName you must use the action \"uninstall\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$ChildSingularName" != "" ] ; then
				printf '%s\n' "${sWARN}W: To erase a $ChildSingularName you must use the action \"delete\"${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
				ServiceHelp 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
				ServiceHelp 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 $ServiceName $(ServiceVersion)"
			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" ] && [ -f "$MeExecutable" ] ; then
					cp "$MeExecutable" "/var/tmp/${ServiceName}.uninstall.tmp"
					cp "$MeExecutable" "${DirTempX}/${ServiceName}.uninstall.tmp"	# Compatibility with old package script: postrm
					chmod u=rx,g=r,o= "/var/tmp/${ServiceName}.uninstall.tmp"
					chmod u=rx,g=r,o= "${DirTempX}/${ServiceName}.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 ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		export LogLevel=4
		"$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"--version" )
		Configuration ro "$@"
		printf '%s\n' "$ServiceName $(ServiceVersion)"
		ServiceCopyright
		printf '%s\n' "$ServiceDocumentation"
		TheTemplateVersion="$(TemplateVersion)"
		if [ "$TheTemplateVersion" != "" ] ; then
			printf '%s\n' "Init script $(TemplateVersion)"
		fi
		TemplateCopyright
		;;
	"--help" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceHelp
		;;
#[durruter]
	"nat" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		nat "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"dnat" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		dnat "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"shaping" )
		printf '%s\n' "${sERROR}E: Feature not yet implemented in this version.${fRESET}" 1>&2
		LastStatus=63 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"Service" )
		# Word "service" in spanish to not conflict with "service" OS command.
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Service "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"unban4fail" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Unban4Fail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"bans" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ListBans4Fail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"monitor" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Monitor "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"via" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Via "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"addresses" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ReportAddresses "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/durruter]
	"" )
		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
		ServiceHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	* )
#[durruter]
		printf '%s\n' "${sERROR}E: Syntax error; Unknown action ${Action}${fRESET}" 1>&2
		printf '%s\n' "${sERROR}E: Syntax error; Unknown object ${Action}${fRESET}" 1>&2
#[/durruter]
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
esac
CleanTempFiles
if [ "$(cat "$MainControllerPidFile" 2>/dev/null | sed -e 's| ||g')" = "$$" ] && [ "$DaemonizeOnStart" != "1" ] ; then
	rm "$MainControllerPidFile"
fi
cd "$PreviousDir" 2>/dev/null
if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
exit $StatusCode
