#!/bin/bash
# INFO signals:
#
# 0 EXIT: when program finishes (already used here)
# 1 HUP: loss of terminal
# 2 INT: is ^C
# 3 QUIT: ^d quit from keyboard
# 4 ILL: illegal instruction
# 5 TRAP: breakpoints
# 6 ABRT: abort
# 7 BUS: hardware problem
# 8 FPE: wrong math operation
# 9 KILL: uncatcheable / unstopable
# 10 USR1: customizable
# 13 PIPE: broken pipe
# 14 ALMR: timers
# 15 TERM: termination
# 17 CHLD: children monitor
# 20 TSTP: ^z
# 24 XCPU: too much cpu consumption
# 28 WINCH: window resize

##TEXTDOMAIN="elive-tools" # disabled on purpose for compatibility, do not enable

if [[ -n "$ZSH_VERSION" ]] ; then
    # Note: if you use ZSH, you must set the SOURCE var name from the main script
    if [[ -n "$SOURCE" ]]; then
        SOURCE="$( readlink -f "$0" || true )"
    else
        SOURCE="$ZSH_SCRIPT"
    fi
fi

# The rest of the definitions are moved to the end of the file, functions must be declared before

#===  FUNCTION  ================================================================
#          NAME:  el_set_display_variables
#   DESCRIPTION:  Set DISPLAY, DBUS, and XAUTHORITY variables for the current user
#    PARAMETERS:  $1 = "debug" (optional) to enable verbose output
#       RETURNS:  0 on success or if already in console
#       EXAMPLE:  el_set_display_variables debug
#===============================================================================
el_set_display_variables(){
    local display e_ipc_socket e_ipc_file pid file is_debug buf is_enlightenment is_pong all_displays session d

    if [[ "$1" = "debug" ]] ; then
        is_debug=1
    fi
    # if we are in console (out of X) we should always exit
    if ((is_console)) ; then
        if ((is_debug)) ; then
            echo -e "we are NOT in console, exiting '${FUNCNAME[0]}' ..." 1>&2
        fi
        return 0
    fi

    # from enlightenment values {{{

    # unset XAUTHORITY if is not from our user (bugs when we "su otheruser")
    # update: we should never unset the XAUTHORITY, verify that this was really wrong before..
    # more exactly: if we run a script from user, that sudo another script that runs a zenity, we will unset the XAUTHORITY and we will never be able to show the zenity message
    #if [[ -n "$XAUTHORITY" ]] && [[ "$XAUTHORITY" != "$HOME/"* ]] ; then
        #unset XAUTHORITY
    #fi

    # 0) If DISPLAY is empty, try to inherit DISPLAY from parent process tree
    if [[ -z "${DISPLAY}" ]] ; then
        curr_pid="$$"
        while [[ -n "$curr_pid" && "$curr_pid" -gt 1 ]] ; do
            ppid="$(awk '/PPid:/ {print $2}' "/proc/$curr_pid/status" 2>/dev/null)"
            [[ -z "$ppid" || "$ppid" -le 1 || "$ppid" -eq "$curr_pid" ]] && break
            if [[ -r "/proc/$ppid/environ" ]] ; then
                p_disp="$(cat "/proc/$ppid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
                if [[ -n "$p_disp" ]] ; then
                    export DISPLAY="$p_disp"
                    break
                fi
            fi
            curr_pid="$ppid"
        done
    fi

    # Validate pre-existing E_IPC_SOCKET against current DISPLAY
    if [[ -n "$E_IPC_SOCKET" && -n "$DISPLAY" ]] ; then
        pid="$( basename "$E_IPC_SOCKET" )"
        pid="${pid%|*}"
        if [[ -n "$pid" && -d "/proc/$pid" ]] ; then
            p_disp="$(cat "/proc/$pid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
            disp_num1="${DISPLAY#*:}" ; disp_num1="${disp_num1%%.*}"
            disp_num2="${p_disp#*:}" ; disp_num2="${disp_num2%%.*}"
            if [[ -n "$disp_num1" && "$disp_num1" != "$disp_num2" ]] ; then
                unset E_IPC_SOCKET
            fi
        else
            unset E_IPC_SOCKET
        fi
    fi

    # Enlightenment mode socket search:
    if [[ -z "$E_IPC_SOCKET" ]] ; then
        while read -r candidate_file ; do
            [[ -z "$candidate_file" ]] && continue
            cand_socket="${candidate_file%\|*}"
            cand_pid="$( basename "$cand_socket" )"
            cand_pid="${cand_pid%|*}"
            if [[ -n "$cand_pid" && -d "/proc/$cand_pid" ]] ; then
                cand_disp="$(cat "/proc/$cand_pid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
                if [[ -n "$DISPLAY" ]] ; then
                    disp_num1="${DISPLAY#*:}" ; disp_num1="${disp_num1%%.*}"
                    disp_num2="${cand_disp#*:}" ; disp_num2="${disp_num2%%.*}"
                    if [[ -n "$disp_num1" && "$disp_num1" = "$disp_num2" ]] ; then
                        export E_IPC_SOCKET="$cand_socket"
                        break
                    fi
                else
                    if [[ -n "$cand_disp" ]] ; then
                        export E_IPC_SOCKET="$cand_socket"
                        export DISPLAY="$cand_disp"
                        break
                    fi
                fi
            fi
        done <<< "$( command find /run/user/${UID}/e-${USER}@*/*\|* /tmp/e-"${USER}"@*/*\|* -mindepth 0 -maxdepth 0 -type s -print0 2>/dev/null | xargs -r0 stat -c '%y %n' 2>/dev/null | sort -nr | sed -e 's|^.*/run|/run|g' -e 's|^.*/tmp|/tmp|g' -e 's|^[^/]*||' )"
    fi

    # E17 mode socket search:
    if [[ -z "$E_IPC_SOCKET" ]] && command ls --color=none /tmp/e-"${USER}"@*/*-* 1>/dev/null 2>/dev/null ; then
        while read -r candidate_file ; do
            [[ -z "$candidate_file" ]] && continue
            cand_socket="${candidate_file%-*}"
            cand_pid="$( basename "$cand_socket" )"
            cand_pid="${cand_pid%-*}"
            if [[ -n "$cand_pid" && -d "/proc/$cand_pid" ]] ; then
                cand_disp="$(cat "/proc/$cand_pid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
                if [[ -n "$DISPLAY" ]] ; then
                    disp_num1="${DISPLAY#*:}" ; disp_num1="${disp_num1%%.*}"
                    disp_num2="${cand_disp#*:}" ; disp_num2="${disp_num2%%.*}"
                    if [[ -n "$disp_num1" && "$disp_num1" = "$disp_num2" ]] ; then
                        export E_IPC_SOCKET="$cand_socket"
                        break
                    fi
                else
                    if [[ -n "$cand_disp" ]] ; then
                        export E_IPC_SOCKET="$cand_socket"
                        export DISPLAY="$cand_disp"
                        break
                    fi
                fi
            fi
        done <<< "$( command find /tmp/e-"${USER}"@*/*-* -mindepth 0 -maxdepth 0 -type s -print0 2>/dev/null | xargs -r0 stat -c '%y %n' 2>/dev/null | sort -nr | sed -e 's|^.*/tmp|/tmp|g' -e 's|^[^/]*||' )"
    fi

    # set display fallback if still empty
    if [[ -z "${DISPLAY}" ]] ; then
        # 1) Use Enlightenment IPC socket if available
        if [[ -n "$E_IPC_SOCKET" ]] ; then
            pid="$( basename "$E_IPC_SOCKET" )"
            pid="${pid%|*}"
            if [[ -n "$pid" && -d "/proc/$pid" ]] ; then
                display="$(cat "/proc/$pid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
                [[ -n "$display" ]] && export DISPLAY="$display"
            fi
        fi
    fi

    # 2) Find any user-owned X client (window manager, session manager)
    if [[ -z "${DISPLAY}" ]] ; then
        # Prefer these processes in order
        for client in e16 enlightenment enlightenment_start x-session-manager startplasma-x11 ; do
            pid="$(pgrep -u "$(id -u)" -x "$client" 2>/dev/null | head -1)"
            if [[ -n "$pid" && -d "/proc/$pid" ]] ; then
                display="$(cat "/proc/$pid/environ" 2>/dev/null | tr '\0' '\n' | grep -a '^DISPLAY=' | sed -e 's/^DISPLAY=//')"
                if [[ -n "$display" ]] ; then
                    export DISPLAY="$display"
                    break
                fi
            fi
        done
    fi

    # 3) Try loginctl (systemd) if available
    if [[ -z "${DISPLAY}" ]] && ((is_systemd)) && command -v loginctl >/dev/null 2>&1 ; then
        for session in $(loginctl list-sessions --no-legend 2>/dev/null | awk -v uid="$(id -u)" -v user="$USER" '($2 == uid || $3 == user) {print $1}') ; do
            display="$(loginctl show-session -p Display "$session" 2>/dev/null | cut -d= -f2)"
            if [[ -n "$display" ]] ; then
                export DISPLAY="$display"
                break
            fi
        done
    fi

    # 4) Parse 'who' output (systemd-openrc-wrapper method)
    if [[ -z "${DISPLAY}" ]] ; then
        display="$(timeout 5 who 2>/dev/null | awk -v user="$USER" '$1 == user { for(i=2;i<=NF;i++) if ($i ~ /^\(:[0-9]/ || $i ~ /^:[0-9]/) { gsub(/[\(\)]/, "", $i); print $i; break } }' | tail -1)"
        [[ -n "$display" ]] && export DISPLAY="$display"
    fi

    # 5) Parse 'w' output (traditional fallback)
    if [[ -z "${DISPLAY}" ]] ; then
        display="$(w -hs "$USER" 2>/dev/null | awk '{ if ($5 ~ /(\/e16|xinit|enlightenment_start)/ ) print $3}' | grep "^:" | tail -1)"
        [[ -n "$display" ]] && export DISPLAY="$display"
    fi

    # 6) Last resort: scan all user processes for DISPLAY and pick the highest number
    if [[ -z "${DISPLAY}" ]] ; then
        # Obtain all displays set in our processes, then sort unique and take the highest numeric display (e.g. :2 > :1 > :0)
        all_displays="$(pgrep -u "$(id -u)" 2>/dev/null | sed 's|^|/proc/|; s|$|/environ|' | xargs -r grep -z -a -h '^DISPLAY=:' 2>/dev/null | tr '\0' '\n' | sed -e 's/^DISPLAY=//' | sort -u -V | tail -1 )"
        if [[ -n "$all_displays" ]] ; then
            [[ -n "$all_displays" ]] && export DISPLAY="$all_displays"
        fi
    fi

    # Clean up: ensure DISPLAY is in the form ":N"
    if [[ -n "${DISPLAY}" ]] ; then
        # Remove leading colon and any trailing ".0" etc.
        display="${DISPLAY#:}"
        display="${display%.*}"
    fi

    # fetch dbus address to we can use things like notify-send or dbus-send
    #unset DBUS_SESSION_BUS_ADDRESS

    #case "$( el_user_desktop_active_get "desktop" )" in
        #"E16")
            #unset DBUS_SESSION_BUS_ADDRESS
            #;;
        #""|"Enlightenment")
            #;;
    #esac

    # only re-set the dbus variable if we have a running E in our actual environment
    if [[ -n "$E_HOME_DIR" ]] && pidof -q enlightenment ; then
        if [[ -x "$( which enlightenment_remote )" ]] ; then
            # cache call
            is_enlightenment=1
        fi
    fi

    # verify enlightenment already set / default dbus value
    if ! ((is_pong)) && [[ -n "$DBUS_SESSION_BUS_ADDRESS" ]] ; then
        if ((is_enlightenment)) ; then
            if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                is_pong=1
            else
                unset DBUS_SESSION_BUS_ADDRESS
            fi
        else
            is_pong=1
        fi
    fi

    # test standard address
    if ! ((is_pong)) && test -S "/run/user/$UID/bus" ; then
        buf="unix:path=/run/user/$UID/bus"
        [[ -n "$buf" ]] && export DBUS_SESSION_BUS_ADDRESS="$buf"
    fi
    # test or invalidate
    if ! ((is_pong)) && [[ -n "$DBUS_SESSION_BUS_ADDRESS" ]] ; then
        if ((is_enlightenment)) ; then
            if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                is_pong=1
            else
                unset DBUS_SESSION_BUS_ADDRESS
            fi
        else
            is_pong=1
        fi
    fi


    # set values for this user / session
    if ! ((is_pong)) && test -e ~/.dbus/session-bus/"$(dbus-uuidgen --get 2>/dev/null)-${display}" ; then
        source ~/.dbus/session-bus/"$(dbus-uuidgen --get)-${display}"
    fi
    # test or invalidate
    if ! ((is_pong)) && [[ -n "$DBUS_SESSION_BUS_ADDRESS" ]] ; then
        if ((is_enlightenment)) ; then
            if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                is_pong=1
            else
                unset DBUS_SESSION_BUS_ADDRESS
            fi
        else
            is_pong=1
        fi
    fi

    # Loop through all dbus socket files in /tmp and test them
    if ! ((is_pong)) && ((is_enlightenment)) && [[ -x "$( which enlightenment_remote )" ]] ; then
        for socket in /tmp/dbus-* ; do
            if test -S "$socket" ; then
                export DBUS_SESSION_BUS_ADDRESS="unix:path=$socket"
                if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                    break
                else
                    unset DBUS_SESSION_BUS_ADDRESS
                fi
            fi
        done
    fi
    # test or invalidate
    if ! ((is_pong)) && [[ -n "$DBUS_SESSION_BUS_ADDRESS" ]] ; then
        if ((is_enlightenment)) ; then
            if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                is_pong=1
            else
                unset DBUS_SESSION_BUS_ADDRESS
            fi
        else
            is_pong=1
        fi
    fi


    # elive cache'd usable one
    if ! ((is_pong)) && [[ -e "$HOME/.cache/dbus-session-address-last.txt" ]] ; then
        buf="$( cat "$HOME/.cache/dbus-session-address-last.txt" )"
        if ! test -e "$( echo "$buf" | sed -e 's|^.*path=||g' )" ; then
            unset buf
        fi
        [[ -n "$buf" ]] && export DBUS_SESSION_BUS_ADDRESS="$buf"
    fi
    # test or invalidate
    if ! ((is_pong)) && [[ -n "$DBUS_SESSION_BUS_ADDRESS" ]] ; then
        if ((is_enlightenment)) ; then
            if enlightenment_remote -version 2>/dev/null | LC_ALL=C grep -qs "^[[:digit:]].*\.[[:digit:]]" ; then
                is_pong=1
            else
                unset DBUS_SESSION_BUS_ADDRESS
            fi
        else
            is_pong=1
        fi
    fi


    # END from enlightenment values }}}
    # xauthority {{{
    if [[ -n "$DISPLAY" ]] && [[ -e "$HOME/.Xauthority" ]] ; then
        XAUTHORITY="$HOME/.Xauthority"
        export XAUTHORITY
    fi

    if ((is_terminal)) && [[ "$UID" != 0 ]] && [[ -z "$SSH_CONNECTION" ]] ; then
        if [[ -z "${DISPLAY}" ]] || [[ -z "$XAUTHORITY" ]] || [[ -z "$E_IPC_SOCKET" ]] ; then
            # only show the error if we have a graphical system running by the user
            if command ls --color=none /tmp/e-"${USER}"@*/*-* /tmp/e-"${USER}"@*/*\|*  1>/dev/null 2>/dev/null || pidof e16 1>/dev/null 2>&1 ; then
                if [[ -z "${DISPLAY}" ]] ; then
                    el_warning "Variable DISPLAY is not set, so we will not have access to the local graphical system"
                fi
                if [[ -z "${XAUTHORITY}" ]] ; then
                    el_warning "Variable XAUTHORITY is not set, so we may not have full access to the local graphical system"
                fi
                # only e17+
                if command ls --color=none /tmp/e-${USER}@*/*-* /tmp/e-"${USER}"@*/*\|* 1>/dev/null 2>/dev/null ; then
                    if [[ -z "${E_IPC_SOCKET}" ]] ; then
                        if [[ "$UID" != 0 ]] ; then
                            el_warning "Variable E_IPC_SOCKET is not set, we may be not the user owner of this graphical session"
                        fi
                    fi
                fi
            fi
        fi
    fi

    # END xauthority }}}

    if ((is_debug)) ; then
        echo -e "DISPLAY '$DISPLAY' DBUS_SESSION_BUS_ADDRESS '$DBUS_SESSION_BUS_ADDRESS' E_IPC_SOCKET '$E_IPC_SOCKET' XAUTHORITY '$XAUTHORITY' UID '$UID' USER '$USER' " 1>&2
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_pulseaudio
#   DESCRIPTION:  Manages the PulseAudio daemon for the current user
#    PARAMETERS:  $1 = mode (check|start|stop|restart)
#       RETURNS:  0 if running (check) or success, 1 if not running or failed
#       EXAMPLE:  el_pulseaudio restart
#===============================================================================
el_pulseaudio(){
    local mode is_pulseaudio

    mode="$1"
    shift

    if [[ ! -e "/var/lib/dpkg/info/pulseaudio.list" ]] ; then
        el_debug "pulseaudio is not installed"
    fi

    case "$mode" in
        check)
            # installed ?
            if [[ ! -e "/var/lib/dpkg/info/pulseaudio.list" ]] ; then
                return 1
            fi

            # running ?
            if pidof -q pulseaudio ; then

                if pulseaudio --check 2>/dev/null 1>&2 \
                    || LC_ALL=C  pactl info 2>/dev/null | grep -qs "Server Name: pulseaudio" ; then
                    return 0
                else
                    return 1
                fi
            else
                return 1
            fi
            ;;

        start)
            if [[ -e "/var/lib/dpkg/info/pulseaudio.list" ]] ; then
                if ((is_systemd)) ; then
                    systemctl --user start pulseaudio.service pulseaudio.socket
                else
                    ( pulseaudio -D  2>/dev/null 1>&2  & )
                fi
            else
                return 1
            fi
            ;;

        stop)
            if pidof -q pulseaudio ; then
                if ((is_systemd))  ; then
                    systemctl --user stop pulseaudio.service pulseaudio.socket 2>/dev/null || true
                else
                    pulseaudio --kill 2>/dev/null || true
                fi
                killall -9 pulseaudio 2>/dev/null || true
            fi
            ;;

        restart)
            $FUNCNAME stop
            $FUNCNAME start
            sleep 2
            ;;
    esac

}

#===  FUNCTION  ================================================================
#          NAME:  el_notify
#   DESCRIPTION:  Sends a desktop notification via notify-send or el_info fallback
#    PARAMETERS:  $1 = mode (soft|normal|important|critical|wait)
#                 $2 = icon name, $3 = title, $4 = message, $@ = extra args
#       RETURNS:  0 on success, 1 on missing parameters
#       EXAMPLE:  el_notify normal "logo-elive" "Update" "System updated successfully"
#===============================================================================
el_notify(){
    # pre {{{
    local time title message icon words mode extra_args

    if [[ -z "$4" ]] ; then
        el_error "Function usage: ${FUNCNAME[0]} (soft|nor|imp|crit) 'icon' 'title' 'message'  - (icon example: 'logo-elive')"
        return 1
    fi

    mode="$1"
    shift
    icon="$1"
    shift
    title="$1"
    shift
    message="$1"
    shift

    # }}}
    # defaults {{{
    #if [[ -z "$icon" ]] ; then
        #icon="logo-elive"
    #fi

    # count amount of seconds
    words="$( echo "$title $message" | wc -w )"
    time="$( echo "$words / 2.8" | LC_NUMERIC=C bc -l | sed -e 's|\..*$||g' )"
    time="${time%.*}"
    time="$(( $time + 3 ))"

    # add extra time if we require an action
    if echo "$@" | grep -qsE -- "(-w|--action|-A)" ; then
        time="$(( $time + 10 ))"
    fi


    case "$mode" in
        soft)
            time="$(( $time - 2 ))"
            extra_args="$extra_args -u low"
            ;;
        nor*)
            time="$(( $time + 4 ))"
            extra_args="$extra_args -u normal"
            ;;
        imp*)
            # 1 minute more
            time="$(( $time + 60 ))"
            extra_args="$extra_args -u critical"
            ;;
        crit*)
            # 15 minutes more
            time="$(( $time + 900 ))"
            extra_args="$extra_args -u critical"
            ;;
        wait)
            # wait 30 minutes more for user input
            time="$(( $time + 1800 ))"
            extra_args="$extra_args -u normal"
            ;;
    esac

    [[ "$time" -lt 2 ]] && time=2

    # convert time to milliseconds
    time="$(( $time * 1000 ))"

    # always make it transient mode, because is so annoying to have notifications kept in a widget
    extra_args="${extra_args} -e"

    # }}}

    el_set_display_variables

    if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
        if ! el_dependencies_check notify-send ; then
            el_dependencies_install "libnotify-bin"
        fi

        # run notification daemon if not running already
        if [[ -n "$EROOT" ]] ; then
            if ! pidof "notification-daemon" 1>/dev/null 2>&1 ; then
                notification-daemon-restarter
                LC_ALL=C sleep 0.3
            fi

            # check that it working otherwise restart daemon, this is needed otherwise "action" based notifications will block the application being stuck
            if ! timeout 5 notify-send -e -t 1 "." 2>/dev/null ; then
                notification-daemon-restarter
            fi
        fi

        notify-send ${extra_args} -t "$time" -i "$icon" "$title" "$message" "$@"
    else
        el_info "${title}: ${message}"
    fi
}


