Gemeldet mit Bildschirmfoto: statt Rahmen erscheinen q, x, l und m.
Ursache nachgemessen - ohne UTF-8-Locale zeichnen dialog und whiptail
die Rahmen ueber die alte VT100-Umschaltung ESC(0, die Terminals im
UTF-8-Modus (PuTTY in der Vorgabe) ignorieren:
LC_ALL=C ESC(0-Umschaltungen: 1 rohe q: 91
LC_CTYPE=C.UTF-8 ESC(0-Umschaltungen: 0 rohe q: 0
ui_init setzt jetzt beim Start der Oberflaeche LC_CTYPE auf die erste
verfuegbare UTF-8-Locale. Bewusst nur LC_CTYPE, damit Sortierung und
Zahlenformat unberuehrt bleiben. Dabei war ein zweiter Anlauf noetig:
ein gesetztes LC_ALL sticht jede einzelne Kategorie aus, der erste
Versuch blieb deshalb wirkungslos. Steht LC_ALL auf einem
Nicht-UTF-8-Wert, wird es entfernt.
Gemessen ueber unsere eigene ui_menu unter LC_ALL=C: vorher 91 rohe q,
danach 0.
Gibt es gar keine UTF-8-Locale, faellt die Oberflaeche auf einfache
Rahmen zurueck (dialog --ascii-lines, sofern unterstuetzt); erzwingen
laesst sich das mit CMK_TUI_ASCII=1. Der Selbsttest hat dafuer einen
eigenen Abschnitt, der die Locale vorher und nachher zeigt und im
Zweifel das fehlende Sprachpaket nennt.
Dokumentiert als Abschnitt 14.4 in beiden Sprachfassungen, samt der
vier PuTTY-Einstellungen fuer die Gegenseite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
474 lines
18 KiB
Bash
474 lines
18 KiB
Bash
#!/usr/bin/env bash
|
|
# =====================================================================
|
|
# common.sh - gemeinsame Funktionen fuer cmk-manage.sh
|
|
# Wird per "source" eingebunden, nicht direkt ausgefuehrt.
|
|
# =====================================================================
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Ausgabe / Logging
|
|
# ---------------------------------------------------------------------
|
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
|
|
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
|
|
else
|
|
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
|
|
fi
|
|
|
|
log() { printf '%s[ %s ]%s %s\n' "$C_BLU" "INFO" "$C_RST" "$*"; }
|
|
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
|
|
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
|
|
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
debug() { [ -n "${VERBOSE:-}" ] && printf ' > %s\n' "$*" >&2 || true; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# .env laden
|
|
# ---------------------------------------------------------------------
|
|
load_env() {
|
|
local envfile="${1:-$PROJECT_DIR/.env}"
|
|
if [ -f "$envfile" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
. "$envfile"
|
|
set +a
|
|
ENV_FILE="$envfile"
|
|
else
|
|
warn "Keine .env gefunden ($envfile) - es gelten die Standardwerte."
|
|
ENV_FILE=""
|
|
fi
|
|
# Rueckfallwerte, falls ein Schluessel in der .env fehlt.
|
|
# Die .env bleibt die massgebliche Konfiguration - hier steht nur,
|
|
# was gilt, wenn dort nichts eingetragen ist.
|
|
CMK_CONTAINER_NAME="${CMK_CONTAINER_NAME:-checkmk}"
|
|
NPM_CONTAINER_NAME="${NPM_CONTAINER_NAME:-nginx-proxy-manager}"
|
|
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
|
|
CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}"
|
|
NPM_ENABLED="${NPM_ENABLED:-on}"
|
|
BACKUP_AUTO="${BACKUP_AUTO:-off}"
|
|
PROXY_FORWARD_HOST="${PROXY_FORWARD_HOST:-127.0.0.1}"
|
|
PROXY_FORWARD_PORT="${PROXY_FORWARD_PORT:-5000}"
|
|
NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}"
|
|
# Leer = automatisch aus dem Admin-Port ableiten
|
|
NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}"
|
|
BACKUP_DIR="$(abs_path "${BACKUP_DIR:-$PROJECT_DIR/backups}")"
|
|
IMAGE_DIR="$(abs_path "${IMAGE_DIR:-$PROJECT_DIR/images}")"
|
|
}
|
|
|
|
# relativen Pfad (./foo) am Projektverzeichnis ausrichten
|
|
abs_path() {
|
|
case "$1" in
|
|
/*) printf '%s\n' "$1" ;;
|
|
*) printf '%s\n' "$PROJECT_DIR/${1#./}" ;;
|
|
esac
|
|
}
|
|
|
|
# .env-Wert dauerhaft setzen
|
|
# env_set KEY WERT [allow_empty]
|
|
env_set() {
|
|
local key="$1" val="$2" allow_empty="${3:-}" f="${ENV_FILE:-$PROJECT_DIR/.env}"
|
|
[ -f "$f" ] || die "Keine .env vorhanden - kann '$key' nicht setzen."
|
|
# mehrzeilige Werte wuerden die .env zerstoeren
|
|
case "$val" in
|
|
*$'\n'*) die "Ungueltiger Wert fuer '$key' (mehrzeilig)." ;;
|
|
esac
|
|
[ -n "$val" ] || [ -n "$allow_empty" ] || die "Leerer Wert fuer '$key' - abgebrochen."
|
|
# Werte mit Leerzeichen in Anfuehrungszeichen setzen
|
|
case "$val" in
|
|
*" "*) case "$val" in \"*\") : ;; *) val="\"$val\"" ;; esac ;;
|
|
esac
|
|
if grep -qE "^[#[:space:]]*${key}=" "$f"; then
|
|
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${val}|" "$f"
|
|
else
|
|
printf '%s=%s\n' "$key" "$val" >>"$f"
|
|
fi
|
|
export "$key=$val"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Container-Runtime erkennen (docker / podman)
|
|
# ---------------------------------------------------------------------
|
|
detect_runtime() {
|
|
if [ -n "${CMK_RUNTIME:-}" ] && command -v "$CMK_RUNTIME" >/dev/null 2>&1; then
|
|
RUNTIME="$CMK_RUNTIME"
|
|
elif [ -n "${CMK_RUNTIME:-}" ]; then
|
|
# Falsch gesetzter Wert darf das Werkzeug nicht unbenutzbar machen
|
|
warn "CMK_RUNTIME='$CMK_RUNTIME' ist nicht installiert - suche selbst.
|
|
Korrigieren mit: cmk-manage.sh config set CMK_RUNTIME auto"
|
|
CMK_RUNTIME=""
|
|
detect_runtime
|
|
return
|
|
elif command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
|
RUNTIME=docker
|
|
elif command -v podman >/dev/null 2>&1; then
|
|
RUNTIME=podman
|
|
elif command -v docker >/dev/null 2>&1; then
|
|
RUNTIME=docker # vorhanden, aber Daemon evtl. gestoppt
|
|
else
|
|
die "Weder docker noch podman gefunden. Bitte zuerst ./install.sh ausfuehren."
|
|
fi
|
|
debug "Runtime: $RUNTIME"
|
|
}
|
|
|
|
# Compose-Kommando ermitteln (als Array COMPOSE_CMD)
|
|
detect_compose() {
|
|
COMPOSE_CMD=()
|
|
if [ "$RUNTIME" = docker ]; then
|
|
if docker compose version >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(docker compose)
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(docker-compose)
|
|
fi
|
|
else
|
|
if command -v podman-compose >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(podman-compose)
|
|
elif podman compose version >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(podman compose)
|
|
fi
|
|
fi
|
|
[ ${#COMPOSE_CMD[@]} -gt 0 ] || die "Kein Compose-Kommando gefunden (docker compose / podman-compose)."
|
|
debug "Compose: ${COMPOSE_CMD[*]}"
|
|
}
|
|
|
|
compose() {
|
|
detect_compose
|
|
( cd "$PROJECT_DIR" && "${COMPOSE_CMD[@]}" "$@" )
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Container-Helfer
|
|
# ---------------------------------------------------------------------
|
|
# Ist der NGINX Proxy Manager eingeschaltet? (on/true/1/yes = ja)
|
|
npm_enabled() {
|
|
case "$(printf '%s' "${NPM_ENABLED:-on}" | tr 'A-Z' 'a-z')" in
|
|
on|true|1|yes|ja|y) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
container_exists() { $RUNTIME container inspect "$1" >/dev/null 2>&1; }
|
|
container_running() { [ "$($RUNTIME container inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]; }
|
|
|
|
require_cmk_running() {
|
|
container_exists "$CMK_CONTAINER_NAME" \
|
|
|| die "Container '$CMK_CONTAINER_NAME' existiert nicht. Zuerst: $0 up"
|
|
container_running "$CMK_CONTAINER_NAME" \
|
|
|| die "Container '$CMK_CONTAINER_NAME' laeuft nicht. Zuerst: $0 up"
|
|
}
|
|
|
|
# Kommando im Checkmk-Container als root ausfuehren
|
|
ct() { $RUNTIME exec "$CMK_CONTAINER_NAME" "$@"; }
|
|
# ... mit stdin
|
|
cti() { $RUNTIME exec -i "$CMK_CONTAINER_NAME" "$@"; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# OMD-Helfer (im Container)
|
|
# ---------------------------------------------------------------------
|
|
cmk_sites() {
|
|
ct sh -c 'ls -1 /omd/sites 2>/dev/null' | sed '/^$/d' || true
|
|
}
|
|
|
|
cmk_site_exists() {
|
|
cmk_sites | grep -qxF "$1"
|
|
}
|
|
|
|
cmk_site_version() {
|
|
ct omd version -b "$1" 2>/dev/null | tr -d '\r' || true
|
|
}
|
|
|
|
# 0 = laeuft komplett, 1 = teilweise, 2 = gestoppt
|
|
cmk_site_state() {
|
|
local rc=0
|
|
ct omd status --bare "$1" >/dev/null 2>&1 || rc=$?
|
|
echo "$rc"
|
|
}
|
|
|
|
cmk_site_state_text() {
|
|
case "$(cmk_site_state "$1")" in
|
|
0) echo "laeuft" ;;
|
|
1) echo "teilweise" ;;
|
|
*) echo "gestoppt" ;;
|
|
esac
|
|
}
|
|
|
|
cmk_versions_installed() {
|
|
ct omd versions -b 2>/dev/null | tr -d '\r' || ct sh -c 'ls -1 /omd/versions | grep -v "^default$"' 2>/dev/null || true
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Diverses
|
|
# ---------------------------------------------------------------------
|
|
human_size() {
|
|
local b="${1:-0}"
|
|
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
|
|
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
|
|
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
|
|
else echo "${b} B"; fi
|
|
}
|
|
|
|
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
|
|
|
|
confirm() {
|
|
[ -n "${ASSUME_YES:-}" ] && return 0
|
|
local prompt="${1:-Fortfahren?} [j/N] " a
|
|
read -r -p "$prompt" a </dev/tty || return 1
|
|
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
|
|
}
|
|
|
|
primary_ip() {
|
|
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|
|
|| hostname -I 2>/dev/null | awk '{print $1}'
|
|
}
|
|
|
|
# =====================================================================
|
|
# TUI-Helfer (dialog bevorzugt, sonst whiptail) - ncurses wie im mc
|
|
# =====================================================================
|
|
UI_BACKTITLE="Checkmk Stack Manager - Container-Runtime: ${RUNTIME:-?}"
|
|
|
|
# Groesse des Terminals ermitteln (mit Rueckfallwerten)
|
|
ui_term_size() {
|
|
UI_ROWS="$(tput lines 2>/dev/null || true)"
|
|
UI_COLS="$(tput cols 2>/dev/null || true)"
|
|
case "$UI_ROWS" in ''|*[!0-9]*) UI_ROWS=24 ;; esac
|
|
case "$UI_COLS" in ''|*[!0-9]*) UI_COLS=80 ;; esac
|
|
if [ "$UI_ROWS" -lt 10 ]; then UI_ROWS=24; fi
|
|
if [ "$UI_COLS" -lt 40 ]; then UI_COLS=80; fi
|
|
return 0
|
|
}
|
|
|
|
# Fenstergroesse aus Textlaenge, Anzahl der Eintraege und Terminalgroesse
|
|
# berechnen. Vorher waren die Werte fest - bei langem Text blieb dann nur
|
|
# eine Zeile fuer die Liste uebrig.
|
|
#
|
|
# ACHTUNG: hier bewusst mit if-Bloecken statt "[ ... ] && VAR=...".
|
|
# Letzteres liefert 1, wenn die Bedingung nicht zutrifft, und beendet
|
|
# unter "set -e" die Funktion bzw. das ganze Skript.
|
|
#
|
|
# ui_calc TEXT FELDER_JE_EINTRAG EINTRAEGE...
|
|
ui_calc() {
|
|
local text="$1" per="$2"; shift 2
|
|
ui_term_size
|
|
|
|
local tl maxw
|
|
tl="$(printf '%s\n' "$text" | wc -l)"
|
|
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
|
|
|
|
# breiteste Zeile der Liste (Kennung und Beschriftung stehen nebeneinander)
|
|
local -a args=("$@")
|
|
local i=0 w itemw=0 n=0
|
|
while [ "$i" -lt "${#args[@]}" ]; do
|
|
w=$(( ${#args[$i]} + 4 ))
|
|
if [ $((i+1)) -lt "${#args[@]}" ]; then
|
|
w=$(( w + ${#args[$((i+1))]} ))
|
|
fi
|
|
if [ "$w" -gt "$itemw" ]; then itemw="$w"; fi
|
|
i=$(( i + per )); n=$(( n + 1 ))
|
|
done
|
|
|
|
# Breite: der breitere von Text und Liste, plus Rahmen
|
|
if [ "$maxw" -gt "$itemw" ]; then UI_W=$(( maxw + 10 )); else UI_W=$(( itemw + 10 )); fi
|
|
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
|
|
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
|
|
|
|
# Listenhoehe: moeglichst alle Eintraege zeigen, ohne aus dem Bild zu laufen
|
|
local avail=$(( UI_ROWS - tl - 9 ))
|
|
if [ "$avail" -lt 3 ]; then avail=3; fi
|
|
if [ "$n" -lt "$avail" ]; then UI_MH="$n"; else UI_MH="$avail"; fi
|
|
if [ "$UI_MH" -lt 1 ]; then UI_MH=1; fi
|
|
|
|
UI_H=$(( tl + UI_MH + 8 ))
|
|
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
|
|
if [ "$UI_H" -lt 10 ]; then UI_H=10; fi
|
|
return 0
|
|
}
|
|
|
|
# Hoehe fuer Fenster ohne Liste (Meldung, Abfrage, Eingabe)
|
|
ui_calc_text() { # text zusatzzeilen
|
|
local text="$1" extra="${2:-7}"
|
|
ui_term_size
|
|
local tl maxw
|
|
tl="$(printf '%s\n' "$text" | wc -l)"
|
|
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
|
|
UI_W=$(( maxw + 10 ))
|
|
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
|
|
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
|
|
UI_H=$(( tl + extra ))
|
|
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
|
|
if [ "$UI_H" -lt 8 ]; then UI_H=8; fi
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Rahmenzeichen in PuTTY & Co.
|
|
#
|
|
# Ohne UTF-8-Locale zeichnen dialog/whiptail die Rahmen ueber die alte
|
|
# VT100-Umschaltung "ESC(0". Terminals, die im UTF-8-Modus laufen -
|
|
# PuTTY in der Vorgabe -, ignorieren diese Umschaltung und zeigen
|
|
# stattdessen die rohen Buchstaben q, x, l, m.
|
|
#
|
|
# Nachgemessen: bei LC_ALL=C schickt whiptail eine ESC(0-Umschaltung und
|
|
# 91 rohe "q"; mit UTF-8-Locale keine einzige. Es genuegt, LC_CTYPE zu
|
|
# setzen - Sortierung und Zahlenformat bleiben damit unberuehrt.
|
|
ui_fix_locale() {
|
|
[ -n "${UI_LOCALE_DONE:-}" ] && return 0
|
|
UI_LOCALE_DONE=1
|
|
UI_LOCALE_NOTE=""
|
|
case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
|
|
*UTF-8*|*utf-8*|*UTF8*|*utf8*) return 0 ;;
|
|
esac
|
|
local l
|
|
for l in C.UTF-8 C.utf8 en_US.UTF-8 en_US.utf8 de_DE.UTF-8 de_DE.utf8; do
|
|
if locale -a 2>/dev/null | grep -qix "$l"; then
|
|
# LC_ALL sticht jede einzelne Kategorie aus - solange es auf
|
|
# einem Nicht-UTF-8-Wert steht, bliebe LC_CTYPE wirkungslos.
|
|
if [ -n "${LC_ALL:-}" ]; then unset LC_ALL; fi
|
|
export LC_CTYPE="$l"
|
|
UI_LOCALE_NOTE="LC_CTYPE auf $l gesetzt (war nicht UTF-8)"
|
|
return 0
|
|
fi
|
|
done
|
|
# Keine UTF-8-Locale vorhanden - dann bleibt nur ASCII
|
|
UI_ASCII=1
|
|
UI_LOCALE_NOTE="keine UTF-8-Locale gefunden - einfache Rahmen"
|
|
return 1
|
|
}
|
|
|
|
# Zusaetzliche Argumente, wenn nur ASCII-Rahmen moeglich sind
|
|
ui_ascii_args() {
|
|
UI_EXTRA=()
|
|
case "${CMK_TUI_ASCII:-}${UI_ASCII:-}" in
|
|
'') return 0 ;;
|
|
esac
|
|
# Nur dialog kann das; whiptail kennt die Option nicht.
|
|
if [ "${UI_BIN:-}" = dialog ] && dialog --ascii-lines --print-version >/dev/null 2>&1; then
|
|
UI_EXTRA=(--ascii-lines)
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
ui_init() {
|
|
ui_fix_locale || true
|
|
ui_ascii_args
|
|
# Geometrie IMMER setzen - sonst laeuft das Skript unter "set -u" auf
|
|
# eine unbound variable, wenn UI_BIN von aussen vorgegeben wurde.
|
|
UI_H="${UI_H:-22}"; UI_W="${UI_W:-78}"; UI_MH="${UI_MH:-13}"
|
|
[ -n "${UI_BIN:-}" ] && return 0
|
|
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
|
|
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
|
|
else UI_BIN=""; fi
|
|
export DIALOGRC="${DIALOGRC:-}"
|
|
}
|
|
|
|
ui_available() { ui_init; [ -n "$UI_BIN" ]; }
|
|
|
|
ui_require() {
|
|
ui_available || die "Fuer die TUI wird 'dialog' oder 'whiptail' benoetigt.
|
|
Debian/Ubuntu : apt-get install -y dialog
|
|
RHEL/Rocky : dnf install -y dialog"
|
|
UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME"
|
|
}
|
|
|
|
# =====================================================================
|
|
# Alle Dialoge laufen ueber genau drei Engstellen.
|
|
#
|
|
# Damit stimmt die Fenstergroesse automatisch - auch fuer Dialoge, die
|
|
# spaeter dazukommen. NEUE FENSTER BITTE IMMER UEBER DIESE DREI BAUEN
|
|
# und niemals "$UI_BIN" direkt aufrufen; "cmk-manage.sh selftest"
|
|
# prueft das nach.
|
|
#
|
|
# _ui_list Listenfenster (menu, checklist, radiolist) -> ui_calc
|
|
# _ui_box Textfenster (msgbox, yesno, inputbox, ...) -> ui_calc_text
|
|
# _ui_file Dateianzeige (textbox) -> volles Bild
|
|
# =====================================================================
|
|
|
|
# _ui_list WIDGET TITEL TEXT FELDER_JE_EINTRAG EINTRAEGE...
|
|
_ui_list() {
|
|
ui_init
|
|
local widget="$1" title="$2" text="$3" per="$4"; shift 4
|
|
ui_calc "$text" "$per" "$@"
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
|
|
}
|
|
|
|
# _ui_box WIDGET TITEL TEXT ZUSATZZEILEN [weitere Argumente]
|
|
# Widgets ohne Rueckgabe (msgbox, yesno, infobox) liefern nur den Status.
|
|
_ui_box() {
|
|
ui_init
|
|
local widget="$1" title="$2" text="$3" extra="$4"; shift 4
|
|
ui_calc_text "$text" "$extra"
|
|
case "$widget" in
|
|
--inputbox|--passwordbox)
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$@" 3>&1 1>&2 2>&3 ;;
|
|
*)
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$@" ;;
|
|
esac
|
|
}
|
|
|
|
# _ui_file TITEL DATEI - nutzt das Terminal so weit wie moeglich aus
|
|
_ui_file() {
|
|
ui_init
|
|
ui_term_size
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$1" \
|
|
--textbox "$2" $(( UI_ROWS - 2 )) $(( UI_COLS - 4 ))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Die eigentlichen Dialoge - alle nur noch duenne Huellen
|
|
# ---------------------------------------------------------------------
|
|
ui_menu() { local t="$1" x="$2"; shift 2; _ui_list --menu "$t" "$x" 2 "$@"; }
|
|
ui_checklist() { local t="$1" x="$2"; shift 2; _ui_list --checklist "$t" "$x" 3 "$@"; }
|
|
ui_radiolist() { local t="$1" x="$2"; shift 2; _ui_list --radiolist "$t" "$x" 3 "$@"; }
|
|
|
|
ui_input() { _ui_box --inputbox "$1" "$2" 10 "${3:-}"; }
|
|
ui_password() { _ui_box --passwordbox "$1" "$2" 10; }
|
|
ui_msg() { _ui_box --msgbox "$1" "$2" 7; }
|
|
ui_yesno() { _ui_box --yesno "$1" "$2" 8; }
|
|
ui_info() { _ui_box --infobox "$1" "$2" 5; }
|
|
ui_text() { _ui_file "$1" "$2"; }
|
|
|
|
# Kommando ausfuehren, Ausgabe live in Datei, danach in Textbox anzeigen
|
|
ui_run() { # title cmd...
|
|
ui_init
|
|
local title="$1"; shift
|
|
local logf; logf="$(mktemp)"
|
|
ui_info "$title" "Bitte warten ...
|
|
|
|
$*"
|
|
if "$@" >"$logf" 2>&1; then
|
|
ui_text "$title - erfolgreich" "$logf"
|
|
rm -f "$logf"; return 0
|
|
else
|
|
ui_text "$title - FEHLGESCHLAGEN" "$logf"
|
|
rm -f "$logf"; return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Dateiauswahl im Midnight-Commander-Stil (funktioniert auch mit whiptail)
|
|
# ---------------------------------------------------------------------
|
|
ui_pick_file() { # startdir [glob] -> Dateipfad auf stdout
|
|
ui_init
|
|
local dir="${1:-$PWD}" glob="${2:-*}" sel
|
|
dir="$(cd "$dir" 2>/dev/null && pwd || echo "$PWD")"
|
|
while :; do
|
|
local -a items=()
|
|
items+=(".." "<Verzeichnis hoeher>")
|
|
local d f
|
|
while IFS= read -r d; do
|
|
[ -n "$d" ] && items+=("$d/" "<DIR>")
|
|
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)
|
|
while IFS= read -r f; do
|
|
[ -n "$f" ] || continue
|
|
items+=("$f" "$(human_size "$(file_size "$dir/$f")")")
|
|
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name "$glob" -printf '%f\n' 2>/dev/null | sort)
|
|
|
|
sel="$(ui_menu "Datei waehlen" "Verzeichnis: $dir\nFilter: $glob" "${items[@]}")" || return 1
|
|
case "$sel" in
|
|
"..") dir="$(dirname "$dir")" ;;
|
|
*/) dir="$dir/${sel%/}" ;;
|
|
*) printf '%s\n' "$dir/$sel"; return 0 ;;
|
|
esac
|
|
done
|
|
}
|