#===  FUNCTION  ================================================================
#          NAME:  el_verify_internet_ping
#   DESCRIPTION:  Verifies internet connectivity by pinging public DNS (8.8.8.8)
#    PARAMETERS:  $1 = timeout in seconds (default 7), $2 = mode (unused)
#       RETURNS:  0 if online, 1 if offline
#       EXAMPLE:  if el_verify_internet_ping 5 ; then echo "Online" ; fi
#===============================================================================
el_verify_internet_ping(){
    # pre {{{
    local timeout count target target2 mode
    timeout="$1"
    mode="$2"
    # note: this code contains commented lines used for debug (to simulate a full lag)

    # note: dns resolving can be very slow if we are for example using torrents
    #target="www.google.com"
    target="8.8.8.8"
    target2="1.1.1.1"

    if [[ -z "$timeout" ]] ; then
        # default timeout if not set
        timeout=7
    fi

    # - pre }}}

    # do we are using servcies that slows down our dns resolving? just make sure and test it..
    #if ps aux | grep -v grep | grep -qsiE "(transmission|torrent|deluge|amule|nicotine)" ; then
        ## special ping modes in case we have a lag (because we don't want to lose time resolving dns)
        #target="8.8.8.8"
        ##if timeout 3 sleep 100000 2>/dev/null 1>&2 ; then
        #if timeout 4 ping -c 1 "$target"  2>/dev/null 1>&2 ; then
            #return 0
        #fi
    #else
        ## this is the normal ping mode
        ##if timeout $timeout sleep 100000 2>/dev/null 1>&2 ; then
        #if timeout $timeout ping -c 1 "$target"  2>/dev/null 1>&2 ; then
            #return 0
        #fi
    #fi

    # normal ping mode, we don't need to use the previous code so we use it by default
    # we only need to know if we can ping to outside, not dns resolving
    if timeout $timeout ping -c 1 "$target"  2>/dev/null 1>&2 ; then
        return 0
    else
        if timeout $timeout ping -c 1 "$target2"  2>/dev/null 1>&2 ; then
            return 0
        fi
    fi


    # wanna debug more?
    if [[ "${EL_DEBUG}" -gt "3" ]] ; then
        el_debug "doing a ping to google don't respond: $( timeout 2 ping -c 1 "$target" 2>&1 )"
        if timeout 2 ping -c 1 "$target" 2>/dev/null 1>&2 ; then
            el_debug "by other side we can ping directly to an ip, check your /etc/resolv.conf"
        else
            el_debug "we cannot even ping to the google dns ($target)"
        fi
    fi

    # in any case if we reach here, is because we cannot ping to google, so return that
    return 1
}
#===  FUNCTION  ================================================================
#          NAME:  el_verify_internet
#   DESCRIPTION:  Checks internet status using carrier, cache, ping, and curl
#    PARAMETERS:  $1 = mode (fast|normal) - affects timeouts and retry loops
#       RETURNS:  0 if online, 1 if offline
#       EXAMPLE:  el_verify_internet fast
#===============================================================================
el_verify_internet(){
    # pre {{{
    local stamp_hour_prev date_hour_now date_hour_prev returned OnLine i mode timeout buf target target2 interface

    stamp_hour_prev="/tmp/.stamps-${USER}/verify_internet_connection"
    mkdir -p "$( dirname "${stamp_hour_prev}" )"

    date_hour_now="$( date +%s )"
    #date_hour_prev="$( tail -1 "${stamp_hour_prev}" 2>/dev/null )"
    #read -r date_hour_prev <<< "$date_hour_prev"

    # note: we should not use delayed, we always want delayed, instead, we should use "fast" when we need a fast check
    mode="$1"

    if [[ "${mode}" = "fast" ]] ; then
        # bad network in house dont show anything before 4 seconds
        timeout=8
    else
        timeout=16
    fi

    target="8.8.8.8"
    target2="1.1.1.1"

    # }}}

    # Test for network carrier first, and exit if we don't have it at all
    # we should have "something" connected somewhere
    unset OnLine
    #for interface in $(ls /sys/class/net/ | grep -Fv lo);
    for interface in /sys/class/net/*
    do
        interface="${interface##*/}"
        if [[ "${interface}" = "lo" ]] ; then
            continue
        fi

        if [[ $(cat "/sys/class/net/$interface/carrier" 2>/dev/null ) = 1 ]]; then
            if [[ "${EL_DEBUG}" -gt "3" ]] ; then
                el_debug "carrier for $interface marks that we are connected"
            fi

            OnLine=1;
            break
        fi
    done


    # cache online (10 min)
    if [[ -n "$OnLine" ]] ; then
        if [[ -s "$stamp_hour_prev" ]] ; then
            buf="$( echo "$(date +%s) - $( stat -c %Y "${stamp_hour_prev}" )" 2>/dev/null | LC_ALL="$EL_LC_EN" bc -l | sed -e 's|\..*$||g' )"
            if [[ -n "$buf" ]] && [[ "$buf" -lt 600 ]] ; then

                # if anything tell us that we are connected and we was before, we should still be
                el_dependencies_check "route"
                if /sbin/route -n | LC_ALL=C grep -qsE "0\.0\.0\.0.*\s+UG\s+" || ip route | LC_ALL=C grep -Fqs "default via" ; then
                    if [[ "${EL_DEBUG}" -gt "3" ]] ; then
                        el_debug "seems like we still online, using cache + small checks"
                    fi
                    return 0
                fi
            fi
        fi
    fi


    # we have not finished from the caches, so remove them so we will avoid the next check loops
    rm -f "$stamp_hour_prev"

    # do we have booted the computer recently? (and so no internet yet), needed for things like geolocation in live
    if ! LC_ALL=C grep -Fqs "boot=live" /proc/cmdline ; then
        if [[ "$( cat "/proc/uptime" | awk '{print $1}' | sed -e 's|\..*$||g' )" -lt 300 ]] ; then

            # we just booted the system, wait for real 40 secons
            for i in $( seq 30 )
            do
                if timeout 2 ping -c 1 "$target"  2>/dev/null 1>&2 ; then
                    return 0
                else
                    if timeout 2 ping -c 1 "$target2"  2>/dev/null 1>&2 ; then
                        return 0
                    fi
                fi
                sleep 1
            done
        fi
    fi


    # do a ping check
    if el_verify_internet_ping $timeout $mode ; then
        echo "$date_hour_now" > "$stamp_hour_prev"
        if [[ "${EL_DEBUG}" -gt "3" ]] ; then
            el_debug "connection to internet found at ping level"
        fi
        return 0
    fi



    # still in fast check ? return 0
    if [[ "${mode}" = "fast" ]] ; then
        return 1
    fi


    # check for connection to internet using curl instead
    if [[ "${EL_DEBUG}" -gt "3" ]] ; then
        el_debug "let's do a last check using a curl to google"
    fi

    el_dependencies_check "curl"
    if timeout 9 curl --connect-timeout 8 www.google.com 2>/dev/null | LC_ALL=C grep -qsi 'html' ; then
        echo "$date_hour_now" > "$stamp_hour_prev"
        if [[ "${EL_DEBUG}" -gt "3" ]] ; then
            el_debug "connection to internet finally using curl"
        fi

        return 0
    else
        if [[ "${EL_DEBUG}" -gt "3" ]] ; then
            el_debug "Connection to internet not found"
        fi

        return 1
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_verify_internet_metered
#   DESCRIPTION:  Checks if any active network connection is metered
#    PARAMETERS:  -
#       RETURNS:  0 if metered, 1 otherwise (or no active connection)
#       EXAMPLE:  if el_verify_internet_metered ; then echo "Metered" ; fi
#===============================================================================
el_verify_internet_metered(){
    local active_connections conn_name result metered_val

    # Ensure we have nmcli
    if ! command -v nmcli &>/dev/null; then
        el_debug "nmcli is not installed, cannot check for metered connection"
        return 1
    fi

    # Retrieve list of active connection names (terse format, only the NAME field)
    active_connections="$(nmcli -t -f NAME con show --active 2>/dev/null)"

    if [[ -z "$active_connections" ]]; then
        el_debug "No active connection."
        return 1
    fi

    # Iterate over each active connection
    while IFS= read -r conn_name; do
        [[ -z "$conn_name" ]] && continue
        # Get the metered property for this connection
        result="$(nmcli -t -f connection.metered con show "$conn_name" 2>/dev/null)"
        # Expected output: "connection.metered:yes" (or no / guessed)
        metered_val="${result#connection.metered:}"
        if [[ "$metered_val" = "yes" ]] || [[ "$metered_val" = "guessed" ]]; then
            el_debug "Metered connection: $conn_name"
            return 0
        fi
    done <<< "$active_connections"

    # No metered connection found
    return 1
}

#===  FUNCTION  ================================================================
#          NAME:  el_make_environment
#   DESCRIPTION:  Initializes user environment (HOME, USER, PATH, PS4, XDG dirs)
#    PARAMETERS:  $1 = "debug" (optional)
#       RETURNS:  -
#       EXAMPLE:  el_make_environment
#===============================================================================
el_make_environment(){
    local is_debug
    if [[ "$1" = "debug" ]] ; then
        is_debug=1
    fi

    if [[ -z "$DSHELL" && -z "$DHOME" && -z "$EXTRA_GROUPS" ]] && [[ -s "/etc/adduser.conf" ]] ; then
        source /etc/adduser.conf
    fi

    # set user
    if [[ -z "${USER}" ]] ; then
        USER="$(whoami)"
        export USER
    fi

    # set home
    if [[ -z "${HOME}" ]] ; then
        if [[ -n "${DHOME}" ]] ; then
            HOME="${DHOME}/$USER"
            export HOME
        else
            HOME="/home/$USER"
            export HOME
        fi
    fi

    # give display access
    el_set_display_variables $1

    #if [[ "$PATH" != *"$HOME/bin"* ]] ; then
        #PATH="$HOME/bin:$PATH"
    #fi

    if [[ -n "${SOURCE}" ]] ; then
        #export PS4='+ ($(basename ${0}):${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}():  }'
        #export PS4='+ [$(basename ${0})|${FUNCNAME[0]:+${FUNCNAME[0]}}|${LINENO}]  '
        export PS4='+ [$(basename ${SOURCE} 2>/dev/null )  ${FUNCNAME[0]:+${FUNCNAME[0]}} ${LINENO}]  '
    else
        export PS4='+ [${FUNCNAME[0]:+${FUNCNAME[0]}} ${LINENO}]  '
    fi

    # XDG default directories
    if [[ -z "${XDG_CONFIG_HOME}" ]] || [[ ! -d "$XDG_CONFIG_HOME" ]] ; then
        XDG_CONFIG_HOME="${HOME}/.config"
        if [[ ! -d "$XDG_CONFIG_HOME" ]] ; then
            mkdir -p "$XDG_CONFIG_HOME"
        fi
    fi

    if [[ -z "${XDG_CACHE_HOME}" ]] || [[ ! -d "$XDG_CACHE_HOME" ]] ; then
        XDG_CACHE_HOME="${HOME}/.cache"
        if [[ ! -d "$XDG_CACHE_HOME" ]] ; then
            mkdir -p "$XDG_CACHE_HOME"
        fi
    fi

    if ((is_debug)) ; then
        echo -e "USER '$USER' UID '$UID' HOME '$HOME' TERM '$TERM' XDG_CACHE_HOME '$XDG_CACHE_HOME' XDG_CONFIG_HOME '$XDG_CONFIG_HOME'" 1>&2
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_title_set
#   DESCRIPTION:  Sets the terminal window title using escape sequences
#    PARAMETERS:  $* = title string
#       RETURNS:  -
#       EXAMPLE:  el_title_set "My Script Running"
#===============================================================================
el_title_set(){

    el_set_display_variables
    echo -ne "\033]0;$*\007"

}

#===  FUNCTION  ================================================================
#          NAME:  el_resolution_get
#   DESCRIPTION:  Retrieves the primary screen resolution via xrandr or xdpyinfo
#    PARAMETERS:  $1 = "h" (horizontal), "v" (vertical), or empty (full string)
#       RETURNS:  Resolution value (e.g., 1920x1080) or specific dimension
#       EXAMPLE:  width=$(el_resolution_get h)
#===============================================================================
el_resolution_get(){
    local buf resolution_h resolution_v resolution

    el_set_display_variables

    # xrandr: more reliable based on the primary screen:
    if [[ -z "$resolution" ]] ; then
        if [[ -x "$( which xrandr )" ]] ; then
            buf="$( LC_ALL=C xrandr -q | grep -w connected | psort -- -p "primary" | head -1 | tr ' ' '\n' )"

            resolution_h="$( echo "$buf" | grep "[[:digit:]]x[[:digit:]]" | sed -e 's|x.*$||g' )"
            read -r resolution_h <<< "$resolution_h"
            resolution_v="$( echo "$buf" | grep "[[:digit:]]x[[:digit:]]" | sed -e 's|^.*x||g' -e 's|\+.*$||g' )"
            read -r resolution_v <<< "$resolution_v"

            resolution="${resolution_h}x${resolution_v}"
        else
            el_warning "suggested missing dependency: xrandr"
        fi
    fi

    # faster default fallback
    if [[ -z "$resolution" ]] ; then
        if [[ -x "$( which xdpyinfo )" ]] ; then
            resolution="$( LC_ALL=C xdpyinfo | LC_ALL=C grep "dimensions:.*x" | awk '{print $2}' | tail -1 )"
        fi
    fi

    if [[ -z "$resolution_h" ]] ; then
        resolution_h="${resolution%%x*}"
        resolution_v="${resolution##*x}"
    fi

    if [[ -n "$resolution" ]] ; then
        if [[ -n "$1" ]] ; then
            if [[ "$1" = "h"* ]] ; then
                echo "$resolution_h"
            elif [[ "$1" = "v"* ]] ; then
                echo "$resolution_v"
            else
                echo "$resolution"
            fi
        else
            echo "$resolution"
        fi
    else
        el_error "problem obtaining primary screen resolution, missing dependencies? xdpyinfo | xrandr"
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_dpi_get
#   DESCRIPTION:  Calculates or rounds the DPI of the primary connected screen
#    PARAMETERS:  $1 = mode (lowered|rounded|empty)
#       RETURNS:  DPI string (e.g., 96x96, 144x144)
#       EXAMPLE:  dpi=$(el_dpi_get rounded)
#===============================================================================
el_dpi_get(){
    local resolution_h resolution_v screen_size_v screen_size_h buf dpi_v dpi_h dpi_lowered

    if el_dependencies_check "xrandr" ; then

        buf="$( LC_ALL=C xrandr -q | grep -w connected | psort -- -p "primary" | head -1 | tr ' ' '\n' )"

        resolution_h="$( echo "$buf" | grep "[[:digit:]]x[[:digit:]]" | sed -e 's|x.*$||g' )"
        read -r resolution_h <<< "$resolution_h"
        resolution_v="$( echo "$buf" | grep "[[:digit:]]x[[:digit:]]" | sed -e 's|^.*x||g' -e 's|\+.*$||g' )"
        read -r resolution_v <<< "$resolution_v"

        screen_size_h="$( echo "$buf" | grep "[[:digit:]]mm" | head -1 | sed -e 's|mm||g' )"
        screen_size_v="$( echo "$buf" | grep "[[:digit:]]mm" | tail -1 | sed -e 's|mm||g' )"

        if [[ -n "$screen_size_v" ]] && [[ -n "$screen_size_h" ]] && [[ "${screen_size_v%.*}" -gt 0 ]] && [[ "${screen_size_h%.*}" -gt 0 ]] ; then
            screen_size_h="$( echo "$screen_size_h / 25.4" | LC_ALL=C  bc -l )"
            screen_size_v="$( echo "$screen_size_v / 25.4" | LC_ALL=C  bc -l )"
        fi
    else
        NOREPORTS=1 el_error "missing dependency: xrandr"
    fi

    if [[ -n "$screen_size_v" ]] && [[ -n "$screen_size_h" ]] && [[ "${screen_size_v%.*}" -gt 0 ]] && [[ "${screen_size_h%.*}" -gt 0 ]] && [[ -n "$resolution_v" ]] && [[ -n "$resolution_h" ]] ; then
        dpi_h="$( echo "$resolution_h / $screen_size_h" | LC_ALL=C  bc -l | sed -e 's|\..*$||g' )"
        dpi_v="$( echo "$resolution_v / $screen_size_v" | LC_ALL=C  bc -l | sed -e 's|\..*$||g' )"
        # round it into steps of 10?
        #dpi_rounded="$((m=dpi_h%10, d=dpi_h-m, m >= 10/2 ? d+10 : d))"


        if [[ -n "$dpi_h" ]] && [[ -n "$dpi_v" ]] ; then

            case "$1" in
                lowered)
                    # reduce dpi by 3 / 4 which looks better
                    dpi_lowered="$( echo "((( ${dpi_h} / 4 ) * 3 ) / 96 ) * 100" | LC_ALL=C  bc -l | sed -e 's|\..*$||g' )"
                    if [[ -n "$dpi_lowered" ]] ; then
                        if [[ "$dpi_lowered" -le 98 ]] ; then
                            # 96 should be the min default
                            echo "96x96"
                        else
                            echo "${dpi_lowered}x${dpi_lowered}"
                        fi
                    else
                        echo "${dpi_h}x${dpi_v}"
                        el_warning "unable to lower the dpi calculation, used the original values instead: ${dpi_h}x${dpi_v}"
                    fi
                    ;;

                rounded)
                    if [[ "${dpi_h}" -le 80 ]] ; then
                        echo "96x96"
                        # temporal debug: this value should be correct?
                        el_error "wrong dpi fetch? ${dpi_h}x${dpi_v}"
                        return 0

                    elif [[ "${dpi_h}" -le 120 ]] ; then
                        echo "96x96"
                        return 0
                    elif [[ "${dpi_h}" -le 165 ]] ; then
                        # 96 x 1.5
                        echo "144x144"
                        return 0
                    elif [[ "${dpi_h}" -le 200 ]] ; then
                        # 96 x 2
                        echo "192x192"
                        return 0
                    #elif [[ "${dpi_h}" -le 300 ]] ; then
                    elif [[ "${dpi_h}" -le 300 ]] ; then
                        # 96 x 2.5
                        echo "240x240"
                        return 0
                    else
                        # 96 x 3 | this should be the maximum default value for now
                        echo "288x288"
                        if [[ "${dpi_h}" -ge 400 ]] ; then
                            el_error "wrong dpi fetch? ${dpi_h}x${dpi_v}"
                        fi
                        return 0
                    fi
                    ;;

                *)
                    echo "${dpi_h}x${dpi_v}"
                    return 0
                    ;;
            esac
        else
            el_error "problem fetching DPI sizes - $resolution_v - $resolution_h | $screen_size_v - $screen_size_h | $dpi_v - $dpi_h | $buf"
        fi

    else
        # fallback value when we don't have screen sizes and similar
        echo "96x96"
        return 0
    fi
}


#===  FUNCTION  ================================================================
#          NAME:  el_dependencies_check
#   DESCRIPTION:  Checks if required commands exist in PATH or standard sbin dirs
#    PARAMETERS:  $@ = command names (pipe '|' separated)
#       RETURNS:  0 if all found, 1 if any are missing
#       EXAMPLE:  el_dependencies_check "curl|wget|jq"
#===============================================================================
el_dependencies_check(){
    # example:  el_dependencies_check gmplayer|find|sed|etc
    local check found found_no dir

    while read -ru 3 check
    do
        unset found

        if command -v "$check" 1>/dev/null 2>&1 ; then
            found=yes
            break
        else
            for dir in ${PATH//:/ } /usr/local/sbin /usr/sbin /sbin
            do
                if test -x "${dir}/$check"
                then
                    found=yes
                    break
                fi
            done
        fi

        if [[ "$found" != "yes" ]] ; then
            NOREPORTS=1 el_error "Command '$check' for $(basename $0) not found, func: ${FUNCNAME[1]}"
            found_no="yes"
        fi
    done 3<<< "$( echo "${@}" | tr '|' '\n' )"


    if [[ "$found_no" = "yes" ]] ; then
        return 1
    else
        return 0
    fi
}


#===  FUNCTION  ================================================================
#          NAME:  el_dependencies_install
#   DESCRIPTION:  Installs packages using apt-get with GUI or terminal su/sudo
#    PARAMETERS:  $@ = package names (space or pipe separated)
#       RETURNS:  0 if success, 1 if installation failed
#       EXAMPLE:  el_dependencies_install "libnotify-bin|curl"
#===============================================================================
el_dependencies_install(){
    local depends temp is_failed

    depends="$( echo "$@" | tr '|' ' ' )"
    el_set_display_variables

    # TODO: move to terminology when compatibility will be implemented
    # TODO:  even better: use a gui tool for install deps
    el_dependencies_check urxvt

    temp="/tmp/.$(basename $0)-${USER}-$$"
    cat > "${temp}" << EOF
#!/bin/bash
SOURCE=dummy

echo -e "Installing Dependencies"

if [[ -x "\$( which urxvt )" ]] && [[ -n "\$DISPLAY" ]] ; then
    urxvt -T 'Installing Packages' -e bash -c "
source /usr/lib/elive-tools/functions
export TERM=screen-256color ; export DEBIAN_FRONTEND=noninteractive ; export DEBIAN_PRIORITY=critical ; export DEBCONF_NONINTERACTIVE_SEEN=true ; export DEBCONF_NOWARNINGS=true

if ! apt-get install --reinstall -y $depends ; then
    el_aptget_update force
    apt-get -f install

    if ! apt-get install -y $depends ; then
        apt-get -f install ; el_aptget_update force ; apt-get install -y $depends
        ret=\$?
    fi
fi
if ((ret)) ; then
    return \$ret
else
    echo ; echo ; echo ; echo ; echo 'All Done' ; sleep 2
fi
"


else
    if [[ -n "\$TERM" ]] ; then
        su -c "
source /usr/lib/elive-tools/functions
export TERM=screen-256color ; export DEBIAN_FRONTEND=noninteractive ; export DEBIAN_PRIORITY=critical ; export DEBCONF_NONINTERACTIVE_SEEN=true ; export DEBCONF_NOWARNINGS=true

if ! apt-get install --reinstall -y $depends ; then
    el_aptget_update force
    apt-get -f install

    if ! apt-get install -y $depends ; then
        apt-get -f install ; el_aptget_update force ; apt-get install -y $depends
        ret=\$?
    fi
fi
if ((ret)) ; then
    return \$ret
else
    echo ; echo ; echo ; echo ; echo 'All Done' ; sleep 2
fi

" root

    else
        el_error "unable to intall dependencies, we don't have access to urxvt or a terminal to do it"
        sleep 6
    fi
fi

EOF

    chmod +x "${temp}"

    if [[ "$UID" = 0 ]] ; then
        bash "$temp"
    else
        if el_check_sudo_automated ; then
            if sudo -H -n bash "$temp" ; then
                unset is_failed
            else
                is_failed=1
            fi
        else
            # deps
            if ! el_dependencies_check gksu ; then
                if [[ -x "$( which urxvt )" ]] ; then
                    urxvt -e bash -c "echo -e 'Installing Dependencies' ; su -c 'TERM=screen-256color DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true apt-get update ; apt-get install -y gksu ; ' root"
                else
                    if ((is_interactive)) ; then
                        su -c 'apt-get update ; TERM=screen-256color DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true apt-get update ; apt-get install -y gksu ; ' root
                    fi
                fi
            fi

            if [[ -x "$( which gksu )" ]] ; then
                # multiple gksu attempts
                for i in 1 2
                do
                    if gksu "$temp" ; then
                        unset is_failed
                        break
                    else
                        is_failed=1
                    fi
                done
            else
                if ((is_interactive)) ; then
                    el_info "Installing Dependencies"

                    if su -c "$temp" root ; then
                        unset is_failed
                    fi
                else
                    el_error "unable to run $temp, we don't have gksu, graphical system, or even a terminal where to do it"
                fi
            fi
        fi
    fi

    rm -f "${temp}"

    if ((is_failed)) ; then
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_package_remove
#   DESCRIPTION:  Uninstalls specified packages via apt-get
#    PARAMETERS:  $@ = package names (space or pipe separated)
#       RETURNS:  0 if success, 1 if removal failed
#       EXAMPLE:  el_package_remove "old-package-name"
#===============================================================================
el_package_remove(){
    local depends temp is_failed i list arch

    depends="$( echo "$@" | tr '|' ' ' )"
    el_set_display_variables

    el_dependencies_check urxvt

    arch="$( el_architecture host )"
    for i in $depends
    do
        if [[ -e "/var/lib/dpkg/info/${i}.list" ]] || [[ -e "/var/lib/dpkg/info/${i}:${arch}.list" ]] ; then
            list="$i $list"
        fi
    done
    depends="$( echo "$list" )"

    temp="/tmp/.$(basename $0)-${USER}-$$"
    cat > "${temp}" << EOF
#!/bin/bash
SOURCE=dummy

echo -e "Removing Packages"

if [[ -x "\$( which urxvt )" ]] && [[ -n "\$DISPLAY" ]] ; then
    urxvt -T 'Removing Packages' -e bash -c "
source /usr/lib/elive-tools/functions
export TERM=screen-256color ; export DEBIAN_FRONTEND=noninteractive ; export DEBIAN_PRIORITY=critical ; export DEBCONF_NONINTERACTIVE_SEEN=true ; export DEBCONF_NOWARNINGS=true

apt-get -f install
apt-get remove $depends
ret=\$?

if ((ret)) ; then
    return \$ret
else
    echo ; echo ; echo ; echo ; echo 'All Done' ; sleep 2
fi
"


else
    if [[ -n "\$TERM" ]] ; then
        su -c "
source /usr/lib/elive-tools/functions
export TERM=screen-256color ; export DEBIAN_FRONTEND=noninteractive ; export DEBIAN_PRIORITY=critical ; export DEBCONF_NONINTERACTIVE_SEEN=true ; export DEBCONF_NOWARNINGS=true

apt-get -f install
apt-get remove $depends
ret=\$?

if ((ret)) ; then
    return \$ret
else
    echo ; echo ; echo ; echo ; echo 'All Done' ; sleep 2
fi

" root

    else
        el_error "unable to intall dependencies, we don't have access to urxvt or a terminal to do it"
        sleep 6
    fi
fi

EOF

    chmod +x "${temp}"

    if [[ "$UID" = 0 ]] ; then
        bash "$temp"
    else
        if el_check_sudo_automated ; then
            if sudo -H -n bash "$temp" ; then
                unset is_failed
            else
                is_failed=1
            fi
        else
            # deps
            if ! el_dependencies_check gksu ; then
                if [[ -x "$( which urxvt )" ]] ; then
                    urxvt -e bash -c "echo -e 'Installing Dependencies' ; su -c 'TERM=screen-256color DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true apt-get update ; apt-get install -y gksu ; ' root"
                else
                    if ((is_interactive)) ; then
                        su -c 'apt-get update ; TERM=screen-256color DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true apt-get update ; apt-get install -y gksu ; ' root
                    fi
                fi
            fi

            if [[ -x "$( which gksu )" ]] ; then
                # multiple gksu attempts
                for i in 1 2
                do
                    if gksu "$temp" ; then
                        unset is_failed
                        break
                    else
                        is_failed=1
                    fi
                done
            else
                if ((is_interactive)) ; then
                    el_info "Removing Dependencies"

                    if su -c "$temp" root ; then
                        unset is_failed
                    fi
                else
                    el_error "unable to run $temp, we don't have gksu, graphical system, or even a terminal where to do it"
                fi
            fi
        fi
    fi

    rm -f "${temp}"

    if ((is_failed)) ; then
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_aptget_update
#   DESCRIPTION:  Updates apt package lists if older than 4 hours or if forced
#    PARAMETERS:  $1 = "force" (optional) to bypass the 4-hour cache
#       RETURNS:  0 if success, 1 if update failed or no internet
#       EXAMPLE:  el_aptget_update force
#===============================================================================
el_aptget_update(){
    local message_updating force want_update buf is_failed
    force="$1"

    el_set_display_variables


    if [[ "$force" = "force" ]] ; then
        want_update=1
    fi

    if [[ -e "/var/lib/apt/periodic/update-success-stamp" ]] ; then
        buf="$( echo "$(date +%s) - $( stat -c %Y /var/lib/apt/periodic/update-success-stamp 2>/dev/null )" | LC_ALL="$EL_LC_EN" bc -l | sed -e 's|\..*$||g' )"
        if [[ -n "$buf" ]] && [[ "$buf" -gt 14400 ]] ; then
            want_update=1
        fi
    else
        want_update=1
    fi

    if ((want_update)) ; then
        # only run update if we have not updated already 4 hours ago
        local _dummy
        _dummy="$( eval_gettext "Updating list of packages" )"
        # note: to have translated the next message we need a different declaration ^ so that the string is considered into the including for translate (without TEXTDOMAIN)
        message_updating="$( TEXTDOMAIN="elive-tools" eval_gettext "Updating list of packages" )"

        if ! el_verify_internet 2>/dev/null 1>&2 ; then
            sleep 5
            if ! el_verify_internet 2>/dev/null 1>&2 ; then
                el_error "We need to update the packages lists but we don't have internet? ignoring update..."
                return 1
            fi
        fi

        # wait for unlock
        el_aptget check 1>/dev/null

        if [[ "$UID" = 0 ]] ; then
            if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] && ! ((is_quiet)) && [[ -n "$guitool" ]]  ; then
                { apt-get update || apt-get update ; } | $guitool --progress --pulsate --percentage=0 --auto-close --text="$message_updating"
            else
                el_aptget update || el_aptget update
            fi
        else
            if el_check_sudo_automated ; then
                if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] && ! ((is_quiet)) && [[ -n "$guitool" ]] ; then
                    sudo -H -n apt-get update || sudo -H -n apt-get update | $guitool --progress --pulsate --percentage=0 --auto-close --text="$message_updating"
                else
                    sudo -H -n apt-get update || sudo -H -n apt-get update
                fi
            else
                if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]]  ; then
                    if ! el_dependencies_check gksu ; then
                        el_dependencies_install "gksu"
                    fi
                    #gksu apt-get update | $guitool --progress --pulsate --percentage=0 --auto-close --text="$message_updating"
                    # multiple gksu attempts
                    for i in 1 2
                    do
                        if gksu "$temp" ; then
                            if gksu apt-get update | $guitool --progress --pulsate --percentage=0 --auto-close --text="$message_updating" ; then
                                unset is_failed
                                break
                            else
                                is_failed=1
                            fi
                        fi
                    done

                    if ((is_failed)) && [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
                        timeout 40 $guitool --error --text="${SOURCE}: $( eval_gettext "Wrong password" )"
                    fi

                else
                    su -c "echo 'Updating Package Lists' ; apt-get update || apt-get update" root
                fi
            fi
        fi

    else
        el_explain 3 "Packages lists already updated at least 4 hours ago, skipping update"
    fi

    if ((is_failed)) ; then
        return 1
    fi

}
#===  FUNCTION  ================================================================
#          NAME:  el_package_newer_check
#   DESCRIPTION:  Compares installed vs candidate versions using apt cache
#    PARAMETERS:  $1 = package name
#       RETURNS:  0 if a newer version is available, 1 if up-to-date, 2 on error
#       EXAMPLE:  if el_package_newer_check "elive-tools" ; then echo "Update available" ; fi
#===============================================================================
el_package_newer_check(){
    local package installed candidate
    # XXX important: this uses apt caches, doesn't check for online version

    package="$1"

    if [[ -z "$package" ]] ; then
        return 2
    fi

    installed="$( el_apt_package_version_get "$package" installed full )"
    candidate="$( el_apt_package_version_get "$package" candidate full )"

    if [[ -n "$installed" ]] && [[ -n "$candidate" ]] ; then
        if dpkg --compare-versions "${candidate}" gt "${installed}" ; then
            return 0
        else
            return 1
        fi
    else
        el_error "no versions get correctly for package ${package}, installed is '${installed}', candidate is '${candidate}', func: ${FUNCNAME[1]}"
        return 1
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_package_upgrade_fast
#   DESCRIPTION:  Directly downloads and installs .deb packages from Elive repos
#    PARAMETERS:  $1 = download timeout, $2 = mode (force|normal), $@ = packages
#                 "force" allows downgrades; "normal" only upgrades
#       RETURNS:  0 if packages were installed, 1 if failed or nothing to do
#       EXAMPLE:  el_package_upgrade_fast 60 normal "elive-tools"
#===============================================================================
el_package_upgrade_fast(){
    # pre {{{
    local tmpdir timeout package installed url_subdir url distro package_url candidate mode dirprev failed arch is_download

    timeout="$1"
    shift
    mode="$1"
    shift

    tmpdir="/tmp/.$(basename $0)_${FUNCNAME[0]}_${USER}"
    failed=0

    if [[ -z "$*" ]] ; then
        NOREPORTS=1 el_error "Wrong parameters given, should be: ${FUNCNAME[0]} timeout mode packages"
        return 1
    fi

    # }}}
    dirprev="$(pwd)"
    mkdir -p "$tmpdir"
    cd "$tmpdir" || return 1

    if ! el_verify_internet 2>/dev/null 1>&2 ; then
        el_debug "no internet found, ignoring"
        return 1
    fi

    # checks
    if ! ls /var/lib/apt/lists/*Packages 1>/dev/null 2>&1 ; then
        # wait for unlock
        el_aptget check 1>/dev/null

        el_aptget_update force

        if ! ls /var/lib/apt/lists/*Packages 1>/dev/null 2>&1 ; then
            el_error "we don't have an ending *Packages file in our apt lists available"
        fi
    fi

    for package in "$@"
    do
        unset is_download installed arch url_subdir url distro candidate package_url failed
        [[ -z "$package" ]] && continue

        # example:  3.8.9+0.0.1+git3118fc936-6buster11
        installed="$( el_apt_package_version_get "$package" installed full )"
        if [[ -z "$installed" ]] ; then
            el_error "unable to get the version of installed package '${package}': $installed:\n$( LC_ALL=C apt-cache policy $package )"
            continue
        fi

        # example:  all
        arch="$( LC_ALL=C dpkg -l | grep -E "^(ii|hi|iU)\s+${package}(:.*\s+)?\s+" | awk '{print $4}' )"
        case "$arch" in
            amd64|i386)
                arch="_${arch}"
                ;;
            all)
                arch="_all"
                ;;
            *)
                el_error "unable to get the arch value for package '${package}': $arch"
                failed=1
                continue
                ;;
        esac

        # example:  pool/main/e/elive-tools/
        url_subdir="$( LC_ALL=C grep -h "^Filename: .*/${package}_" /var/lib/apt/lists/*Packages | sort -t'/' -k5,5 -V -u | sed -e "s|/${package}_.*$|/|g" -e 's|^Filename: ||g' | sort -u | head -1  )"
        if [[ -z "$url_subdir" ]] ; then
            # force update if is not found:
            el_aptget_update force

            url_subdir="$( LC_ALL=C grep -h "^Filename: .*/${package}_" /var/lib/apt/lists/*Packages | sort -t'/' -k5,5 -V -u | sed -e "s|/${package}_.*$|/|g" -e 's|^Filename: ||g' | sort -u | head -1  )"
            if [[ -z "$url_subdir" ]] ; then
                el_error "unable to get the url_subdir for package '${package}': $url_subdir"
                failed=1
                continue
            fi
        fi

        # example:  file:///home/thana/packages/mirror-elive/elive/
        # example:  http://repo.buster.elive.elivecd.org/
        url="$( grep -hE "^deb\s+(http.*elivecd.org|file:///)" /etc/apt/sources.list.d/*elive.list | awk '{print $2}' | tail -1 )"
        if [[ -n "$url" ]] ; then
            #make sure we end on slash
            url="${url%/}/"
        else
            el_error "unable to get the 'url' for package '${package}': $(grep -h "^deb " /etc/apt/sources.list.d/*elive.list )\nthis feature only works for now with packages from the Elive repository"
            failed=1
            continue
        fi

        # example:  buster
        distro="$( grep -hE "^deb\s+(http.*elivecd.org|file:///)" /etc/apt/sources.list.d/*elive.list | awk '{print $3}' | tail -1 )"
        if [[ -z "$distro" ]] ; then
            el_error "unable to get the 'distro' for package '${package}': $(grep -h "^deb " /etc/apt/sources.list.d/*elive.list )"
            failed=1
            continue
        fi

        # example:  http://repo.buster.elive.elivecd.org/pool/main/e/elive-tools/elive-tools_3.8.9+0.0.1+git3118fc936-6buster11_all.deb
        # example:  /home/thana/packages/mirror-elive/elive/pool/main/e/elive-tools/elive-tools_3.8.9+0.0.1+git3118fc936-6buster11_all.deb
        case "$url" in
            "file:///"*)
                package_url="${url#file://}${url_subdir}$( ls -1 "${url#file://}${url_subdir}" | grep "${package}_.*${distro}.*${arch}\.deb" | sort -uV | tail -1 )"
                if ! [[ -s "$package_url" ]] ; then
                    el_error "unable to get the 'package_url' for package '${package}' in file:/// mode:\n$( ls "${url#file://}${url_subdir}" | grep "${package}.*\.deb" )"
                    failed=1
                    continue
                fi
                ;;
            "http"*)
                package_url="$( lynx -read_timeout 12 -connect_timeout 12 -dump "${url}${url_subdir}" | grep "http.*/${package}_.*${distro}.*${arch}\.deb" | sed -e 's|^.* http|http|g' | tail -1 )"
                # try again, sometimes fails
                if [[ -z "$package_url" ]] ; then
                    for i in $(seq 3)
                    do
                        sleep 5
                        package_url="$( COLUMNS=800 LC_ALL=$EL_LC_EN  lynx -read_timeout 30 -connect_timeout 30 -dump "${url}${url_subdir}" | grep "http.*/${package}_.*${distro}.*${arch}\.deb" | sed -e 's|^.* http|http|g' | tail -1 )"
                        [[ -n "$package_url" ]] && break
                    done
                fi
                if [[ -z "$package_url" ]] ; then
                    el_error "unable to get the 'package_url' for package '${package}' distro '${distro}' arch '${arch}' :\n$( lynx -read_timeout 20 -connect_timeout 20 -dump "${url}${url_subdir}" | grep "${package}.*\.deb" )"
                    failed=1
                    continue
                fi

                ;;
        esac

        # example:  3.8.9+0.0.1+git3118fc936-6buster11
        candidate="$( echo "$package_url" | sed -e "s|^.*/${package}_||g" -e 's|_.*$||g' )"
        if [[ -z "$candidate" ]] ; then
            el_error "unable to get the 'candidate' for package '${package}': $(echo "$package_url" )"
            failed=1
            continue
        fi

        # install dependencies if needed
        if ! el_dependencies_check wget ; then
            el_dependencies_install "wget"
        fi

        # update: if force, we don't care now about the specific version, because we allow downgrades:
        case "$mode" in
            force)
                # different version
                if [[ "$installed" = "$candidate" ]] ; then
                    el_debug "no new version found for ${package}, installed '$installed' vs candidate '${candidate}': ${package_url}"
                else
                    el_debug "downloading candidate for ${package}, installed '$installed' vs candidate '${candidate}': ${package_url} "
                    is_download=1
                fi
                ;;
            normal)
                if dpkg --compare-versions "$installed" lt "$candidate" ; then
                    el_debug "new version for ${package}, installed '$installed' vs candidate '${candidate}': ${package_url}"
                    is_download=1
                else
                    el_debug "no new version found for ${package}, installed '$installed' vs candidate '${candidate}': ${package_url}"
                fi
                ;;
            *)
                el_warning "unknown mode $mode for ${FUNCNAME[0]}"
                ;;
        esac

        if ((is_download)) ; then
            case "$package_url" in
                "/"*)     cp -f "$package_url" .  ; ;;
                "http"*)  timeout ${timeout} wget -qc "$package_url"  ; ;;
            esac
        fi
    done

    # install all downloaded packages
    if ls ./*deb  1>/dev/null 2>&1 ; then
        el_debug "going to install:\n$( ls -1 ./*deb )"

        # wait for unlock
        el_aptget check 1>/dev/null

        if [[ "$UID" = 0 ]] ; then
            TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  dpkg -i ./*deb 1>/dev/null
            TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  dpkg --configure -a

            if TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  el_aptget -f install -q=2 -y ; then
                el_debug "updated packages successfully: $*"
                failed=0
            else
                sleep 10
                # check for good state
                el_aptget check 1>/dev/null

                TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  dpkg --configure -a
                TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  el_aptget -f install -q=2 -y


                # try to install again
                TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  dpkg -i ./*deb 1>/dev/null
                TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  dpkg --configure -a

                if TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  el_aptget -f install -q=2 -y ; then
                    el_debug "updated packages successfully: $*"
                    failed=0
                else
                    failed=1
                fi
            fi
        else
            el_sudo bash -c "dpkg -i *deb ; dpkg --configure -a"

            if el_aptget -f install -q=2 -y ; then
                el_debug "updated packages successfully: $*"
                failed=0
            else
                el_sudo bash -c "dpkg --configure -a ; apt-get -f install -q=2 -y ; dpkg -i *deb ; dpkg --configure -a"

                if el_aptget -f install -q=2 -y ; then
                    el_debug "updated packages successfully: $*"
                    failed=0
                else
                    failed=1
                fi
            fi
        fi
    else
        failed=1
    fi

    cd "$dirprev" || el_warning "prev dir doesnt exist anymore: $dirprev"
    rm -rf "$tmpdir"

    # exit with the validated status
    if ((failed)) ; then
        return 1
    else
        return 0
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_check_variables
#   DESCRIPTION:  Verifies that the named variables are set and non-empty
#    PARAMETERS:  $@ = variable names (pipe '|' separated)
#       RETURNS:  0 if all variables have values, 1 if any are empty/unset
#       EXAMPLE:  el_check_variables "USER|HOME|DISPLAY"
#===============================================================================
el_check_variables(){
    # example $0 foo,bar,we
    # note: if you get an error from this function, note that the function needs to be called with the varname, not its $value
    local found_no _var

    while read -ru 3 _var
    do
        if [[ -n "$ZSH_VERSION" ]] ; then
            if [[ ! -n "${(P)_var}" ]] ; then
                NOREPORTS=1 el_error "Variable '$_var' is not set, func: ${FUNCNAME[1]}"
                found_no="yes"
            fi
        else
            if [[ -z "${!_var}" ]] ; then
                NOREPORTS=1 el_error "Variable '$_var' is not set, func: ${FUNCNAME[1]}"
                found_no="yes"
            fi
        fi
    done 3<<< "$( echo "${@}" | tr '|' '\n' )"

    if [[ "$found_no" = "yes" ]] ; then
        return 1
    else
        return 0
    fi
}


#===  FUNCTION  ================================================================
#          NAME:  el_check_files
#   DESCRIPTION:  Verifies existence and non-zero size of specified files
#    PARAMETERS:  $@ = file paths (pipe '|' separated)
#       RETURNS:  0 if all files exist and are not empty, 1 otherwise
#       EXAMPLE:  el_check_files "/etc/elive-version|/etc/hosts"
#===============================================================================
el_check_files(){
    # example $0 foo,bar,we
    local _var found_no

    if [[ -z "$1" ]] ; then
        echo -e "E: ${FUNCNAME[0]} called without parameters (nothing to check?)" 1>&2

        return 1
    fi

    while read -ru 3 _var
    do
        if [[ ! -s "${_var}" ]] ; then
            NOREPORTS=1 el_error "File '$_var' not exist, func: ${FUNCNAME[1]}"
            found_no="yes"
        fi
    done 3<<< "$( echo "${@}" | tr '|' '\n' )"

    if [[ "$found_no" = "yes" ]] ; then
        return 1
    else
        return 0
    fi

}


#===  FUNCTION  ================================================================
#          NAME:  el_check_dirs
#   DESCRIPTION:  Verifies that the specified paths exist and are directories
#    PARAMETERS:  $@ = directory paths (pipe '|' separated)
#       RETURNS:  0 if all paths are directories, 1 otherwise
#       EXAMPLE:  el_check_dirs "/home/$USER|/tmp"
#===============================================================================
el_check_dirs(){
    # example $0 foo,bar,we
    local found_no line

    if [[ -z "$1" ]] ; then
        echo -e "E: ${FUNCNAME[0]} called without parameters (nothing to check?)" 1>&2

        return 1
    fi

    while read -ru 3 line
    do
        if [[ ! -d "${line}" ]] ; then
            NOREPORTS=1 el_error "Directory '$line' not exist, func: ${FUNCNAME[1]}"
            found_no="yes"
        fi
    done 3<<< "$( echo "${@}" | tr '|' '\n' )"

    if [[ "$found_no" = "yes" ]] ; then
        return 1
    else
        return 0
    fi
}


#===  FUNCTION  ================================================================
#          NAME:  el_check_dir_has_files
#   DESCRIPTION:  Verifies that directories exist and contain at least one file
#    PARAMETERS:  $@ = directory paths (pipe '|' separated)
#       RETURNS:  0 if all directories are non-empty, 1 otherwise
#       EXAMPLE:  el_check_dir_has_files "/etc/apt/sources.list.d"
#===============================================================================
el_check_dir_has_files(){
    local dir files

    if [[ -z "$1" ]] ; then
        echo -e "E: ${FUNCNAME[0]} called without parameters (nothing to check?)" 1>&2
        return 1
    fi

    while read -ru 3 dir
    do
        if [[ ! -d "$dir" ]] && [[ ! -L "$dir" ]] ; then
            el_debug "Directory '$dir' not exist, func: ${FUNCNAME[1]}"

            return 1
        else
            files="$( command ls -1 "${dir%/}/"* 2>/dev/null | wc -l )"
            if ! [[ "$files" -gt 0 ]] ; then
                el_debug "Directory '$dir' doesn't contain any file, func: ${FUNCNAME[1]}"

                return 1
            fi
        fi
    done 3<<< "$( echo "${@}" | tr '|' '\n' )"
}

#===  FUNCTION  ================================================================
#          NAME:  el_check_sudo_automated
#   DESCRIPTION:  Checks if sudo can be run without a password prompt (non-interactive)
#    PARAMETERS:  -
#       RETURNS:  0 if sudo is passwordless, 1 if it requires a password
#       EXAMPLE:  if el_check_sudo_automated ; then sudo apt-get update ; fi
#===============================================================================
el_check_sudo_automated(){
    if sudo -H -n echo 2>/dev/null 1>/dev/null ; then
        return 0
    else
        el_debug "Automated sudo is required: Your sudo requires a password, we need it to be configured for not request it, func: ${FUNCNAME[1]}"
        return 1
    fi
}
#===  FUNCTION  ================================================================
#          NAME:  el_sudo
#   DESCRIPTION:  Runs a command as root using sudo, gksu, or su as available
#    PARAMETERS:  $@ = command and arguments to execute
#       RETURNS:  Exit status of the executed command
#       EXAMPLE:  el_sudo apt-get update
#===============================================================================
el_sudo(){
    # pre {{{
    local args is_failed returned
    el_security_function_loop || return 0

    args="$@"
    el_check_variables "args"

    el_set_display_variables
    returned=0

    # }}}
    if el_check_sudo_automated 2>/dev/null 1>&2 ; then
        sudo -H -n "$@"
        returned=$?
    else

        # try to run with automated sudo (without-prompt) first:
        if [[ "${DISPLAY}" = ":0.0" ]] || [[ "${DISPLAY}" = ":0" ]] ; then
            if ! el_dependencies_check gksu ; then
                el_dependencies_install "gksu"
            fi

            # multiple gksu attempts
            for i in 1 2
            do
                gksu $@
                returned=$?

                if [[ "$returned" = 0 ]] ; then
                    unset is_failed
                    break
                else
                    is_failed=1
                fi
            done

            if ((is_failed)) && [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
                timeout 40 $guitool --error # --text="${SOURCE}: $( eval_gettext "Wrong password" )"
            fi
        else
            if ((is_interactive)) ; then
                el_info "Insert Admin Password to run: $*"

                if groups | grep -qsw "sudo" ; then
                    sudo -H "$@"
                    returned=$?
                else
                    # simulate a fail to use "su -c" instead
                    returned=1
                fi

                if ! [[ "$returned" = 0 ]] ; then
                    su -c "$@" root
                    returned=$?
                fi
            else
                el_error "unable to run el_sudo because we are not in interactive mode"
                returned=1
            fi
        fi
    fi

    return "$returned"

}
#===  FUNCTION  ================================================================
#          NAME:  el_check_translations_required
#   DESCRIPTION:  Checks if the current TEXTDOMAIN needs more translation work
#    PARAMETERS:  $1 = mode ("report" to notify user)
#       RETURNS:  0 if translation is needed (>6% untranslated), 1 otherwise
#       EXAMPLE:  el_check_translations_required report
#===============================================================================
el_check_translations_required(){
    # pre {{{
    local stat percentage_untranslated is_translation_required mode

    # dont annoy users in the stable released
    if ! el_check_version_development ; then
        return 1
    fi

    mode="$1"
    if [[ -z "$mode" ]] ; then
        el_warning "deprecated use of ${FUNCNAME[0]} from ${FUNCNAME[1]} without the \$mode var set"
    fi

    # }}}

    if [[ -n "$TEXTDOMAIN" ]] ; then
        # we already said it ?
        if el_flag check "translation_required_${TEXTDOMAIN}" ; then
            return 1
        fi
        el_flag add "translation_required_${TEXTDOMAIN}"

        # deprecated location
        stat="/var/cache/elive-translations/statistics/${LANG%%_*}/${TEXTDOMAIN}.txt"

        if [[ -s "${stat}" ]] ; then
            percentage_untranslated="$(cat "${stat}" | LC_ALL=C grep -F "messages_untranslated_percentage" | awk '{print $2}' | tail -1 )"

            if [[ -n "${percentage_untranslated}" ]] && [[ "${percentage_untranslated}" -gt 6 ]] ; then
                is_translation_required=1
            fi
        fi
    fi


    if ((is_translation_required)) ; then

        case "$mode" in
            report)
                el_notify normal gtranslator "$( eval_gettext "Elive Translations" )" "$( eval_gettext "This application is not fully translated to your language. You can use Eltrans to help improve the Elive translations, and then everybody will enjoy Elive correctly translated into your language too." )"
                ;;
        esac

        return 0
    else
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_check_version_development
#   DESCRIPTION:  Checks /etc/elive-version to see if this is a development build
#    PARAMETERS:  -
#       RETURNS:  0 if development version, 1 if stable
#       EXAMPLE:  if el_check_version_development ; then echo "Beta version" ; fi
#===============================================================================
el_check_version_development(){
    if [[ -e "/etc/elive-version" ]] ; then
        if [[ "$( LC_ALL=C grep "^stable-release" /etc/elive-version | awk '{print $2}' )" = yes ]] ; then
            return 1
        else
            return 0
        fi
    else
        NOREPORTS=1 el_error "File /etc/elive-version not found, func: ${FUNCNAME[1]}"
        return 0
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_elive_version_get
#   DESCRIPTION:  Retrieves the Elive version string from /etc/elive-version
#    PARAMETERS:  -
#       RETURNS:  Version string (e.g., "3.8.30")
#       EXAMPLE:  version=$(el_elive_version_get)
#===============================================================================
el_elive_version_get(){
    # pre {{{
    local version
    el_debug
    el_security_function_loop || return 0

    # }}}
    version="$( LC_ALL=C grep "^elive-version: " "/etc/elive-version" )"
    version="${version#elive-version: }"

    if [[ -z "$version" ]] ; then
        version="2.9.99"
    fi

    if [[ -n "$version" ]] ; then
        echo "$version"
    else
        el_error "Unable to get this version of Elive ID number"
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_check_version_is_days_recent
#   DESCRIPTION:  Checks if the system build date is within the last N days
#    PARAMETERS:  $1 = number of days to check against
#       RETURNS:  0 if build is recent, 1 if older or error
#       EXAMPLE:  if el_check_version_is_days_recent 30 ; then echo "Recent build" ; fi
#===============================================================================
el_check_version_is_days_recent(){
    # pre {{{
    local date_builded_y date_builded_m date_builded_d date_today_y date_today_m date_today_d days_required date_builded date_today date_builded_total_days days_difference date_today_total_days
    el_debug
    el_security_function_loop || return 0

    days_required="$1"

    # }}}

    # calculate date builded {{{

    date_builded="$( LC_ALL=C grep "^date-builded" /etc/elive-version | sed -e 's|date-builded: ||g' )"
    if [[ -z "$date_builded" ]] ; then
        date_builded="2018-05-00"
    fi

    date_builded_y="${date_builded%%-*}"

    date_builded_m="${date_builded%-*}"
    date_builded_m="${date_builded_m#*-}"
    date_builded_m="${date_builded_m#0}"

    date_builded_d="${date_builded##*-}"
    date_builded_d="${date_builded_d#0}"

    date_builded_total_days="$(( ${date_builded_m} * 30 + ${date_builded_d} ))"
    date_builded_total_days="$(( ${date_builded_y} * 365 + ${date_builded_total_days} ))"

    # - calculate date builded }}}
    # calculate date today {{{

    date_today="$( date +%F )"

    date_today_y="${date_today%%-*}"

    date_today_m="${date_today%-*}"
    date_today_m="${date_today_m#*-}"
    date_today_m="${date_today_m#0}"

    date_today_d="${date_today##*-}"
    date_today_d="${date_today_d#0}"

    date_today_total_days="$(( ${date_today_m} * 30 + ${date_today_d} ))"
    date_today_total_days="$(( ${date_today_y} * 365 + ${date_today_total_days} ))"

    # - calculate date today }}}
    days_difference="$(( ${date_today_total_days} - ${date_builded_total_days} ))"

    # retrun false if we have any error calculating it
    if [[ -z "$days_difference" ]] || [[ -z "$days_required" ]] ; then
        return 1
    fi

    if [[ "$days_difference" -le "$days_required" ]] ; then
        return 0
    else
        return 1
    fi
}
#===  FUNCTION  ================================================================
#          NAME:  el_check_version_development_is_days_recent
#   DESCRIPTION:  Checks if a development build date is within the last N days
#    PARAMETERS:  $1 = number of days to check against
#       RETURNS:  0 if recent dev build, 1 if stable or old build
#       EXAMPLE:  el_check_version_development_is_days_recent 15
#===============================================================================
el_check_version_development_is_days_recent(){
    # pre {{{
    local date_builded_y date_builded_m date_builded_d date_today_y date_today_m date_today_d days_required date_builded date_today date_builded_total_days days_difference date_today_total_days
    el_debug
    el_security_function_loop || return 0

    days_required="$1"

    if ! el_check_version_development ; then
        # stable versions should be not considered recent dev versions
        return 1
    fi

    # }}}

    # calculate date builded {{{

    date_builded="$( LC_ALL=C grep "^date-builded" /etc/elive-version | sed -e 's|date-builded: ||g' )"
    if [[ -z "$date_builded" ]] ; then
        date_builded="2018-05-00"
    fi

    date_builded_y="${date_builded%%-*}"

    date_builded_m="${date_builded%-*}"
    date_builded_m="${date_builded_m#*-}"
    date_builded_m="${date_builded_m#0}"

    date_builded_d="${date_builded##*-}"
    date_builded_d="${date_builded_d#0}"

    date_builded_total_days="$(( ${date_builded_m} * 30 + ${date_builded_d} ))"
    date_builded_total_days="$(( ${date_builded_y} * 365 + ${date_builded_total_days} ))"

    # - calculate date builded }}}
    # calculate date today {{{

    date_today="$( date +%F )"

    date_today_y="${date_today%%-*}"

    date_today_m="${date_today%-*}"
    date_today_m="${date_today_m#*-}"
    date_today_m="${date_today_m#0}"

    date_today_d="${date_today##*-}"
    date_today_d="${date_today_d#0}"

    date_today_total_days="$(( ${date_today_m} * 30 + ${date_today_d} ))"
    date_today_total_days="$(( ${date_today_y} * 365 + ${date_today_total_days} ))"

    # - calculate date today }}}
    days_difference="$(( ${date_today_total_days} - ${date_builded_total_days} ))"

    # retrun false if we have any error calculating it
    if [[ -z "$days_difference" ]] || [[ -z "$days_required" ]] ; then
        return 1
    fi


    if [[ "$days_difference" -le "$days_required" ]] ; then
        return 0
    else
        return 1
    fi
}
#===  FUNCTION  ================================================================
#          NAME:  el_check_is_shell
#   DESCRIPTION:  Determines if the script is being sourced in an interactive shell
#    PARAMETERS:  -
#       RETURNS:  0 if in a shell (bash/zsh), 1 if running as a script
#       EXAMPLE:  if el_check_is_shell ; then return 0 ; fi
#===============================================================================
el_check_is_shell(){
    # pre {{{
    #el_security_function_loop || return 0

    # }}}
    # check when in bash
    if [[ "$0" = "bash" ]] ; then
        return 0
    fi

    # check when in zsh
    if [[ -n "$ZSH_VERSION" ]] && [[ -z "$ZSH_SCRIPT" ]] ; then
        return 0
    fi

    # anything else is a script
    return 1
}

#===  FUNCTION  ================================================================
#          NAME:  el_explain
#   DESCRIPTION:  Prints formatted progress/debug messages to stderr with colors
#    PARAMETERS:  $1 = verbose level (0=error, 1=warn, 2=info, 3=debug)
#                 $@ = message (supports __word__ and XXwordXX highlighting)
#       RETURNS:  -
#       EXAMPLE:  el_explain 2 "Processing __file__ now"
#===============================================================================
el_explain(){
    # pre {{{
    if ! ((is_print_fancy)) || ! [[ "${EL_DEBUG}" -ge 2 ]] ; then
        return 0
    fi

    local funclevel spaced verbose_level message

    verbose_level="$1"
    shift
    message="$*"

    # show only if we are on this verbose level
    if ! [[ "${EL_DEBUG}" -ge "$verbose_level" ]] ; then
        return 0
    fi

    #spaced="$verbose_level"
    funclevel="$(( ${#FUNCNAME[*]} - 2 ))"

    el_check_variables "verbose_level|message"
    # }}}
    # tabulate output {{{
    #while [[ $spaced -gt 0 ]] ; do
        #message="  $message"
        #spaced="$(( $spaced - 1 ))"
    #done

    # }}}
    # hilight words {{{
    message="${message/__/${el_c_b}}"
    message="${message/__/${el_c_gr}}"
    message="${message/__/${el_c_b}}"
    message="${message/__/${el_c_gr}}"
    message="${message/__/${el_c_b}}"
    message="${message/__/${el_c_gr}}"
    message="${message/XX/${el_c_r}}"
    message="${message/XX/${el_c_gr}}"
    message="${message/XX/${el_c_r}}"
    message="${message/XX/${el_c_gr}}"
    message="${message/XX/${el_c_r}}"
    message="${message/XX/${el_c_gr}}"
    # }}}
    # show {{{
    # this is a good example of levels of colors visualization for the user's eye
    # show normal message unless we are at a debugging level
    if [[ "${EL_DEBUG}" -ge 3 ]] ; then
        case $funclevel in
            1)
                echo -en " ${el_c_gr}[${el_c_b2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            2)
                echo -en " ${el_c_gr}[${el_c_b}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            3)
                echo -en " ${el_c_gr}[${el_c_c2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            4)
                echo -en " ${el_c_gr}[${el_c_c}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            5)
                echo -en " ${el_c_gr}[${el_c_g2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            6)
                echo -en " ${el_c_gr}[${el_c_g}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            7)
                echo -en " ${el_c_gr}[${el_c_y2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            8)
                echo -en " ${el_c_gr}[${el_c_y}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            9)
                echo -en " ${el_c_gr}[${el_c_r2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            10)
                echo -en " ${el_c_gr}[${el_c_r}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            11)
                echo -en " ${el_c_gr}[${el_c_m2}${verbose_level}${el_c_gr}] " 1>&2
                ;;
            *)
                echo -en " ${el_c_gr}[${el_c_m}${verbose_level}${el_c_gr}] " 1>&2
                ;;
        esac
    else
        echo -en "${el_c_gr}  " 1>&2
    fi

    # continuation of the message
    echo -e "${message}${el_c_n}" 1>&2

    # }}}
}
#===  FUNCTION  ================================================================
#          NAME:  el_debug
#   DESCRIPTION:  Prints debug info including source file, line, and function stack
#    PARAMETERS:  $@ = message (optional)
#       RETURNS:  -
#       EXAMPLE:  el_debug "Variable x is $x"
#===============================================================================
el_debug(){
    # pre {{{
    if ! ((is_print_fancy)) || [[ ! "${EL_DEBUG}" -ge 3 ]] ; then
        return 0
    fi
    # don't show debug messages from this own tool (like if we are in a shell and the functions calls themselves)
    # check if we have the variable set, because we need to use it (so this may not work in ZSH)
    if [[ -z "${BASH_SOURCE[*]}" ]] && ! el_check_is_shell ; then
        return 0
    fi

    local count counter funcnames sourcenames_arr arg sourcename message
    message="$*"

    # }}}
    # get the list of function names array {{{
    if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
        counter="${#FUNCNAME[*]}"
        count=0
        for arg in "${FUNCNAME[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ "$arg" != "source" ]] && [[ "$arg" != "main" ]] ; then
                    funcnames="$arg > $funcnames"
                fi
            fi
        done
        funcnames="${funcnames% >*}"

        counter="${#BASH_SOURCE[*]}"
        count=0
        for arg in "${BASH_SOURCE[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ -n "$arg" ]] ; then
                    arg="$( readlink -f "$arg" || true )"
                    el_array_member_add "$arg >" "${sourcenames_arr[@]}" ; sourcenames_arr=("${_out[@]}")
                fi
            fi
        done

        sourcename="$( readlink -f "$0" || true )"
        if [[ -n "$sourcenames_arr" ]] ; then
            if [[ "${sourcenames_arr[*]}" = "$sourcename >"* ]] ; then
                sourcenames_arr="$( echo "${sourcenames_arr[*]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            else
                sourcenames_arr="$sourcename $( echo "${sourcenames_arr[*]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            fi
            sourcenames_arr="${sourcenames_arr% }"
            sourcenames_arr="${sourcenames_arr#> }"
            #read -r sourcenames_arr <<< "$sourcenames_arr"
        else
            sourcenames_arr="$sourcename"
        fi
    fi

    # }}}
    # show debug {{{

    if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
        if [[ -n "$message" ]] ; then
            echo -e "${el_c_c}D: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames}] ${el_c_c}$message${el_c_n}" 1>&2
        else
            echo -e "${el_c_gr}C: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames}]${el_c_n}" 1>&2
        fi
    else
        if [[ -n "$message" ]] ; then
            echo -e "${el_c_c}D: ${el_c_c}$message${el_c_n}" 1>&2
        fi
    fi

    # }}}
}

#===  FUNCTION  ================================================================
#          NAME:  el_info
#   DESCRIPTION:  Prints informational messages to stderr
#    PARAMETERS:  $@ = message
#       RETURNS:  -
#       EXAMPLE:  el_info "Starting backup process..."
#===============================================================================
el_info(){
    # pre {{{
    if ! ((is_print_fancy)) || [[ ! "${EL_DEBUG}" -ge 1 ]] ; then
        return 0
    fi

    local count counter funcnames sourcenames_arr arg sourcename message
    message="$@"

    # }}}
    # get the list of function names array {{{
    if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
        counter="${#FUNCNAME[*]}"
        count=0
        for arg in "${FUNCNAME[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ "$arg" != "source" ]] && [[ "$arg" != "main" ]] ; then
                    funcnames="$arg > $funcnames"
                fi
            fi
        done

        funcnames="${funcnames% >*}"

        counter="${#BASH_SOURCE[*]}"
        count=0
        for arg in "${BASH_SOURCE[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ -n "$arg" ]] ; then
                    arg="$( readlink -f "$arg" || true )"
                    sourcenames_arr+=("$arg >")
                    # el_array_member_add "$arg >" "${sourcenames_arr[@]}" ; sourcenames_arr=("${_out[@]}")
                fi
            fi
        done

        sourcename="$( readlink -f "$0" || true )"
        if [[ -n "$sourcenames_arr" ]] ; then
            if [[ "${sourcenames_arr[*]}" = "$sourcename >"* ]] ; then
                sourcenames_arr="$( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            else
                sourcenames_arr="$sourcename $( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            fi
            sourcenames_arr="${sourcenames_arr% }"
            sourcenames_arr="${sourcenames_arr#> }"
            #read -r sourcenames_arr <<< "$sourcenames_arr"
        else
            sourcenames_arr="$sourcename"
        fi
    fi

    # }}}
    # show info {{{

    if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
        echo -e "${el_c_c2}I: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames}] ${el_c_c2}$message${el_c_n}" 1>&2
    else
        echo -e "${el_c_c2}I: ${el_c_c2}$message${el_c_n}" 1>&2
    fi

    # }}}
}

#===  FUNCTION  ================================================================
#          NAME:  el_warning
#   DESCRIPTION:  Prints warning messages
#    PARAMETERS:  $@ = message
#       RETURNS:  -
#       EXAMPLE:  el_warning "Disk space is low"
#===============================================================================
el_warning(){
    # pre {{{
    local count counter funcnames sourcenames_arr arg message is_noreports
    message="$*"

    # }}}
    # get the list of function names array {{{
    if { [[ -z "$NOREPORTS" ]] && [[ -z "$NOREPORT" ]] && [[ -z "$NO_ELIVETOOLS_REPORTS" ]] && { [[ -n "$REPORTS" ]] || [[ -n "$EL_REPORTS" ]] || [[ -n "$FORCE_REPORTS" ]] ; } ; }    || { [[ -n "$is_print_fancy" ]] && [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; } ; then

        counter="${#FUNCNAME[*]}"
        count=0
        for arg in "${FUNCNAME[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ "$arg" != "source" ]] && [[ "$arg" != "main" ]] ; then
                    funcnames="$arg > $funcnames"
                fi
            fi
        done
        funcnames="${funcnames% >*}"

        counter="${#BASH_SOURCE[*]}"
        count=0
        for arg in "${BASH_SOURCE[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ -n "$arg" ]] ; then
                    sourcenames_arr+=("$arg >")
                    # el_array_member_add "$arg >" "${sourcenames_arr[@]}" ; sourcenames_arr=("${_out[@]}")
                fi
            fi
        done
        if [[ -n "$sourcenames_arr" ]] ; then
            if [[ "${sourcenames_arr[*]}" = "$0 >"* ]] ; then
                sourcenames_arr="$( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            else
                sourcenames_arr="$0 $( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            fi
            sourcenames_arr="${sourcenames_arr% }"
            sourcenames_arr="${sourcenames_arr#> }"
            #read -r sourcenames_arr <<< "$sourcenames_arr"
        else
            sourcenames_arr="$0"
        fi
    fi

    # }}}
    # show warning {{{

    if ((is_print_fancy)) && [[ "${EL_DEBUG}" -ge 1 ]] ; then
        if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
            echo -e "${el_c_y2}W: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames}] ${el_c_y2}$message${el_c_n}" 1>&2
        else
            echo -e "${el_c_y2}W: ${el_c_y2}$message${el_c_n}" 1>&2
        fi
    fi

    if [[ -n "${BASH_SOURCE[*]}" ]] ; then
        # set colors for our reports
        local el_c_n el_c_r el_c_r2 el_c_g el_c_g2 el_c_y el_c_y2 el_c_m el_c_m2 el_c_c el_c_c2 el_c_b el_c_b2
        el_c_gr="\033[1;30m" # Gray color
        el_c_gr2="\033[0;30m" # Gray2 color
        el_c_r="\033[1;31m" # Red color  (orig: red)
        el_c_r2="\033[0;31m" # Red2 color  (orig: red)
        el_c_g="\033[1;32m" # Green color  (orig: green)
        el_c_g2="\033[0;32m" # Green2 color  (orig. green2)
        el_c_y="\033[1;33m" # Yellow color  (orig. yellow)
        el_c_y2="\033[0;33m" # Yellow2 color  (orig. yellow)
        el_c_b="\033[1;34m" # Blue color
        el_c_b2="\033[0;34m" # Blue2 color
        el_c_m="\033[1;35m" # Magenta color
        el_c_m2="\033[0;35m" # Magenta2 color
        el_c_c="\033[1;36m" # Cyan color
        el_c_c2="\033[0;36m" # Cyan2 color
        el_c_w="\033[1;37m" # White
        el_c_w2="\033[0;37m" # White strong
        el_c_n="\033[0;39m" # Normal color  (orig: normal)

        el_report_to_elive "${el_c_y2}W: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames} ] ${el_c_y2}$message${el_c_n}"
    fi

    # }}}
}

#===  FUNCTION  ================================================================
#          NAME:  el_error
#   DESCRIPTION:  Prints error messages
#    PARAMETERS:  $@ = message
#       RETURNS:  -
#       EXAMPLE:  el_error "Failed to mount device"
#===============================================================================
el_error(){
    # pre {{{
    local count counter funcnames sourcenames_arr arg message
    message="$*"

    # }}}
    # get the list of function names array {{{
    if { [[ -z "$NOREPORTS" ]] && [[ -z "$NOREPORT" ]] && [[ -z "$NO_ELIVETOOLS_REPORTS" ]] && { [[ -n "$REPORTS" ]] || [[ -n "$EL_REPORTS" ]] || [[ -n "$FORCE_REPORTS" ]] ; } ; }    || { [[ -n "$is_interactive" ]] && [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; } ; then
        counter="${#FUNCNAME[*]}"
        count=0
        for arg in "${FUNCNAME[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ "$arg" != "source" ]] && [[ "$arg" != "main" ]] ; then
                    funcnames="$arg > $funcnames"
                fi
            fi
        done
        funcnames="${funcnames% >*}"

        counter="${#BASH_SOURCE[*]}"
        count=0
        for arg in "${BASH_SOURCE[@]}"
        do
            count="$(( $count + 1 ))"
            if [[ "$count" -gt 1 ]] && [[ "$count" -lt "$counter" ]] ; then
                if [[ -n "$arg" ]] ; then
                    sourcenames_arr+=("$arg >")
                    # el_array_member_add "$arg >" "${sourcenames_arr[@]}" ; sourcenames_arr=("${_out[@]}")
                fi
            fi
        done
        if [[ -n "$sourcenames_arr" ]] ; then
            if [[ "${sourcenames_arr[*]}" = "$0 >"* ]] ; then
                sourcenames_arr="$( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            else
                sourcenames_arr="$0 $( echo "${sourcenames_arr[@]}" | tr ' ' '\n' | tac | tr '\n' ' ' )"
            fi
            sourcenames_arr="${sourcenames_arr% }"
            sourcenames_arr="${sourcenames_arr#> }"
            #read -r sourcenames_arr <<< "$sourcenames_arr"
        else
            sourcenames_arr="$0"
        fi
    fi

    # }}}
    # show error {{{


    if ((is_print_fancy)) && [[ "${EL_DEBUG}" -ge 0 ]] ; then
        if [[ -n "${BASH_SOURCE[*]}" ]] && [[ "${EL_DEBUG}" -ge 3 ]] ; then
            echo -e "${el_c_r}E: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames}] ${el_c_r}$message${el_c_n}" 1>&2
        else
            echo -e "${el_c_r}E: ${el_c_r}$message${el_c_n}" 1>&2
        fi
    fi

    if [[ -n "${BASH_SOURCE[*]}" ]] ; then
        # set colors for our reports
        local el_c_n el_c_r el_c_r2 el_c_g el_c_g2 el_c_y el_c_y2 el_c_m el_c_m2 el_c_c el_c_c2 el_c_b el_c_b2 el_c_gr el_c_gr2
        el_c_gr="\033[1;30m" # Gray color
        el_c_gr2="\033[0;30m" # Gray2 color
        el_c_r="\033[1;31m" # Red color  (orig: red)
        el_c_r2="\033[0;31m" # Red2 color  (orig: red)
        el_c_g="\033[1;32m" # Green color  (orig: green)
        el_c_g2="\033[0;32m" # Green2 color  (orig. green2)
        el_c_y="\033[1;33m" # Yellow color  (orig. yellow)
        el_c_y2="\033[0;33m" # Yellow2 color  (orig. yellow)
        el_c_b="\033[1;34m" # Blue color
        el_c_b2="\033[0;34m" # Blue2 color
        el_c_m="\033[1;35m" # Magenta color
        el_c_m2="\033[0;35m" # Magenta2 color
        el_c_c="\033[1;36m" # Cyan color
        el_c_c2="\033[0;36m" # Cyan2 color
        el_c_w="\033[1;37m" # White
        el_c_w2="\033[0;37m" # White strong
        el_c_n="\033[0;39m" # Normal color  (orig: normal)

        # always report warnings with debug (function calls)
        el_report_to_elive "${el_c_r}E: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames} ] ${el_c_r}$message${el_c_n}"
    fi

    # }}}
}

#===  FUNCTION  ================================================================
#          NAME:  el_group_check
#   DESCRIPTION:  Checks if the current user is a member of the specified group
#    PARAMETERS:  $1 = group name
#       RETURNS:  0 if member, 1 otherwise
#       EXAMPLE:  if el_group_check "sudo" ; then echo "Admin" ; fi
#===============================================================================
el_group_check(){
    # pre {{{
    local group

    group="${1}"

    el_check_variables "group"

    # }}}

    if [[ " $(id -Gn "$USER") " = *" $group "* ]] ; then
        return 0
    else
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_group_add
#   DESCRIPTION:  Adds the current user to a group and notifies about reboot
#    PARAMETERS:  $1 = group name
#       RETURNS:  0 on success, 1 on failure
#       EXAMPLE:  el_group_add "docker"
#===============================================================================
el_group_add(){
    # pre {{{
    local group is_failed

    group="$1"

    el_check_variables "group"

    # }}}
    if ! el_group_check "${group}" ; then
        if ! el_dependencies_check gksu ; then
            el_dependencies_install "gksu"
        fi
        # multiple gksu attempts
        for i in 1 2
        do
            if gksu adduser $USER $group ; then
                unset is_failed
                break
            else
                is_failed=1
            fi
        done

        if ((is_failed)) && [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
            local message_wrong_password
            message_wrong_password="$( eval_gettext "Wrong password" )"

            timeout 40 $guitool --error # --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$message_wrong_password" )"
        fi


        # try again if not reached
        if ! el_group_check "${group}" ; then
            if ! el_dependencies_check gksu ; then
                el_dependencies_install "gksu"
            fi
            # multiple gksu attempts
            for i in 1 2
            do
                if gksu adduser $USER $group ; then
                    unset is_failed
                    break
                else
                    is_failed=1
                fi
            done

            if ((is_failed)) && [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
                local message_wrong_password
                message_wrong_password="$( printf "$( eval_gettext "Wrong password" )" "" )"

                timeout 40 $guitool --error # --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$message_wrong_password" )"
            fi
        fi

        # inform that a reboot is needed
        if el_group_check "$group" ; then
            el_notify normal logo-elive "Your user is now added to the new group ${group}, reboot your computer for use it correctly"
        fi
    fi


    if ((is_failed)) ; then
        return 1
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_confirm
#   DESCRIPTION:  Prompts user for Yes/No confirmation
#    PARAMETERS:  $1 = question string
#       RETURNS:  0 for Yes, 1 for No
#       EXAMPLE:  if el_confirm "Do you want to proceed?" ; then do_it ; fi
#===============================================================================
el_confirm(){
    # pre {{{
    local reply question flag_glob

    if [[ -n "$BASH_VERSION" ]] ; then
        if shopt extglob | LC_ALL=C grep -q "extglob.*off" ; then
            flag_glob=1
        fi

        shopt -s extglob
    fi
    question="$1"

    el_check_variables "question"


    # }}}
    # return answer {{{
    if ((is_interactive)) ; then
        if ((FORCE_YES)) ; then
            reply="y"
        else
            echo -en "$question [y/n]: \a"
            if [[ -n $ZSH_VERSION ]] ; then
                read reply
            else
                read -e -N1 reply
            fi
        fi

        case $reply in
            y|Y|yes|Yes|YES|s|S)
                return  0
                ;;
            n|N|no|No|NO|nope)
                return  1
                ;;
            *)
                # repeat question until confirmation
                if el_confirm "$@" ; then
                    return 0
                else
                    return 1
                fi
                ;;
        esac
    else
        # Note: we should never open a gui from here, to many tools / scripts are not expecting to be run??
        #el_error "Asking for confirmation without being in interactive mode, failing with a 'no' answer..."
        #return 1
        if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
            if $guitool --question --text="$question" ; then
                return 0
            else
                return 1
            fi
        else
            return 1
        fi
    fi

    if ((flag_glob)) ; then
        shopt -u extglob
    fi

    # }}}

}

#===  FUNCTION  ================================================================
#          NAME:  el_security_function_loop
#   DESCRIPTION:  Detects infinite recursion by inspecting the function call stack
#    PARAMETERS:  $1 = max allowed recursions (default 4)
#       RETURNS:  1 if a loop is detected (to break it), 0 otherwise
#       EXAMPLE:  el_security_function_loop 5 || return 0
#===============================================================================
el_security_function_loop(){
    # pre {{{
    local funclevels funclevel count_recursive recursives_allowed is_loop_infinite is_loop_predetected

    # }}}
    # high value of count_recursive if we call a function recursively multiple times
    if [[ -n "$1" ]] ; then
        recursives_allowed="$1"
    fi

    funclevels="$(( ${#FUNCNAME[@]} - 2 ))"
    funclevel="${funclevels}"

    if [[ -n "$recursives_allowed" ]] ; then
        count_recursive="$(( 1 + $recursives_allowed ))"
    else
        count_recursive="4"
    fi


    # walk around our parent functions
    while [[ $funclevel -gt 0 ]] ; do
        # ignore the last check, which is of course the same name
        if [[ "${count_recursive}" -lt "$funclevels" ]] ; then
            # check if we already come from our function-name in our parents
            if [[ "${FUNCNAME[$funclevel]}" = "${FUNCNAME[1]}" ]] ; then
                if ((is_loop_predetected)) ; then
                    # already entered loop, we should break the loop and continue gracefully
                    is_loop_infinite=1
                else
                    # give at least a chance to the infinite-loop, but we shouldn't
                    is_loop_predetected=1
                fi
            fi
        fi


        funclevel="$(( $funclevel - 1 ))"
        count_recursive="$(( $count_recursive + 1 ))"
    done


    # do action for our resulting checks
    if ((is_loop_predetected)) ; then
        if ((is_loop_infinite)) ; then
            # note: we set it as a warning instead of error because it is really a way that solves errors making the application working, at least for "smart" applications (no linear procedure but a set of tasks and where the application is meant to know what to do)
            if [[ "${EL_DEBUG}" -ge "1" ]] ; then
                echo -e "${el_c_y}W: Entered in an Infinite Loop, let's jump it${el_c_n}" 1>&2
                #echo -e "Note: Declarative type of programming requires this feature for work correctly, in short, if the application works correctly then everything is fine." 1>&2

                # show the array of calls
                funclevels="$(( ${#FUNCNAME[*]} - 2 ))"
                funclevel="${funclevels}"
                echo -en "${el_c_b}calls:${el_c_gr}" 1>&2
                while [[ $funclevel -gt 0 ]] ; do
                    echo -en " -> ${FUNCNAME[$funclevel]}" 1>&2
                    funclevel="$(( $funclevel - 1 ))"
                done
                echo -e "${el_c_n}" 1>&2
            fi

            return 1
        else
            if [[ "${EL_DEBUG}" -ge "2" ]] ; then
                if [[ -z "$recursives_allowed" ]] ; then
                    echo -e "${el_c_y}W: Detected a possible infinite loop${el_c_n}" 1>&2
                fi

                # show the array of calls
                funclevels="$(( ${#FUNCNAME[*]} - 2 ))"
                funclevel="${funclevels}"
                echo -en "${el_c_gr}calls:" 1>&2
                while [[ $funclevel -gt 0 ]] ; do
                    echo -en " -> ${FUNCNAME[$funclevel]}" 1>&2
                    funclevel="$(( $funclevel - 1 ))"
                done
                echo -e "${el_c_n}" 1>&2
            fi
        fi
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_array_member_check
#   DESCRIPTION:  Checks if a specific string exists within an array
#    PARAMETERS:  $1 = string to match, $@ = array elements
#       RETURNS:  0 if found, 1 otherwise
#       EXAMPLE:  if el_array_member_check "item" "${my_array[@]}" ; then echo "Found" ; fi
#===============================================================================
el_array_member_check(){
    local match list i
    match="$1"
    shift
    list=("$@")

    if [[ -n $ZSH_VERSION ]] ; then
        for i in "${(P)list[@]}"
        do
            [[ -z "$i" ]] && continue
            if [[ "${list[i]}" = "$match" ]] ; then
                return 0
            fi
        done
    else
        for i in "${!list[@]}"
        do
            [[ -z "$i" ]] && continue
            if [[ "${list[i]}" = "$match" ]] ; then
                return 0
            fi
        done
    fi

    return 1
}

#===  FUNCTION  ================================================================
#          NAME:  el_array_member_unset
#   DESCRIPTION:  Removes matching elements from an array, outputting to $_out
#    PARAMETERS:  $1 = string to remove, $@ = array elements
#       RETURNS:  Populates global array _out
#       EXAMPLE:  el_array_member_unset "bad_item" "${my_array[@]}" ; my_array=("${_out[@]}")
#===============================================================================
el_array_member_unset(){
    _out=()
    local i item=$1
    shift

    for i in "$@"; do
        [[ -z "$i" ]] && continue
        if [[ $item != "$i" ]]; then
            _out+=("$i")
        fi
    done

    # note: this is a bit hacky but it works until newer versions of bash, the common alternative is: for i in "${!list_requires_update[@]}"; do [[ ${list_requires_update[i]} = "$package" ]] && unset 'list_requires_update[i]'; done
}

#===  FUNCTION  ================================================================
#          NAME:  el_array_member_add
#   DESCRIPTION:  Adds an element to an array if not already present
#    PARAMETERS:  $1 = string to add, $@ = array elements
#       RETURNS:  Populates global array _out
#       EXAMPLE:  el_array_member_add "new_item" "${my_array[@]}" ; my_array=("${_out[@]}")
#===============================================================================
el_array_member_add(){
    _out=()
    local i item=$1 match
    shift

    # see if we have the entry already
    for i in "$@"; do
        if [[ -n "$i" ]] ; then
            if [[ "$item" = "$i" ]]; then
                match=1
            fi
        fi
    done

    # if is repeated, just show the default array, otherwise, append the value
    if ((match)) ; then
        _out=("$@")
    else
        for i in "$@"; do
            if [[ -n "$i" ]] ; then
                _out+=("$i")
            fi
        done

        # add elements to the new array
        _out+=("$item")
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_array_print_per_line
#   DESCRIPTION:  Prints each array element on a new line
#    PARAMETERS:  $@ = array elements
#       RETURNS:  -
#       EXAMPLE:  el_array_print_per_line "${my_array[@]}"
#===============================================================================
el_array_print_per_line(){
    # note: echo "${arr[@]}" doesn't works, so this function
    # has been made in order to avoid using it
    printf "%s\n" "$@"
}

#===  FUNCTION  ================================================================
#          NAME:  el_step_requires_fixme
#   DESCRIPTION:  Breakpoint for dev code; opens a subshell or shows a warning
#    PARAMETERS:  $1 = working directory, $2 = message
#       RETURNS:  -
#       EXAMPLE:  el_step_requires_fixme "/tmp" "Check why this file is missing"
#===============================================================================
el_step_requires_fixme(){
    # pre {{{
    local dir prev_dir message

    dir="$1"
    shift
    if [[ -n "$1" ]] ; then
        message="$1"
        shift
    fi

    prev_dir="$(pwd)"

    : ${dir:="$prev_dir"}

    # }}}
    el_check_dirs "${dir}"
    cd "$dir" || el_warning "dir doesnt exist: $dir"

    if ((is_interactive)) ; then
        echo -e "\n"
        #el_warning "Experimental or uncomplete code:"
        echo -e "We have set a breakpoint here, report to Elive this message if needed:"
        echo -e "${FUNCNAME[0]} for $0"
        echo -e "Function: ${FUNCNAME[1]}  |  Line: $LINENO"
        if [[ -n "$message" ]] ; then
            el_explain 0 "message: XX${message}XX"
            echo -e ""
        fi
        echo -e "$(tput setaf 3)$(tput blink)New SHELL Started$(tput op)"
        el_explain 0 "Type '__exit__' for continue..."
        PROMPT_PREPEND="breakpoint" $SHELL -l || true
    else
        cd "${prev_dir}" || el_warning "prev dir doesnt exist anymore: ${prev_dir}"
        local _dummy
        _dummy="$( eval_gettext "Experimental or incomplete code detected. Report this to the Elive forums if necessary. Exiting now..." )"
        if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
            timeout 50 $guitool --warning --text="$( TEXTDOMAIN="elive-tools" eval_gettext "Experimental or incomplete code detected. Report this to the Elive forums if necessary. Exiting now..." )"
        fi
        el_error "shell is not interactive, cannot run ${FUNCNAME}"
        exit 1
    fi

    cd "${prev_dir}" || el_warning "prev dir doesnt exist anymore: ${prev_dir}"

}

#===  FUNCTION  ================================================================
#          NAME:  el_subshell
#   DESCRIPTION:  Opens an informative subshell for the user
#    PARAMETERS:  $1 = working directory, $2 = message
#       RETURNS:  -
#       EXAMPLE:  el_subshell "/tmp" "You can now inspect the logs in this shell"
#===============================================================================
el_subshell(){
    # pre {{{
    local dir prev_dir message

    dir="$1"
    shift
    if [[ -n "$1" ]] ; then
        message="$1"
        shift
    fi

    prev_dir="$(pwd)"

    : ${dir:="$prev_dir"}

    # }}}
    el_check_dirs "${dir}"
    cd "$dir" || el_warning "dir doesnt exist: $dir"

    if ((is_interactive)) ; then
        echo -e "\n"
        if [[ -n "$message" ]] ; then
            el_info "${message}"
            echo -e ""
        fi
        echo -e "$(tput setaf 3)New SHELL Started$(tput op)"
        el_explain 2 "Type '__exit__' or '__logout__' when you have finished to continue..."
        PROMPT_PREPEND="subshell" $SHELL -l || true
    else
        cd "${prev_dir}" || el_warning "prev dir doesnt exist anymore: ${prev_dir}"
        el_error "shell is not interactive, cannot run ${FUNCNAME}"
        return 1
    fi

    cd "${prev_dir}" || el_warning "prev dir doesnt exist anymore: ${prev_dir}"

}

#===  FUNCTION  ================================================================
#          NAME:  el_report_to_elive
#   DESCRIPTION:  Sends telemetry/error reports to Elive servers via netcat
#    PARAMETERS:  $@ = message string
#       RETURNS:  -
#       EXAMPLE:  el_report_to_elive "Critical failure in module X"
#===============================================================================
el_report_to_elive(){
    # pre {{{
    # NOREPORTS means that we don't want reports in a forced way, so exit
    if [[ -n "$NOREPORTS" ]] || [[ -n "$NOREPORT" ]] || [[ -n "$NO_ELIVETOOLS_REPORTS" ]] ; then
        # only if we dont want to report specifically something (like: main tool don't send reports, which calls a subcommand with reports, so the first one has the variable exported and we don't want it to affect to us)
        if ! ((REPORTS)) && ! ((FORCE_REPORTS)) ; then
            return 0
        fi
    fi
    # ignore all thanatests reports
    if grep -qs "thanatests" /proc/cmdline ; then
        return
    fi

    local message message_stripped return_status id eliveversion execution_stack architecture tmp_conf_f source limit_reports_amount limit_reports_seconds buf messages_extra tool counter fingerprint custom
    el_security_function_loop || return 0
    # max 5 reports per tool
    limit_reports_amount=6
    # min 4 hours passed if limit is reached to send more reports: 14400, 2 days: 172800
    if ((FORCE_REPORTS)) ; then
        limit_reports_seconds=30
    else
        limit_reports_seconds=172800
    fi

    messages_extra=""

    if ((EL_REPORTS)) && ! ((FORCE_REPORTS)) ; then
        # never send reports for systems that are not elive, we don't need them at all (and them don't want that too)
        if ! [[ -s "/etc/elive-version" ]] ; then
            return 0
        fi
        # make sure its not a modified version of Elive, so we don't want to receive reports from other things
        if grep -Fqs "machine-id: " /etc/elive-version && grep -Fqs "fingerprint: " /etc/elive-version ; then
            fingerprint="$( echo "DFF8D7C2+$( grep -F "machine-id: " /etc/elive-version | sed -e 's|^machine-id: ||g' )" | tail -1 | sha1sum | awk '{print $1}'  )"
            if ! grep -Fqs "fingerprint: $fingerprint" /etc/elive-version ; then
                return 0
            fi
        else
            if ! LC_ALL=C grep -Fqs "boot=live" /proc/cmdline && ! LC_ALL=C grep -Fqs "elive-codename: eliveserver" /etc/elive-version ; then
                return 0
            fi
        fi
        # same for specific versions of Elive
        if LC_ALL=C grep -qs "^special-version:" "/etc/elive-version" ; then
            return 0
        fi
    else
        # old code or forced mode:
        if ((REPORTS)) || ((FORCE_REPORTS)) ; then
            # always send reports when REPORTS is set
            # useful for calling subcommands with REPORTS set from a main tool without reports
            true
        else
            # this case means: EL_REPORTS is not set (we dont want reports), and we are not using the old code OR forcing it, so exit because we simply don't want reports...
            # TODO: after 3.8.13 or more, move this REPORTS part to the fingerprint check
            return 0
        fi
    fi

    if [[ -x "$(which nc 2>/dev/null)" ]] ; then
        if [[ -e "/var/lib/dpkg/info/netcat-openbsd.list" ]] ; then
            tool="nc -N"
        else
            tool="nc -q1"
        fi
    else
        return 0
    fi

    if [[ -n "$REPORTS" ]] && [[ -z "$EL_REPORTS" ]] ; then
        messages_extra=" ||| tool '$SOURCE' still uses the REPORTS DEPRECATED CODE, UPDATE IT ${messages_extra}"
    fi

    if ((is_interactive)) ; then
        if ((is_terminal)) ; then
            messages_extra=" ||| TERMINAL MODE:  '$is_terminal' console '${is_console}' ( '$TERM'  ) is_interactive '${is_interactive}', BLACKLIST all terminal and console REPORTS because we only need them from the TOOLS? (temporal warning to verify the correct detection) ${messages_extra}"
        else
        #     # console mode: we need it from deliver
        #     messages_extra=" ||| (not) terminal mode: '$is_terminal' console '${is_console}' is_interactive '${is_interactive}', BLACKLIST all terminal and console REPORTS because we only need them from the TOOLS? (temporal warning to verify the correct detection) ${messages_extra}"
            messages_extra=" ||| console from"
        fi
    #else
        #messages_extra=" ||| is_interactive '${is_interactive}', is_terminal '${is_terminal}', (example of only-tools (no-interactive) report)"
    fi


    # at least one month old, we can parse the results from the server to show only recent ones too
    if ! ((FORCE_REPORTS)) ; then
        if el_check_version_development 1>/dev/null 2>&1 ; then
            # ignore reports for more than X days old
            # 2 weeks after a beta release should be enough to fix any bugs! (1 week to download version + 1 days of download/misc)
            if LC_ALL=C grep -Fqs "boot=live" /proc/cmdline ; then
                if ! el_check_version_development_is_days_recent 12 ; then
                    return 0
                fi
            else
                if ! el_check_version_development_is_days_recent 24 ; then
                    return 0
                fi
            fi
        else
            # stable versions will need more time
            if LC_ALL=C grep -Fqs "boot=live" /proc/cmdline ; then
                if ! el_check_version_is_days_recent 24 ; then
                    return 0
                fi
            else
                if ! el_check_version_is_days_recent 90 ; then
                    return 0
                fi
            fi
        fi
    fi

    # accept a max number of reports to send {{{
    conf_counter_reports_sent=0

    el_config_get
    source="$( readlink -f "$SOURCE" )"
    tmp_conf_f="${HOME}/.config/elive/elive-tools/el_config/${source//\//%%}_conf.sh"

    # at least X time passed to clean reports counter
    if [[ -s "$tmp_conf_f" ]] ; then
        buf="$( echo "$(date +%s) - $( stat -c %Y "$tmp_conf_f" 2>/dev/null )" | LC_ALL="$EL_LC_EN" bc -l | sed -e 's|\..*$||g' )"
        if [[ -n "$buf" ]] && [[ "$buf" -gt "$limit_reports_seconds" ]] ; then
            conf_counter_reports_sent=0
            el_config_save "conf_counter_reports_sent"
        fi

        if [[ "$conf_counter_reports_sent" -ge "$limit_reports_amount" ]] ; then
            return 0
        fi
    fi


    # }}}


    message="$*"
    # remove special chars like colors, don't depend on colors-remove:
    message_stripped="$( echo -e "$@" | sed -r -e "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" -e "s/\x0f//g" )"

    if [[ -z "$message" ]] || [[ -z "$message_stripped" ]] ; then
        return 0
    fi

    if [[ -s "/etc/elive/settings" ]] ; then
        source "/etc/elive/settings" 1>/dev/null 2>&1
    fi
    if [[ -s "$HOME/.config/elive/settings" ]] ; then
        source "$HOME/.config/elive/settings"
    fi

    # not configured? ask for it
    if [[ -z "$conf_send_debug_reports" ]] ; then

        # ask for reports
        if ! LC_ALL=C grep -Fqs "boot=live" /proc/cmdline ; then
            if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
                local message_ask_email
                # note: don't use "'" char in the messages or the functions will fail when using it with ZSH
                message_ask_email="$( printf "$( eval_gettext "Error found in %s. Insert your email if you wish to inform Elive about this, allowing us to fix bugs automatically. We will contact you only if it's an important issue or if we need further details." )" "$(basename $0)" )"
                conf_send_debug_reports_email="$( timeout 240 $guitool --entry --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$message_ask_email" )" )"

                # set conf
                if echo "$conf_send_debug_reports_email" | grep -qs "@.*\." ; then
                    conf_send_debug_reports="yes"
                else
                    conf_send_debug_reports="no"
                fi

                # save conf
                mkdir -p "$HOME/.config/elive"

                sed -i -e "/conf_send_debug_reports=/d" "$HOME/.config/elive/settings" 2>/dev/null
                sed -i -e "/conf_send_debug_reports_email=/d" "$HOME/.config/elive/settings" 2>/dev/null

                echo -e "conf_send_debug_reports=\"$conf_send_debug_reports\"" >> "$HOME/.config/elive/settings"
                echo -e "conf_send_debug_reports_email=\"$conf_send_debug_reports_email\"" >> "$HOME/.config/elive/settings"
            fi
        else
            # don't ask the user for his contact if he's running on live mode
            conf_send_debug_reports="yes"
            conf_send_debug_reports_email="livemode"
        fi
    fi

    # user don't wants to report, so ignore it
    if [[ "$conf_send_debug_reports" = "no" ]] || [[ -z "$conf_send_debug_reports" ]] ; then
        if ! ((FORCE_REPORTS)) ; then
            return 0
        fi
    fi

    # protocol 1.0 values (in order):
    #v_version v_id v_type v_filetype v_topic v_sizeb v_from_name v_from_email v_return_status v_custom v_stack_tree

    # }}}
    # set values{{{
    case "$message_stripped" in
        "E: "*)  return_status=1  ; ;;
        "W: "*)  return_status=2  ; ;;
        "I: "*)  return_status=3  ; ;;
        "D: "*)  return_status=4  ; ;;
    esac

    if grep -qs "MACHINE_VIRTUAL=" "/etc/elive/machine-profile" ; then
        custom="machine_virtual"
    else
        custom="machine_real"
    fi


    if [[ -z "$id" ]] ; then
        id="$( LC_ALL=C grep "^machine-id: " /etc/elive-version | awk '{print $2}' | tail -1 )"
    fi
    # create an unique identifier to differentiate betweem same-reports
    if [[ -z "$id" ]] ; then
        id="$( timeout 4 /sbin/blkid | LC_ALL=C grep -oP 'UUID="\K[^"]+' | sha256sum | awk '{print $1}' )"
    fi
    if [[ -z "$id" ]] ; then
        id="$( ip a | LC_ALL=C grep -E "(link/ether|fe80)" | sha256sum | awk '{print $1}' )"
    fi
    if [[ -z "$id" ]] ; then
        id=nomachineid
    fi
    # alternative (dynamic, it will change)
    # if [[ -z "$id" ]] ; then
    #     id="$( dd if=/dev/urandom bs=1k count=1 2>/dev/null | sha1sum | awk '{print $1}' )"
    # fi

    if [[ -z "$conf_send_debug_reports_email" ]] ; then
        conf_send_debug_reports_email="anonymous:${id}"
    fi


    eliveversion="$( el_elive_version_get 2>/dev/null )"

    # }}}
    # send {{{
    # only report recent things
    if el_verify_internet fast 1>/dev/null 2>&1 ; then
        # get stack to senD
        execution_stack="$( pstree -A -l -n -s $$ )"
        execution_stack="$( echo "$execution_stack" | sed -e 's|^init---||g' -e 's|^.*lightdm---||g' -e 's|---pstree||g' -e 's|---enlightenment---|---|g' -e 's|---ck-launch-sessi||g' -e's|-+-ck-launch-sessi||g' -e 's|---enlightenment_s||g' -e 's|---e_wrapper---e_wrapper|---e_wrapper|g' -e 's|---el_report_to_elive||g' )"
        architecture="$( el_architecture "host" )"

        # unblock
        waitfor "$( basename "$tool" )"

        # send
        if ! { echo -e "ELIVE_DATA_TYPE: 1.0::${id}::transfer::text::reports::0::$(whoami)::${conf_send_debug_reports_email}::${return_status}::${custom}::${architecture}::${eliveversion}::${SOURCE}::" ; echo -e "${message}${messages_extra}" ; echo "     stack: $execution_stack" ; echo -ne "\0" ; } | timeout 4 $tool www.elivecd.org 60008 -w 4 1>/dev/null 2>&1 ; then
            true

            LC_ALL="$EL_LC_EN" sleep 1
            if ! { echo -e "ELIVE_DATA_TYPE: 1.0::${id}::transfer::text::reports::0::$(whoami)::${conf_send_debug_reports_email}::${return_status}::${custom}::${architecture}::${eliveversion}::${SOURCE}::" ; echo -e "${message}${messages_extra}" ; echo "      stack: $execution_stack" ; echo -ne "\0" ; } | timeout 8 $tool www.elivecd.org 60008 -w 8 1>/dev/null 2>&1 ; then
                true

                #LC_ALL="$EL_LC_EN" sleep 1
                #if ! { echo -e "ELIVE_DATA_TYPE: 1.0::${id}::transfer::text::reports::0::::${conf_send_debug_reports_email}::${return_status}::::${architecture}::${eliveversion}::${SOURCE}::" ; echo -e "${message}${messages_extra}" ; } | timeout 8 $tool www.elivecd.org 60008 -w 1 1>/dev/null 2>&1 ; then
                    #true

                    #LC_ALL="$EL_LC_EN" sleep 1
                    #if ! { echo -e "ELIVE_DATA_TYPE: 1.0::${id}::transfer::text::reports::0::::${conf_send_debug_reports_email}::${return_status}::::${architecture}::${eliveversion}::${SOURCE}::" ; echo -e "${message}${messages_extra}" ; } | timeout 8 $tool www.elivecd.org 60008 -w 1 1>/dev/null 2>&1 ; then
                        #true
                    #fi
                #fi
            fi
        fi

        conf_counter_reports_sent="$(( $conf_counter_reports_sent + 1 ))"
        el_config_save "conf_counter_reports_sent"
    fi
    # }}}
}
#===  FUNCTION  ================================================================
#          NAME:  el_mark_state
#   DESCRIPTION:  Sends tracking/analytics events to Elive statistics server
#    PARAMETERS:  $1 = state ID or category, $2 = action (optional)
#       RETURNS:  -
#       EXAMPLE:  el_mark_state "installer" "started"
#===============================================================================
el_mark_state(){
    # pre {{{

    local id state category action
    if [[ -n "$2" ]] ; then
        category="$1"
        action="$2"
    else
        if [[ -n "$1" ]] ; then
            state="$1"
        fi
    fi

    # demo code: this is an example of how to run background processes without annoying debug info, but adding these settings globally will:
    # Important: this breaks el_confirm in zsh
    #if [[ -n "$ZSH_VERSION" ]] && ! [[ "$0" = "bash" ]] && ! [[ "$SOURCE" = "$0" ]] ; then
        #setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR
    #fi

    # }}}
    # only send with user's permission {{{
    # get conf
    if [[ -s "/etc/elive/settings" ]] ; then
        source "/etc/elive/settings" 1>/dev/null 2>&1
    fi
    if [[ -s "$HOME/.config/elive/settings" ]] ; then
        source "$HOME/.config/elive/settings"
    fi

    # exit if user doesn't want to participate
    if [[ "${conf_send_debug_reports}" != "yes" ]] ; then
        return 0
    fi
    # }}}
    # parse ids {{{
    # always run in background to not slow down anything from the tools
    if [[ -n "$category" ]] && [[ -n "$action" ]] ; then
        if [[ -n "$ZSH_VERSION" ]] ; then
            curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org'${SOURCE}'&idsite=2&rec=1&e_c='${category}'&e_a='${action}'"]}' "https://statistics.elivecd.org/piwik.php" 1>/dev/null 2>&1 || true
        else
            { curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org'${SOURCE}'&idsite=2&rec=1&e_c='${category}'&e_a='${action}'"]}' "https://statistics.elivecd.org/piwik.php" & disown ; } 1>/dev/null 2>&1 || true
        fi
    fi

    if [[ -n "$state" ]] ; then
        case "$state" in
            test)
                id="3"
                ;;
            upgraded)
                id="2"
                ;;
            installing)
                id="5"
                ;;
            installed)
                id="6"
                ;;
            noreports)
                # TODO: remove, temporal tests
                id="7"
                ;;
        esac
        if [[ -n "$ZSH_VERSION" ]] ; then
            curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org'${SOURCE}'&idsite=2&rec=1&action_name=track_goal&idgoal='${id}'"]}' "https://statistics.elivecd.org/piwik.php" 1>/dev/null 2>&1 || true
        else
            { curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org'${SOURCE}'&idsite=2&rec=1&action_name=track_goal&idgoal='${id}'"]}' "https://statistics.elivecd.org/piwik.php" & disown ; } 1>/dev/null 2>&1 || true
        fi
    fi

    if [[ -z "$1" ]] ; then
        if [[ -n "$ZSH_VERSION" ]] ; then
            curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org&idsite=2&rec=1"]}' "https://statistics.elivecd.org/piwik.php" 1>/dev/null 2>&1 || true
        else
            { curl -m 10 -L -i -X POST -d '{"requests":["?url=https://www.elivecd.org&idsite=2&rec=1"]}' "https://statistics.elivecd.org/piwik.php" & disown ; } 1>/dev/null 2>&1 || true
        fi
    fi
    # - parse ids }}}
}

#===  FUNCTION  ================================================================
#          NAME:  el_flag
#   DESCRIPTION:  Manages temporary state flags using files in /tmp
#    PARAMETERS:  $1 = action (add|del|check), $2 = flag name
#       RETURNS:  0/1 for check action based on flag existence
#       EXAMPLE:  el_flag add "reboot_needed"
#===============================================================================
el_flag(){
    # pre {{{
    local action name
    el_security_function_loop || return 0

    action="${1}"
    name="${2}"

    #el_debug "$action - $name"

    TMP_FLAGS_d="${TMP_FLAGS_d:-"/tmp/.$(basename $0)-flags-$USER"}"

    # always create dir, flush FS's problems
    mkdir -p "$TMP_FLAGS_d"

    el_check_variables "action|name"

    # }}}
    case $action in
        add|set|put)
            el_explain 4 "added $name flag"
            touch "${TMP_FLAGS_d}/$name"
            ;;
        del|remove|delete|rm)
            rm -f "${TMP_FLAGS_d}/$name"

            if ! el_check_dir_has_files "${TMP_FLAGS_d}" 2>/dev/null ; then
                rmdir "${TMP_FLAGS_d}" 2>/dev/null || true
            fi
            el_debug "removed flag $name"
            ;;
        check|test|verify)
            if [[ -e "${TMP_FLAGS_d}/$name" ]]; then
                el_debug "$action $name -> [flag found]"
                return 0
            else
                el_debug "$action $name -> [no flag]"
                return 1
            fi
            ;;
        *)
            el_error "unknown action ${action}, func: ${FUNCNAME[1]}"
            ;;
    esac
}


#===  FUNCTION  ================================================================
#          NAME:  el_add_on_exit
#   DESCRIPTION:  Registers a command to be executed when the script exits
#    PARAMETERS:  $@ = command string
#       RETURNS:  -
#       EXAMPLE:  el_add_on_exit "rm -f $tmpfile"
#===============================================================================
el_add_on_exit(){
    if [[ -z "$1" ]] ; then
        return 0
    fi
    local n=${#on_exit_items[*]}
    on_exit_items[$n]="$*"

    # add the trap if is not added
    if [[ $n -eq 0 ]]; then
        #echo "Setting trap"
        trap on_exit EXIT
    fi
}
#===  FUNCTION  ================================================================
#          NAME:  on_exit
#   DESCRIPTION:  Trap handler that runs all commands in the on_exit_items array
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  trap on_exit EXIT
#===============================================================================
on_exit(){
    local i
    # create empty variable of array type if not exist
    if [[ -z "$on_exit_items" ]] ; then
        declare -a on_exit_items
    fi

    # run each appended command
    for i in "${on_exit_items[@]}"
    do
        if [[ -n "$i" ]] ; then
            el_debug "on_exit evaluating: $i"
            eval "$i" || true
        fi
    done
}

#===  FUNCTION  ================================================================
#          NAME:  el_request_report_bug
#   DESCRIPTION:  Prompts the user to report a bug to the Elive tracker
#    PARAMETERS:  $@ = message to display
#       RETURNS:  -
#       EXAMPLE:  el_request_report_bug "The application crashed during startup"
#===============================================================================
el_request_report_bug(){
    # pre {{{
    local message url
    el_debug
    el_security_function_loop || return 0

    message="$*"
    el_check_variables "message"

    el_set_display_variables

    # set colors for our reports
    local el_c_n el_c_r el_c_r2 el_c_g el_c_g2 el_c_y el_c_y2 el_c_m el_c_m2 el_c_c el_c_c2 el_c_b el_c_b2
    el_c_gr="\033[1;30m" # Gray color
    el_c_gr2="\033[0;30m" # Gray2 color
    el_c_r="\033[1;31m" # Red color  (orig: red)
    el_c_r2="\033[0;31m" # Red2 color  (orig: red)
    el_c_g="\033[1;32m" # Green color  (orig: green)
    el_c_g2="\033[0;32m" # Green2 color  (orig. green2)
    el_c_y="\033[1;33m" # Yellow color  (orig. yellow)
    el_c_y2="\033[0;33m" # Yellow2 color  (orig. yellow)
    el_c_b="\033[1;34m" # Blue color
    el_c_b2="\033[0;34m" # Blue2 color
    el_c_m="\033[1;35m" # Magenta color
    el_c_m2="\033[0;35m" # Magenta2 color
    el_c_c="\033[1;36m" # Cyan color
    el_c_c2="\033[0;36m" # Cyan2 color
    el_c_w="\033[1;37m" # White
    el_c_w2="\033[0;37m" # White strong
    el_c_n="\033[0;39m" # Normal color  (orig: normal)


    # }}}
    # use el_report_to_elive {{{
    local execution_stack
    execution_stack="$( pstree -A -l -n -s $$ )"
    execution_stack="$( echo "$execution_stack" | sed -e 's|^init---||g' -e 's|^.*lightdm---||g' -e 's|---pstree||g' -e 's|---enlightenment---|---|g' -e 's|---ck-launch-sessi||g' -e's|-+-ck-launch-sessi||g' -e 's|---enlightenment_s||g' -e 's|---e_wrapper---e_wrapper|---e_wrapper|g' -e 's|---el_report_to_elive||g' )"
    el_report_to_elive "${el_c_y2}W: ${el_c_gr}[ ${sourcenames_arr} +${el_c_c2}${BASH_LINENO[0]} ${el_c_gr}${funcnames} (${execution_stack})] ${el_c_r}$message${el_c_n}"

    # - use el_report_to_elive }}}
    # we don't need anything more since the new tool # TODO: remove old code
    return 0

    # task list sorted by created time
    #url='http://bugs.elivecd.org/index.php?do=index&project=2&project=2&do=index&order=dateopened&sort=desc'
    ## check if we should ignore this function
    ##if el_check_version_development_is_days_recent 30 ; then
    ## TODO: set 10 only for now, change it to 30 when migrating from NOREPORTS to EL_REPORTS=yes
    #if el_check_version_development_is_days_recent 15 ; then

        #if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
            #local _dummy
            #_dummy="$( printf "$( eval_gettext "We found something that we want to be informed about its details, it may be a problem in Elive or a simple state that we want to know, since it happened to you it can happen to anybody and we want to make Elive as much stable as possible, please take 1 minute to report this message to our developers:<b>\n\n%s\n\n(file: %s, function: %s, line: %s)</b>\n\nCan you please send us now about your recent problem found ?" )" "$message" "${BASH_SOURCE[1]}" "${FUNCNAME[1]}" "${BASH_LINENO[0]}" )"

            #local message_report_bug
            #message_report_bug="$( printf "$( TEXTDOMAIN="elive-tools" eval_gettext "We found something that we want to be informed about its details, it may be a problem in Elive or a simple state that we want to know, since it happened to you it can happen to anybody and we want to make Elive as much stable as possible, please take 1 minute to report this message to our developers:<b>\n\n%s\n\n(file: %s, function: %s, line: %s)</b>\n\nCan you please send us now about your recent problem found ?" )" "$message" "${BASH_SOURCE[1]}" "${FUNCNAME[1]}" "${BASH_LINENO[0]}" )"

            #if $guitool --question --text="$message_report_bug" ; then
                #if [[ -x /usr/bin/firefox ]] ; then
                    #firefox "$url" &
                #fi
                #if [[ -x /usr/bin/iceweasel ]] ; then
                    #iceweasel "$url" &
                #fi
                ## use web-launcher TODO
                #if [[ -x /usr/bin/chromium ]] ; then
                    #chromium --temp-profile "$url" &
                #fi

                ## explain some details while reporting it
                #local _dummy
                #_dummy="$( eval_gettext "Make sure that the report doesn't already exist first, then click where it says to add a new task. You must be registered for create a task first. Close this dialog when you have finished. Thank you." )"
                #local message_instructions
                #message_instructions="$( TEXTDOMAIN="elive-tools" eval_gettext "Make sure that the report doesn't already exist first, then click where it says to add a new task. You must be registered for create a task first. Close this dialog when you have finished. Thank you." )"
                #sleep 6
                #timeout 240 $guitool --info --text="$message_instructions" || true

            #fi
        #else
            #el_warning "$message_report_bug --> http://bugs.elivecd.org"
        #fi
    #fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_processes_using_mountpoint
#   DESCRIPTION:  Identifies (and optionally kills) processes using a mountpoint
#    PARAMETERS:  $1 = mountpoint or device, $2 = mode ("kill" to terminate)
#       RETURNS:  0 on success, 1 on error
#       EXAMPLE:  el_processes_using_mountpoint "/media/usb" kill
#===============================================================================
el_processes_using_mountpoint(){
    # pre {{{
    local dir mode pid commands pids_mountpoint_arr
    el_debug
    el_security_function_loop || return 0

    dir="$1"
    mode="$2"

    el_check_variables "dir"
    #el_check_dirs "$dir"

    # get mountpoint if is a device
    if [[ -b "$dir" ]] ; then
        if mounts-manager --check-is-mounted "$dir" 2>/dev/null 1>&2 ; then
            dir="$( mounts-manager --get-mountpoint "$dir" 2>/dev/null )"
        fi
    fi

    # check
    if [[ -b "$dir" ]] ; then
        el_error "$dir is not a directory but a block device, try to use mounts-manager --get-mountpoint"
        return 1
    fi

    if ! [[ -d "$dir" ]] ; then
        el_error "$dir must be a directory"
        return 1
    fi

    # }}}

    for pid in $( fuser -m "$dir" 2>/dev/null | tr ' ' '\n' | LC_ALL=C grep -vE "($dir|$$)" )
    do
        pid="${pid//[^[:digit:]]}"
        commands="$( ps axo pid,comm 2>/dev/null | awk -v pid="$pid" '{if ($1 == pid) { $1="" ; print $0 }}' | sed 's| $||g' )"
        if [[ -n "$commands" ]] ; then
            echo "Mountpoint busy by process $pid: $commands"
            el_array_member_add "$pid" "${pids_mountpoint_arr[@]}" ; pids_mountpoint_arr=("${_out[@]}")
        fi
    done

    if [[ "${mode}" = "kill" ]] ; then
        el_debug "Killing processes in reverse mode until the mountpoints is freed"
        # reverse loop of array
        for (( pid=${#pids_mountpoint_arr[@]} ; pid>=0 ; pid-- ))
        do
            if ps "${pids_mountpoint_arr[pid]}" 1>/dev/null 2>&1 ; then
                el_debug "Killing process: \n$( ps "${pids_mountpoint_arr[pid]}" )"
                kill "${pids_mountpoint_arr[pid]}" 2>/dev/null
                kill -9 "${pids_mountpoint_arr[pid]}" 2>/dev/null
                LC_ALL=C sleep 0.2
            fi
        done
    fi

    unset pids_mountpoint_arr
}

#===  FUNCTION  ================================================================
#          NAME:  el_user_email_get
#   DESCRIPTION:  Attempts to find the user's email via config files or git
#    PARAMETERS:  -
#       RETURNS:  Email string or error message
#       EXAMPLE:  email=$(el_user_email_get)
#===============================================================================
el_user_email_get(){
    # pre {{{
    local user_email

    # }}}

    # user has configured it for elive
    user_email="$( grep "^email_acount=" "$HOME/.config/email-sender" 2>/dev/null | sed -e 's|^.*="||g' -e 's|".*$||g' | grep -F "@" | tail -1 )"

    # get the email from his git/github account
    if ! echo "$user_email" | grep -qs "[[:alpha:]]" ; then
        user_email="$( git config --get user.email 2>/dev/null | grep -F "@" | tail -1 )"
    fi

    if echo "$user_email" | grep -qs "[[:alpha:]]" ; then
        echo "$user_email"
    else
        el_error "unable to get user's email"
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_user_desktop_running
#   DESCRIPTION:  Checks if a specific desktop process is running for the user
#    PARAMETERS:  $1 = desktop process name (e.g., e16, enlightenment)
#       RETURNS:  0 if running, 1 otherwise
#       EXAMPLE:  if el_user_desktop_running "e16" ; then echo "E16 is active" ; fi
#===============================================================================
el_user_desktop_running(){
    # pre {{{
    local desktop

    desktop="$1"
    shift

    if [[ -z "$desktop" ]] ; then
        el_error "parameter must be a desktop name, like e16 or enlightenment, ignoring..."
        return 1
    fi

    # }}}

    if ps ux | grep -vw grep | grep -iwqs "$desktop" ; then
        return 0
    else
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_user_desktop_active_get
#   DESCRIPTION:  Identifies the active GUI user or the running desktop environment
#    PARAMETERS:  $1 = mode ("user" or "desktop")
#       RETURNS:  Username or Desktop name (E16, Enlightenment, etc.)
#       EXAMPLE:  current_desktop=$(el_user_desktop_active_get desktop)
#===============================================================================
el_user_desktop_active_get(){
    # pre {{{
    local username mode is_found

    mode="$1"
    shift

    if [[ -z "$mode" ]] ; then
        el_error "mode must be set, using 'user' as default"
        mode="user"
    fi


    # }}}

    case "$mode" in
        user)
            # systemd
            if ! ((is_found)) && ((is_systemd)) ; then
                username="$( LC_ALL="$EL_LC_EN" loginctl user-status | colors-remove | awk 'BEGIN {RS=""; FS="\n"} /State: active/ {print $1}' | sed -E -e 's|\s+\(.*$||g' | head -n 1 )"
                [[ -n "$username" ]] && is_found=1
            fi
            if ! ((is_found)) && [[ -n "$XDG_GREETER_DATA_DIR" ]] ; then
                username="$( echo "$XDG_GREETER_DATA_DIR" | sed -e 's|^.*/data/||g' -e 's|^.*/||g' )"
                [[ -n "$username" ]] && is_found=1
            fi
            if ! ((is_found)) && [[ -n "$ECONFDIR" ]] ; then
                username="$( echo "$ECONFDIR" | sed -e 's|^/home/||g' -e 's|/.*$||g' )"
                [[ -n "$username" ]] && is_found=1
            fi
            if ! ((is_found)) && [[ -n "$E_HOME_DIR" ]] ; then
                username="$( echo "$E_HOME_DIR" | sed -e 's|^/home/||g' -e 's|/.*$||g' )"
                [[ -n "$username" ]] && is_found=1
            fi
            if ! ((is_found)) ; then
                username="$( who | grep -E 'tty[0-9]*.*\(:[[:digit:]]+' | awk '{print $1}' | head -1 )"
                [[ -n "$username" ]] && is_found=1
            fi
            if ! ((is_found)) && [[ -n "$XAUTHORITY" ]] ; then
                username="$( echo "$XAUTHORITY" | sed -e 's|^/home/||g' -e 's|/.*$||g' )"
                [[ -n "$username" ]] && is_found=1
            fi

            if [[ -n "$username" ]] ; then
                echo "$username"
                return 0
            fi

            ;;
        desktop)
            if ! ((is_found)) && [[ -n "$DESKTOP_STARTUP_ID" ]] ; then
                case "$DESKTOP_STARTUP_ID" in
                    "e16/"*)        echo "E16" ; is_found=1 ; ;;
                    "E_START/"*)    echo "E16" ; is_found=1 ; ;;
                esac
            fi
            if ! ((is_found)) && [[ -n "$XDG_SESSION_DESKTOP" ]] ; then
                case "$XDG_SESSION_DESKTOP" in
                    "e16")                  echo "E16" ; is_found=1 ; ;;
                    "enlightenment")        echo "Enlightenment" ; is_found=1 ; ;;
                esac
            fi
            if ! ((is_found)) && [[ -n "$XDG_CURRENT_DESKTOP" ]] ; then
                case "$XDG_CURRENT_DESKTOP" in
                    "E16")                  echo "E16" ; is_found=1 ; ;;
                    "Enlightenment")        echo "Enlightenment" ; is_found=1 ; ;;
                esac
            fi
            if ! ((is_found)) && [[ -n "$DESKTOP" ]] ; then
                case "$DESKTOP" in
                    "E16")                  echo "E16" ; is_found=1 ; ;;
                    "Enlightenment")        echo "Enlightenment" ; is_found=1 ; ;;
                esac
            fi

            # check
            #if [[ "$UID" -eq 0 ]] ; then
                #el_error "you must be a normal user to get the desktop user"
                #return 1
            #fi

            # return the desktop
            if ! ((is_found)) && [[ -n "$EROOT" ]] ; then
                echo "E16"
                is_found=1
            fi
            if ! ((is_found)) && [[ "$E_HOME_DIR" = *"/.e/e" ]] ; then
                echo "Enlightenment"
                is_found=1
            fi
            if ! ((is_found)) &&  [[ -n "$E_START" ]] ; then
                echo "E17"
                is_found=1
            fi


            if ! ((is_found)) ; then
                # try to detect running active desktop
                if [[ "$USER" = "$( el_user_desktop_active_get user )" ]] ; then
                    if ps ux | grep -vw grep | grep -iwqs e16 ; then
                        echo "E16"
                        is_found=1
                    else
                        if ps ux | grep -vw grep | grep -iwqs enlightenment ; then
                            echo "Enlightenment"
                            is_found=1
                        fi
                    fi
                fi
            fi

            if ! ((is_found)) ; then
                # try to detect running active desktop
                if [[ "$USER" = "$( el_user_desktop_active_get user )" ]] ; then
                    if pidof -q e16 ; then
                        echo "E16"
                        is_found=1
                    else
                        if pidof -q enlightenment_start ; then
                            echo "Enlightenment"
                            is_found=1
                        fi
                    fi
                fi
            fi

            ;;
    esac
}

#===  FUNCTION  ================================================================
#          NAME:  el_request_donation
#   DESCRIPTION:  Displays a GUI prompt asking the user to donate to Elive
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_request_donation
#===============================================================================
el_request_donation(){
    # pre {{{
    local arg

    if ! [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
        return 0
    fi

    [[ -s /etc/elive/settings ]] && source /etc/elive/settings
    if ((is_premium_user)) ; then
        return 0
    fi


    # }}}

    local _dummy
    _dummy="$( eval_gettext "Elive enjoys making the life of its users easier with unique features and tools. Remember, Elive is a non-commercial project that only survives through donations." )"
    local _dummy2
    _dummy2="$( eval_gettext "Do you want to donate to Elive so that we can keep it alive and continue improving it?" )"

    local _dummy3
    _dummy3="$( eval_gettext "Making a donation only takes a minute or two and ensures there will be more greatly improved versions of Elive in the future." )"

    # show it
    #$guitool --info --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$_dummy" )" || true
    if $guitool --question --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$_dummy $_dummy2" )" ; then
        /etc/alternatives/x-www-browser "https://www.elivecd.org/donate" &
    else
        if $guitool --question --text="$( TEXTDOMAIN="elive-tools" eval_gettext "$_dummy $_dummy3" )" ; then
            /etc/alternatives/x-www-browser "https://www.elivecd.org/donate" &
        fi
    fi


}

#===  FUNCTION  ================================================================
#          NAME:  el_config_get
#   DESCRIPTION:  Loads persistent configuration variables for the current tool
#    PARAMETERS:  - (Uses global $SOURCE to identify the config file)
#       RETURNS:  Sources the config file into the current shell
#       EXAMPLE:  el_config_get
#===============================================================================
el_config_get(){
    # pre {{{
    local tmp_conf_f source
    #tmp_conf_f="/tmp/.$(basename $0)_${USER}_configs.sh"
    source="$( readlink -f "$SOURCE" 2>/dev/null || true )"
    if [[ ! -e "$source" ]] ; then
        el_debug "ignoring config because of a broken source '$source' location from '$(pwd)' dir"
        return
    fi
    tmp_conf_f="${HOME}/.config/elive/elive-tools/el_config/${source//\//%%}_conf.sh"

    # }}}
    # checks {{{
    if el_check_is_shell ; then
        el_debug "ignoring el_config_* because we are from a shell"
        return 0
    fi
    # variable must be set
    if [[ -z "${SOURCE}" ]] ; then
        el_error "\$0 (source tool) should be set to access to its conf ID"
        return 1
    fi

    # }}}
    if [[ -s "$tmp_conf_f" ]] ; then
        # Warning: never use "local" variables in your function for the ones that you are going to get
        source "$tmp_conf_f" 1>/dev/null 2>&1
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_config_save
#   DESCRIPTION:  Saves specified variables to a persistent config file
#    PARAMETERS:  $@ = variable names (must start with "conf_")
#       RETURNS:  0 on success, 1 on error
#       EXAMPLE:  el_config_save "conf_last_run" "conf_user_choice"
#===============================================================================
el_config_save(){
    # pre {{{
    local entry
    # note: by some reason this needs to be alone and first or may not work:

    # checks {{{
    if el_check_is_shell ; then
        el_warning "ignoring el_config_* because we are from a shell"
        return 1
    fi
    # variable must be set
    if [[ -z "${SOURCE}" ]] ; then
        NOREPORTS=1 el_error "\$0 (source tool) should be set to access to its conf ID"
        return 1
    fi

    # }}}

    local tmp_conf_f source
    source="$( readlink -f "$SOURCE" 2>/dev/null || true )"
    if [[ ! -e "$source" ]] ; then
        el_debug "ignoring config because of a broken source '$source' location from '$(pwd)' dir"
        return
    fi
    tmp_conf_f="${HOME}/.config/elive/elive-tools/el_config/${source//\//%%}_conf.sh"

    if [[ -z "$*" ]] ; then
        el_error "${FUNCNAME[0]} called without parameter, (name of variable to save). by ${FUNCNAME[1]}"
        return 1
    fi

    if ! declare -p "$@" 1>/dev/null 2>&1 ; then
        el_error "${FUNCNAME[0]} called with a wrong parameter (empty varilable?), use only the name of variable to save. by ${FUNCNAME[1]}"
        return 1
    fi

    # create conf file
    if ! [[ -s "$tmp_conf_f" ]] ; then
        if ! mkdir -p "$( dirname "$tmp_conf_f" )" ; then
            el_error "unable to create confs dir: $( mkdir -p \"$( dirname "$tmp_conf_f" )\" 2>&1 ), $( ls -la "$HOME" | grep "\.config" | tr '\n' ';'  ), $( ls -la "${HOME}/.config/elive/elive-tools/" | tr '\n' ';'  )"
            return 1
        fi

        touch "$tmp_conf_f"
    fi

    # }}}
    # remove old possible entries
    for entry in "$@"
    do
        if ! [[ "$entry" = "conf_"* ]] ; then
            el_warning "variables to save should be prepended as conf_* and you have '$entry', this will ensure that you know that is a conf variable and to NEVER set them as a local variable in your code, otherwise will not work since bash 5.0"
        fi

        # bash entries
        sed -i "/^${entry}=/d" "$tmp_conf_f"
        sed -i "/^declare.* ${entry}=/d" "$tmp_conf_f"
        # zsh entries
        sed -i "/^typeset.* ${entry}=/d" "$tmp_conf_f"
        sed -i "/^typeset.* ${entry}$/d" "$tmp_conf_f"
        #el_debug "deleted old conf: $entry"
    done

    if [[ "$( declare -p "$@" | wc -l )" -gt 1 ]] ; then
        el_warning "saving variables with more than one line give problems, dont use it in your code or improve the function: $*"
    fi

    # append configuration, and save the variable as global so it can work outside this function
    # Warning: never use "local" variables in your function for the ones that you are going to save
    declare -p "$@" | sed -e 's|declare\s--|declare -|g' -e 's|declare -|declare -g|g' | awk '{$2 = $2" --"; print $0}' >> "$tmp_conf_f"
    return 0
}

#===  FUNCTION  ================================================================
#          NAME:  el_config_restart
#   DESCRIPTION:  Deletes the persistent configuration file for the current tool
#    PARAMETERS:  - (Uses global $SOURCE)
#       RETURNS:  -
#       EXAMPLE:  el_config_restart
#===============================================================================
el_config_restart(){
    # pre {{{
    local tmp_conf_f source

    source="$( readlink -f "$SOURCE" 2>/dev/null || true )"
    if [[ ! -e "$source" ]] ; then
        el_debug "ignoring config because of a broken source '$source' location from '$(pwd)' dir"
        return
    fi
    tmp_conf_f="${HOME}/.config/elive/elive-tools/el_config/${source//\//%%}_conf.sh"

    # }}}
    # checks {{{
    if el_check_is_shell ; then
        el_debug "ignoring el_config_* because we are from a shell"
        return 0
    fi
    # variable must be set
    if [[ -z "${SOURCE}" ]] ; then
        el_error "\$0 is not set"
        return 0
    fi

    # }}}
    if [[ -s "$tmp_conf_f" ]] ; then
        rm -f "$tmp_conf_f"
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_ram_usage_show
#   DESCRIPTION:  Displays RAM statistics in MB or percentage
#    PARAMETERS:  $1 = mode (free|total|used|used_percent)
#       RETURNS:  Numeric value or full 'free' output
#       EXAMPLE:  percent=$(el_ram_usage_show used_percent)
#===============================================================================
el_ram_usage_show(){
    # pre {{{
    local arg used total

    arg="$1"

    # }}}

    # all reports are in MB
      case "$arg" in
          free)
              free -m | LC_ALL=C grep "^Mem: " | awk '{print $7}'
              ;;
          total)
              free -m | LC_ALL=C grep "^Mem: " | awk '{print $2}'
              ;;
          used)
              free -m | LC_ALL=C grep "^Mem: " | awk '{print $3}'
              ;;
          used_percent)
              used="$( free -m | LC_ALL=C grep "^Mem: " | awk '{print $3}' )"
              total="$( free -m | LC_ALL=C grep "^Mem: " | awk '{print $2}' )"
              echo "( $used / $total ) * 100" | bc -l | sed -e 's|\..*$||g'
              ;;
          *)
              free
              ;;
      esac

}

#===  FUNCTION  ================================================================
#          NAME:  el_architecture
#   DESCRIPTION:  Returns the system or binary architecture
#    PARAMETERS:  $1 = "host" or "kernel", $2 = "normal" or "gnu", $3 = file path
#       RETURNS:  Architecture string (e.g., amd64, i386-linux-gnu)
#       EXAMPLE:  arch=$(el_architecture host gnu)
#===============================================================================
el_architecture(){
    # pre {{{
    local arg arch file args
    args="$@"

    if [[ -n "$1" ]] ; then
        arg="$1"
        shift
    fi

    if [[ -n "$1" ]] ; then
        format="$1"
        shift
    else
        format="normal"
    fi

    if [[ -n "$1" ]] ; then
        file="$1"
        shift
    fi


    # defaults
    if [[ ! -e "$file" ]] ; then
        file="/bin/ls"
    fi

    # }}}
    # fetch data
    case "$arg" in
        kernel)
            arch="$( uname -m )"
            ;;
        host)
            arch="$( file -Lb "$file" | sed -e 's|, version .*$||g' -e 's|^.*, ||g' )"
            ;;
        *)
            el_error "argument missing, select one: host, kernel [normal|gnu]"
            return 1
            ;;
    esac

    # convert
    case "$arch" in
        "x86-64"|x86_64|amd64|64bit)
            case "$format" in
                gnu)   arch="x86_64-linux-gnu"  ; ;;
                *)     arch="amd64"             ; ;;
            esac
            ;;
        *80386|i386|i486|i586|i686)
            case "$format" in
                gnu)   arch="i386-linux-gnu"  ; ;;
                *)     arch="i386"             ; ;;
            esac
            ;;
    esac

    # result
    if [[ -n "$arch" ]] ; then
        echo "$arch"
    else
        el_error "unable to know (args '$args' ) the architecture '${arch}' for this machine, uname -m '$(uname -m)', file -Lb /bin/ls '$( file -Lb /bin/ls 2>&1 )'"
        return 1
    fi

}

#===  FUNCTION  ================================================================
#          NAME:  el_show_variables
#   DESCRIPTION:  Show all the elive-tools variables with comments
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_show_variables
#===============================================================================
el_show_variables(){
   echo -e "\n${el_c_y}Elive-tools Variables:${el_c_n}"
   # Extract variables that have a comment on the same line, excluding commented lines
   grep -E "^[[:space:]]*[^#]*[[:alnum:]_]+=[^#]* # [[:alnum:]]+" /usr/lib/elive-tools/functions | \
       while read -r line; do
           # Extract variable name: find the word immediately before the first '='
           var_name=$(echo "$line" | sed -E 's/^.*(^|[[:space:]|&|;])(export[[:space:]]+)?([[:alnum:]_]+)=.*/\3/')

           # Extract comment (part after #)
           comment=$(echo "$line" | sed -E 's/^.*#[[:space:]]*//')

           if [[ -n "$var_name" ]]; then
               echo -ne "${el_c_b2}${var_name}${el_c_n}"
               echo -e "    - ${comment}"
           fi
   done | sort -u
   echo
}

#===  FUNCTION  ================================================================
#          NAME:  el_show_funcs
#   DESCRIPTION:  Show all the elive-tools functions with short descriptions
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_show_funcs
#===============================================================================
el_show_funcs(){
   local func_name desc
   echo -e "\n${el_c_y}Elive-tools Functions:${el_c_n}"

   # Parse the file for the specific header format
   awk '
   /^#          NAME:/ { name=$3 }
   /^#   DESCRIPTION:/ {
       # Capture everything after DESCRIPTION:
       match($0, /DESCRIPTION:[[:space:]]*/)
       desc=substr($0, RSTART + RLENGTH)
   }
   /^[a-zA-Z0-9_]+\(\)\{/ {
       # Extract function name from line like "el_confirm(){"
       split($0, a, "(")
       fname = a[1]

       if (name == fname) {
           printf "%s|%s\n", fname, desc
       } else {
           printf "%s|\n", fname
       }
       name=""; desc=""
   }
   ' /usr/lib/elive-tools/functions | while IFS="|" read -r name description; do
       echo -ne "${el_c_b2}${name}${el_c_n}"
       if [[ -n "$description" ]]; then
           echo -ne "    - ${description}"
       fi
       echo
   done
   echo
}

#===  FUNCTION  ================================================================
#          NAME:  el_show_functions
#   DESCRIPTION:  Show all the elive-tools functions with full headers
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_show_functions
#===============================================================================
el_show_functions(){
   echo -e "\nElive-tools Functions"
   echo -e "\nYour script must start like:
#!/bin/bash
SOURCE=\"$0\"
source /usr/lib/elive-tools/functions
# optional:
EL_REPORTS=\"1\"
el_make_environment
"
   echo -e "\nHeader defintions:"

   # Use awk to print from the header start to the line before the function definition
   awk '
   /^#===  FUNCTION/ {
       in_header = 1
   }
   /^[a-zA-Z0-9_]+\(\)\{/ {
       if (in_header) {
           print ""
       }
       in_header = 0
   }
   in_header {
       print $0
   }
   ' /usr/lib/elive-tools/functions
   echo
}

#===  FUNCTION  ================================================================
#          NAME:  el_get_machine_id
#   DESCRIPTION:  Generates a unique hardware ID using DMI, UUID, or CPU info
#    PARAMETERS:  $1 = "-f" or "--force" (optional) to bypass cached ID
#       RETURNS:  Unique ID string
#       EXAMPLE:  id=$(el_get_machine_id)
#===============================================================================
el_get_machine_id(){
    # pre {{{
    local machine_id buf packed_info force

    if [[ "$1" = "-f" ]] || [[ "$1" = "--force" ]] ; then
        force=1
        shift
    fi

    # Helper function to check if a serial/UUID string is generic/invalid
    _is_valid_id(){
        local val="$1"
        [[ -z "$val" ]] && return 1

        # Strip leading/trailing spaces and dots
        val="$( echo "$val" | sed -e 's/^[.[:space:]]*//' -e 's/[.[:space:]]*$//' )"
        [[ -z "$val" ]] && return 1

        # Require at least 8 alphanumeric characters
        local clean_alnum
        clean_alnum="$( echo "$val" | tr -cd '[:alnum:]' )"
        [[ ${#clean_alnum} -lt 8 ]] && return 1

        # Filter out common generic placeholder/OEM strings
        if echo "$val" | LC_ALL=C grep -qiE "(to be filled|o\.?e\.?m|default|not specified|not available|not present|none|invalid|unknown|^serial$|^board serial|^system serial|type2|standard|ubuntu|Calistoga|Candy|Bobba|boardserial|Aspire|imedia|peppy|proliant|veriton|xxxx|0000000|123456|^part num|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)" ; then
            return 1
        fi

        return 0
    }

    # Use the cached / saved ID from /etc/elive-version unless force is requested
    if ! ((force)) && grep -qsF "machine-id: " "/etc/elive-version" 2>/dev/null ; then
        machine_id="$( LC_ALL=C grep "^machine-id: " /etc/elive-version | awk '{print $2}' | tail -1 )"
        if [[ -n "$machine_id" ]] ; then
            echo "$machine_id"
            return 0
        fi
    fi

    if ! [[ "$UID" = 0 ]] ; then
        el_error "${FUNCNAME[0]} must be run with privileges"
        return 1
    fi

    # }}}

    # 1) Get system UUID
    if [[ -z "$machine_id" ]] ; then
        buf="$( dmidecode -s system-uuid 2>/dev/null || true )"
        [[ -z "$buf" ]] && buf="$( cat /sys/class/dmi/id/product_uuid 2>/dev/null || true )"
        read -r buf <<< "$buf"
        if _is_valid_id "$buf" ; then
            if [[ "$buf" != *"00000000-0000-0000-0000-000000000000" ]] && [[ "$buf" != *"ffffffff-ffff-ffff-ffff-ffffffffffff"* ]] ; then
                buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
                machine_id="sysuid${buf}"
            fi
        fi
    fi

    # 2) Get board serial number
    if [[ -z "$machine_id" ]] ; then
        buf="$( cat /sys/class/dmi/id/board_serial 2>/dev/null || true )"
        [[ -z "$buf" ]] && buf="$( dmidecode -s baseboard-serial-number 2>/dev/null || true )"
        read -r buf <<< "$buf"
        if _is_valid_id "$buf" ; then
            buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
            machine_id="boards${buf}"
        fi
    fi

    # 3) Get system serial number
    if [[ -z "$machine_id" ]] ; then
        buf="$( dmidecode -s system-serial-number 2>/dev/null || true )"
        [[ -z "$buf" ]] && buf="$( cat /sys/class/dmi/id/product_serial 2>/dev/null || true )"
        read -r buf <<< "$buf"
        if _is_valid_id "$buf" ; then
            buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
            machine_id="sysser${buf}"
        fi
    fi

    # 4) Get OS machine-id
    if [[ -z "$machine_id" ]] ; then
        buf="$( cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null || true )"
        read -r buf <<< "$buf"
        if _is_valid_id "$buf" && [[ "$buf" =~ ^[0-9a-fA-F]{32}$ ]] ; then
            buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
            machine_id="machid${buf}"
        fi
    fi

    # 5) Pack a combination of hardware/system identifiers if specific serials/UUIDs aren't valid
    if [[ -z "$machine_id" ]] ; then
        packed_info=""
        packed_info+="$( dmidecode -s bios-vendor 2>/dev/null )"
        packed_info+="$( dmidecode -s bios-version 2>/dev/null )"
        packed_info+="$( dmidecode -s system-manufacturer 2>/dev/null )"
        packed_info+="$( dmidecode -s system-product-name 2>/dev/null )"
        packed_info+="$( dmidecode -s baseboard-product-name 2>/dev/null )"
        packed_info+="$( cat /proc/cpuinfo 2>/dev/null | LC_ALL=C grep -E "(model name|model|cpu family)" | sort -u | head -2 )"
        packed_info+="$( cat /sys/class/net/*/address 2>/dev/null | LC_ALL=C grep -v "00:00:00:00:00:00" | sort -u | head -1 )"
        packed_info+="$( /sbin/blkid 2>/dev/null | LC_ALL=C grep -oP 'UUID="\K[^"]+' | sort | head -1 )"

        buf="$( echo "$packed_info" | sha1sum 2>/dev/null | awk '{print $1}' )"
        if [[ -n "$buf" ]] && [[ ${#buf} -ge 32 ]] ; then
            buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
            machine_id="packed${buf}"
        fi
    fi

    # 6) Re-use a backup if present in /tmp/.machine-id
    if [[ -z "$machine_id" ]] ; then
        if [[ -e "/tmp/.machine-id" ]] ; then
            buf="$( cat /tmp/.machine-id 2>/dev/null | tail -1 || true )"
            read -r buf <<< "$buf"
            if _is_valid_id "$buf" ; then
                buf="$( echo "$buf" | sha1sum 2>/dev/null | awk '{print $1}' )"
                machine_id="recycl$buf"
            fi
        fi
    fi

    # 7) Fallback: generate a random sha1 number with prefix sha1generated
    if [[ -z "$machine_id" ]] ; then
        buf="$( dd if=/dev/urandom bs=1k count=1 2>/dev/null | sha1sum | awk '{print $1}' )"
        machine_id="sha1gn${buf}"
    fi

    # Show results
    if [[ -n "$machine_id" ]] ; then
        echo "$machine_id"
    else
        el_error "no machine id found"
    fi
}
#===  FUNCTION  ================================================================
#          NAME:  el_json_append_file
#   DESCRIPTION:  Merges two JSON files using 'jq'
#    PARAMETERS:  $1 = target file, $2 = file containing JSON to append
#       RETURNS:  0 on success, 1 on error
#       EXAMPLE:  el_json_append_file "config.json" "extra.json"
#===============================================================================
el_json_append_file(){
    # pre {{{
    local file extra extrafile
    el_debug
    el_security_function_loop || return

    file="$1"
    extrafile="$2"

    el_check_variables "file|extrafile"

    if [[ ! -s "$file" ]] ; then
        el_error "file given is empty: $file"
        return 1
    fi
    if [[ ! -s "$extrafile" ]] ; then
        el_error "file extra given is empty: $extrafile"
        return 1
    fi

    if ! el_dependencies_check jq ; then
        el_dependencies_install "jq"
    fi

    # }}}
    # fixes
    extra="$( cat "$extrafile" )"
    if ! cat "$file" | jq -S ". |= . + ${extra}" 1>/dev/null 2>&1 ; then
        extra="$( echo "{ $extra }" )"
    fi

    # append extra file to original one
    if cat "$file" | jq -S ". |= . + ${extra}" 1>/dev/null 2>&1 ; then
        cat "$file" | jq -S ". |= . + ${extra}" > "${file}.new" && mv -f "${file}.new" "$file"
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_json_delete_ids_regex
#   DESCRIPTION:  Deletes JSON keys matching a regex pattern using 'jq'
#    PARAMETERS:  $1 = target file, $2 = regex pattern
#       RETURNS:  0 on success, 1 on error
#       EXAMPLE:  el_json_delete_ids_regex "data.json" "^temp_.*"
#===============================================================================
el_json_delete_ids_regex(){
    # pre {{{
    local file match
    el_debug
    el_security_function_loop || return

    file="$1"
    match="$2"

    el_check_variables "file|match"

    if [[ ! -s "$file" ]] ; then
        el_error "file given is empty: $file"
        return 1
    fi
    if [[ -z "$match" ]] ; then
        el_error "nothing to match '$match' for given file '$file'"
        return 1
    fi

    if ! el_dependencies_check jq ; then
        el_dependencies_install "jq"
    fi

    # }}}
    # delete matches
    #cat "$file" | jq "delpaths([paths | select(.[-1] | strings | startswith(\"${match}\"))])" > "${file}.new" && mv -f "${file}.new" "$file"
    cat "$file" | jq "delpaths([paths | select(.[-1] | strings | test(\"${match}\"; \"ix\"))])" > "${file}.new" && mv -f "${file}.new" "$file"
}

#===  FUNCTION  ================================================================
#          NAME:  el_apt_package_version_get
#   DESCRIPTION:  Retrieves package version from dpkg status or apt cache
#    PARAMETERS:  $1 = package, $2 = "installed"|"candidate", $3 = "full"|"clean"
#       RETURNS:  Version string
#       EXAMPLE:  ver=$(el_apt_package_version_get "bash" installed clean)
#===============================================================================
el_apt_package_version_get(){
    local package state mode version
    package="$1"
    shift
    state="$1"
    shift
    mode="$1"
    shift

    case "$state" in
        "installed")
            version="$( awk -v FS="\n" -v RS="" -v package="$package" '{ if ( ($1 == "Package: "package ) && ($2 == "Status: install ok installed" || $2 == "Status: hold ok installed") ) print $0}' /var/lib/dpkg/status | LC_ALL=C grep "^Version: " | sed -e 's|^Version: ||g' )"
            if [[ -z "$version" ]] ; then
                version="$( LC_ALL="$EL_LC_EN" apt-cache policy "$package" | LC_ALL=C grep -F "Installed:" | sed -e 's|^.*Installed: ||g' | LC_ALL=C grep -E "^[[:digit:]]*" | head -1 )"
            fi
            ;;

        "candidate")
            version="$( LC_ALL="$EL_LC_EN" apt-cache policy "$package" | LC_ALL=C grep -F "Candidate:" | sed -e 's|^.*Candidate: ||g' | LC_ALL=C grep -E "^[[:digit:]]*" | head -1 )"
            ;;
    esac

    read -r version <<< "$version"

    # check
    if [[ -z "$version" ]] ; then
        return 1
    fi

    case "$mode" in
        clean)
            # clean version values
            version="${version#*:}"
            version="${version%-*}"
            ;;
        full)
            version="${version}"
            ;;
    esac

    echo "$version"
}


#===  FUNCTION  ================================================================
#          NAME:  el_aptget
#   DESCRIPTION:  Runs apt-get while waiting for other package locks to release
#    PARAMETERS:  $@ = apt-get arguments
#       RETURNS:  Exit status of apt-get
#       EXAMPLE:  el_aptget install -y vim
#===============================================================================
el_aptget(){
    local i j is_shown
    i=0

    tput sc
    # while fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock  >/dev/null 2>&1 ; do
    while fuser /var/lib/apt/lists/lock  >/dev/null 2>&1 ; do
        case $(($i % 4)) in
            0 ) j="-" ;;
            1 ) j="\\" ;;
            2 ) j="|" ;;
            3 ) j="/" ;;
        esac
        tput rc
        if ((is_interactive)) ; then
            echo -en "\r\033[K[$j] Waiting for other software managers to finish..." 1>&2
        else
            if [[ -n "$DISPLAY" ]] && ! ((is_shown)) ; then
                el_notify normal "packages-gdebi" "Package Tasks Running" "Waiting for other software managers to finish before to continue..."
                is_shown=1
            fi
        fi

        sleep 0.5
        ((i=i+1))
    done

    # run what we want
    if [[ "$UID" = 0 ]] ; then
        TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  apt-get "$@"
    else
        if el_check_sudo_automated ; then
            TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  sudo -H -n apt-get "$@"
        else
            if [[ "${DISPLAY}" = ":0.0" || "${DISPLAY}" = ":0" ]] ; then
                if ! el_dependencies_check gksu ; then
                    el_dependencies_install "gksu"
                fi
                TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  gksu apt-get $*
            else
                #su -c "TERM=linux DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true  apt-get $@" root
                urxvt -e bash -c "echo -e 'Installing Dependencies' ; su -c 'TERM=screen-256color DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical DEBCONF_NONINTERACTIVE_SEEN=true DEBCONF_NOWARNINGS=true apt-get $*' root"
            fi
        fi
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  el_prettify_to_html
#   DESCRIPTION:  prettify a message or diff to html format
#    PARAMETERS:  $1 = message
#       RETURNS:  html string
#===============================================================================
el_prettify_to_html(){
    local message="$1"
    local output

    if [[ "$message" =~ "<span" ]] || [[ "$message" =~ "<h3>" ]] ; then
        # do not process something that has already html tags
        output="$message"
    else
        output="$( printf "%s" "$message" | sed 's|\\n|<br>|g' |
            awk '{ if ($1 ~ /^\s*\-/) print "<span style=\"color:#a74000;font-family:monospace;\">"$0"</span>" ;
                    else if ($0 ~ /@@.*@@/) print "<span style=\"color:#e0a300;font-family:monospace;\">"$0"</span>" ;
                    else if ($1 ~ /^\s*\+/) print "<span style=\"color:#00a742;font-family:monospace;\">"$0"</span>" ;
                    else if ($1 ~ /^\s*\+\+\+/) print "<span style=\"color:blue;font-family:monospace;\">"$0"</span>" ;
                    else if ($1 ~ /^\s*\-\-\-/) print "<span style=\"color:#614700;font-family:monospace;\">"$0"</span>" ;
                    else if ($1 ~ /^E:/) print "<span style=\"color:red;\">"$0"</span>" ;
                    else if ($1 ~ /^W:/) print "<span style=\"color:#e0a300;\">"$0"</span>" ;
                    else {
                        if ($0 ~ /[Ww]arning|[Ee]rror/) print "<b>"$0"</b>" ;
                        else print $0
                    } }' )"
    fi

    echo "$output" | sed -e 's|\\n|<br>|g' | tr '\n' '@' | sed -e 's|@$||g' -e 's|@|<br>|g'
}





###################################################################
# DEPRECATED FUNCTIONS TO FALLBACK:
###################################################################
#===  FUNCTION  ================================================================
#          NAME:  el_notify_user
#   DESCRIPTION:  Deprecated function fallback to el_notify
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_notify_user "Title" "Message"
#===============================================================================
el_notify_user(){
    el_warning "Deprecated function ${FUNCNAME[0]} by elive-tools functions from $SOURCE $0 - (fallback to el_notify)"
    el_notify "normal" "logo-elive" "$1" "$2"
}
#===  FUNCTION  ================================================================
#          NAME:  el_notify_user_important
#   DESCRIPTION:  Deprecated function fallback to el_notify
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_notify_user_important "Alert" "Critical error"
#===============================================================================
el_notify_user_important(){
    el_warning "Deprecated function ${FUNCNAME[0]} by elive-tools functions from $SOURCE $0 - (fallback to el_notify)"
    el_notify "critical" "logo-elive" "$1" "$2"
}
#===  FUNCTION  ================================================================
#          NAME:  el_check_translations_required_notify
#   DESCRIPTION:  deprecated function, use el_check_translations_required instead
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_check_translations_required_notify
#===============================================================================
el_check_translations_required_notify(){
    el_error "deprecated function ${FUNCNAME[0]} from elive-tools functions from $SOURCE $0, use el_check_translations_required instead"
}
#===  FUNCTION  ================================================================
#          NAME:  el_check_dependencies
#   DESCRIPTION:  compatibility wrapper for el_dependencies_check
#    PARAMETERS:  -
#       RETURNS:  -
#       EXAMPLE:  el_check_dependencies "curl|wget"
#===============================================================================
el_check_dependencies(){
    # compatibility
    el_warning "${FUNCNAME[0]} is moved to 'el_dependencies_check', update or report this code (full compatibility still working, don't worry), from: ${FUNCNAME[1]}"
    el_dependencies_check "$@"
}
#===  FUNCTION  ================================================================
#          NAME:  el_debian_version_get
#   DESCRIPTION:  sets the variables is_bookworm and others to know the version of debian
#    PARAMETERS:  -
#       RETURNS:  is_distro' variables
#===============================================================================
el_debian_version_get(){
    case "$( cat /etc/debian_version )" in
        15.*|"duke"*)
            is_duke=1
            ;;
        14.*|"forky"*)
            is_forky=1
            ;;
        13.*|"trixie"*)
            is_trixie=1
            ;;
        12.*|"bookworm"*)
            is_bookworm=1
            ;;
        11.*|"bullseye"*)
            is_bullseye=1
            is_old_rdiff=1
            ;;
        10.*|"buster"*)
            is_buster=1
            is_old_rdiff=1
            ;;
        9.*|"stretch"*)
            is_stretch=1
            is_old_rdiff=1
            ;;
        8.*|"jessie"*)
            is_jessie=1
            is_old_rdiff=1
            ;;
        7.*|"wheezy"*)
            is_wheezy=1
            is_old_rdiff=1
            ;;
        *"sid"*)
            is_sid=1
            ;;
        *"experimental"*)
            is_experimental=1
            ;;
        *)
            el_error "Unknown Debian version: $( cat /etc/debian_version )"
            ;;
    esac
}


###################################################################
#  Always run:
###################################################################

# gettext functions
if [[ -x "/usr/bin/gettext.sh" ]] ; then
    . gettext.sh
else
    # make it compatible
    eval_gettext(){
        echo "$@"
    }
fi

if [[ -n "$ZSH_VERSION" ]] ; then
    # Note: seems like if you put a ? just after a char, cannot be sourced correctly from zsh
    setopt nonomatch
    alias shopt=':'
    alias _expand=_bash_expand
    alias _complete=_bash_comp
    #emulate -L sh
    #setopt kshglob noshglob braceexpand
    # always split words or this would make things incompatible, like for i in ${PATH//:/ } (in other words, variables split by spaces instead of sentences, just like in bash
    setopt shwordsplit
fi

# set defaults for guis
[[ -z "$zenity" ]] && [[ -x "/usr/bin/zenity" ]] && zenity="/usr/bin/zenity --window-icon=/usr/share/icons/gnome/256x256/apps/logo-elive.png"
[[ -z "$yad" ]] && [[ -x "/usr/bin/yad" ]] && yad="/usr/bin/yad --borders=15 --center --image=logo-elive"
# set a default guitool compatible with the scripts
guitool="$zenity"  # default GUI tool (zenity, yad, etc)

# always know the debian version
el_debian_version_get


# ----------------------------------------------------------------------
# First, detect the real TTY device (if any) – this works even without TERM
# ----------------------------------------------------------------------
_is_tty=0
_tty_dev=""
if [ -t 0 ]; then
    _is_tty=1
    # Try `tty` command, fallback to /proc
    _tty_dev=$(tty 2>/dev/null || readlink -f /proc/$$/fd/0 2>/dev/null)
fi

# ----------------------------------------------------------------------
# Now your original code, but with corrected initial values
# ----------------------------------------------------------------------

# do we are in a terminal ? avoid interactive-in-terminal if not
if [[ -n "$TERM" ]] ; then
    # this can be from a cronjob
    if [[ "$TERM" = "dumb" ]] ; then
        # know which desktop we are running, if we are
        if [[ -z "$E_START" ]] && [[ -z "$EROOT" ]] ; then
            if pgrep -U "$UID" e16 1>/dev/null ; then
                if [[ -d "/usr/share/e16" ]] ; then
                    export EROOT=/usr/share/e16
                else
                    el_warning "${FUNCNAME[0]} - ${FUNCNAME[1]}: seems like our user is running e16 as desktop but there is not /usr/share/e16 directory"
                fi
            else
                if pgrep -U "$UID" -f enlightenment_start 1>/dev/null ; then
                    if [[ -e "/usr/bin/enlightenment_start" ]] ; then
                        export E_START="/usr/bin/enlightenment_start"
                    else
                        el_warning "${FUNCNAME[0]} - ${FUNCNAME[1]}: seems like our user is running e16 as desktop but there is not /usr/bin/enlightenment_start executable"
                    fi
                fi
            fi
        fi
    fi

    # note: TTY variable not shows in bash login aparently
    if [[ "$TERM" = "linux" ]] ; then
        # note: if you want to enable colors in an application run from E (.desktop file), you must set the TERM variable, and LS_COLORS too
        if [[ -n "$E_START" ]] || [[ -n "$EROOT" ]] ; then
            # launched from e17 itself
            is_terminal=0
            is_console=0
        else
            # note: in the past we had is_terminal=1 forced in order to get features from other things, this is not needed anymore since the use of is_interactive
            is_terminal=0 # we are in a terminal
            is_console=1 # we are in a non-X console
        fi
    fi

    # note: do not use [[ "$TERM_PROGRAM" = "tmux" ]] because we may run things from non-interactive using tmux-attach-jobs
    if [[ "$TERM" = *rxvt* ]] || [[ "$TERM" = *xterm* ]] || [[ "$TERM" = "terminology" ]] || [[ "$TERM" = *gnome* ]] || [[ "$TERM" = "screen-256color"* ]] || [[ "$TERM" = *tmux* ]] ; then
        is_terminal=1
        is_console=0
    fi

    if [[ "$TERM" = "screen-256color"* ]] || [[ "$TERM" = *"tmux"* ]] ; then
        is_terminal_remote=1 # we are in a remote session
    fi

    # cronjobs must never be interactives
    if [[ "$TERM" = "dumb" ]] ; then
        is_terminal=0
    fi
else
    # TERM is unset – but we can still detect if we are on a console using the TTY device
    if [[ $_is_tty -eq 1 ]] ; then
        case "$_tty_dev" in
            /dev/tty[1-9]*)
                is_terminal=0
                is_console=1
                ;;
            /dev/pts/*)
                is_terminal=1
                is_console=0
                ;;
            *)
                is_terminal=0
                is_console=0
                ;;
        esac
    else
        is_terminal=0
        is_console=0
    fi
    # if we don't have TERM we need this value to not trigger 'tput' errors
    export TERM="linux"
fi
unset _is_tty _tty_dev


if ((is_terminal)) || ((is_console)) ; then
    is_interactive=1  # flag: we are in interactive mode (user is reachable)
    is_print_fancy=1  # flag: to know when we want fancy printing (colored)
fi

if [[ -n "$FORCE_INTERACTIVE" ]] ; then
    case "$FORCE_INTERACTIVE" in
        "yes"|1)
            is_interactive=1
            is_print_fancy=1
            ;;
        "no"|0)       unset is_interactive  ; ;;
    esac
elif ((EL_NON_INTERACTIVE)) ; then
    unset is_interactive
fi
#if ! ((is_interactive)) ; then
    ## when we are in non-interactive, we should also disable access to display, otherwise some things may popup expecting answer, like in el_confirm
    #update: do not enable this, otherwise tools like elive-assistant will fail (xdo typing, so elive-assistant is not run interactively)
    #unset DISPLAY
#fi


# set a default useful debug PS
export PS4='+ [${FUNCNAME[0]:+${FUNCNAME[0]}} ${LINENO}]  '  # Prompt for debug mode, showing function name and line number

# TODO: LS_COLORS is not a reliable way to know that we are on a terminal, we should rely on is_terminal then
if [[ -n "$NOCOLOR" ]] || ! ((is_print_fancy)) || ! ((is_terminal)) ; then
    el_c_n=""
    el_c_r=""
    el_c_r2=""
    el_c_g=""
    el_c_g2=""
    el_c_y=""
    el_c_y2=""
    el_c_m=""
    el_c_m2=""
    el_c_c=""
    el_c_c2=""
    el_c_b=""
    el_c_b2=""
    el_c_gr=""
    el_c_gr2=""
    el_c_w=""
    el_c_w2=""

    el_c_blink=""
    el_c_underline=""
    el_c_italic=""
    el_c_bold=""
else
    el_c_gr="\033[1;30m" # Gray color
    el_c_gr2="\033[0;30m" # Gray2 color
    el_c_r="\033[1;31m" # Red color  (orig: red)
    el_c_r2="\033[0;31m" # Red2 color  (orig: red)
    el_c_g="\033[1;32m" # Green color  (orig: green)
    el_c_g2="\033[0;32m" # Green2 color  (orig. green2)
    el_c_y="\033[1;33m" # Yellow color  (orig. yellow)
    el_c_y2="\033[0;33m" # Yellow2 color  (orig. yellow)
    el_c_b="\033[1;34m" # Blue color
    el_c_b2="\033[0;34m" # Blue2 color
    el_c_m="\033[1;35m" # Magenta color
    el_c_m2="\033[0;35m" # Magenta2 color
    el_c_c="\033[1;36m" # Cyan color
    el_c_c2="\033[0;36m" # Cyan2 color
    el_c_w="\033[1;37m" # White
    el_c_w2="\033[0;37m" # White strong
    el_c_n="\033[0;39m" # Normal color  (orig: normal)

    if ((is_console)) ; then
        ## gray's are not visible in console when vga=normal (t460s doesnt configure the good one so it fallbacks to normal), use cyan's instead
        el_c_gr="\033[1;36m" # Gray color
        #el_c_gr2="\033[0;36m" # Gray2 color
    fi
    #else
        #el_c_gr="\033[1;30m" # Gray color
        #el_c_gr2="\033[0;30m" # Gray2 color
    #fi

    el_c_blink="\033[5m" # Blink 'color' effect  (orig. blink)
    el_c_underline="\033[4m" # Underline 'color' effect  (orig. underline)
    el_c_italic="\033[3m" # Italic 'color' effect
    el_c_bold="\033[1m" # Bold 'color' effect
fi

# Elive debug verbosity:
# - 0 : only show errors
# - 1 : + warnings
# - 2 : + info messages (user always should want to see information messages, so this should be the default one)
# - 3 : + debug
# - 4 : + very debug
# - 5 : + very very debug
#if grep -Fqs thanatests /proc/cmdline ; then
    #EL_DEBUG=${EL_DEBUG:-"3"}
#else
    EL_DEBUG=${EL_DEBUG:-"2"} # Elive debug verbosity level (0-5)
#fi


# extra vars
if [[ -z "$DSHELL" && -z "$DHOME" && -z "$EXTRA_GROUPS" ]] && [[ -s "/etc/adduser.conf" ]] ; then
    source /etc/adduser.conf 2>/dev/null || true
fi
[[ -z "$DHOME" ]] && export DHOME="/home"  # default OS home directory
[[ -z "$UID" ]] && export UID="$(id -u)"
# set the user's home, sometimes, like if we are in an init script, the home points to /
if [[ -z "$HOME" ]] || [[ "$HOME" = "/" ]] ; then
    if [[ "$UID" = "0" ]] || [[ "$USER" = "root" ]] ; then
        HOME="/root"
        export HOME
    else
        HOME="$DHOME/$(id -un)"
        export HOME
    fi
fi

if [[ -z "$USER" ]] ; then
    USER="$(id -un)"
    export USER
fi


# use LC_ALL=C like this instead:  LC_ALL="$EL_LC_EN":
if LC_ALL=C grep -qs "^en_US.UTF-8\s*" "/etc/locale.gen" ; then
    EL_LC_EN="en_US.UTF-8" # fallback charset to show messages supporting utf8 if detected
else
    # warning: this can broke things by not being utf8
    EL_LC_EN="C"
fi


# systemd?
if [[ -d /run/systemd/system ]] && pidof -cq systemd ; then
    is_systemd=1 # flag: OS runs on systemd
elif [[ -x /sbin/openrc ]] && [[ -d /run/openrc ]]; then
    is_openrc=1 # flag: OS runs on openrc
elif [[ -f /run/runit.stopit ]] && pidof -q runsvdir ; then
    is_runit=1 # flag: OS runs on runit
else
    is_sysvinit=1 # flag: OS runs on sysvinit
fi

# Verify that only one init system is detected
_init_count="$(( ${is_systemd:-0} + ${is_openrc:-0} + ${is_runit:-0} + ${is_sysvinit:-0} ))"
if [[ $_init_count -gt 1 ]]; then
    el_warning "Multiple init systems detected: systemd=${is_systemd}, openrc=${is_openrc}, runit=${is_runit}, sysvinit=${is_sysvinit}"
fi
unset _init_count





# FIXME: implement:
#
# - check same proceses
# - el_fixme_required -> report to the user that the feature is not implemented by some future userfriendly way
# - trap signals (exit statuses, etc)
#EXAMPLES (of correct managing of traps):
    #set -E  # inherit errors to parent, good for deep traping, suggested
    #trap "error_signal_debug" ERR  # the ERR traps any error, so "ls idontexist" would send this signal too, which is not suggested to have in the exit_error function, but at least good for a debug purpose and know that something failed somewhere
    #trap "exit_error" 1 2 3 6 9 11 13 14 15
    #trap "exit_ok" 0 EXIT
    #el_add_on_exit exit_ok  # or you can use this feature, so it calls commands when something is finished, good to add "rm thisfile" everywhere arounds the code
    #error_signal_debug(){
        #_trapped="$?"
        #if [[ -n "$_trapped" ]] ; then
            #el_warning "trapped signal: '${_trapped}' from '${FUNCNAME[1]}' in lines '${BASH_LINENO[*]}'"
        #fi
    #}
    #exit_error(){
        #_trapped="$?"
        #cleanups
        #exit $_trapped
    #}
    #exit_ok(){
        #echo "good bye"
    #}
# - report, something for inform the user about something important, which interfaces and cases ?


# vim: set foldmethod=marker filetype=sh :

