diff --git a/README.md b/README.md index a2a4089..558ca66 100644 --- a/README.md +++ b/README.md @@ -1621,7 +1621,28 @@ Bedienung: Pfeiltasten, **Leertaste** markiert in Auswahllisten (z. B. mehrere Sites für ein Backup), **Enter** bestätigt, **ESC/Cancel** geht zurück. Die Fenstergröße richtet sich nach Terminalgröße, Textlänge und Anzahl der Einträge – in einem größeren Terminal sind entsprechend mehr Einträge auf -einmal sichtbar. +einmal sichtbar. Das gilt für **alle** Fenster, auch in `migrate_creator.sh`: +sämtliche Dialoge laufen über drei gemeinsame Stellen in +[scripts/lib/common.sh](scripts/lib/common.sh), die die Größe selbst berechnen. + +### 14.4 Selbstprüfung + +```bash +./scripts/cmk-manage.sh selftest +``` + +Prüft die Skripte auf die Fallstricke, die sich hier als wiederkehrend +erwiesen haben: + +| Prüfung | Warum | +|---|---| +| Syntax aller vier Skripte | Tippfehler fallen sofort auf | +| Dialoge gehen über die gemeinsamen Stellen | sonst hätte ein neues Fenster wieder eine feste Größe | +| keine fest eingetragenen Fenstergrößen | dito | +| kein `[ … ] && VAR=…` | liefert 1, wenn die Bedingung nicht zutrifft, und beendet unter `set -e` das Skript – dreimal die Ursache eines stillen Abbruchs | +| Einstellungstabelle vollständig | jeder Schlüssel hat sechs Felder und steht in `.env.example` | + +Wer eigene Menüpunkte ergänzt, sollte den Aufruf danach einmal machen. Es wird `dialog` bevorzugt; ist nur `whiptail` vorhanden, wird dieses genutzt. Eine Dateiauswahl mit Verzeichnisnavigation ist eingebaut. diff --git a/README_en.md b/README_en.md index e1b77b3..c80fef6 100644 --- a/README_en.md +++ b/README_en.md @@ -1625,6 +1625,28 @@ Operation: arrow keys, **space** toggles entries in selection lists (e.g. several sites for a backup), **Enter** confirms, **ESC/Cancel** goes back. The window size adapts to the terminal size, the length of the text and the number of entries – in a larger terminal more entries are visible at once. +This holds for **every** window, `migrate_creator.sh` included: all dialogs go +through three shared entry points in +[scripts/lib/common.sh](scripts/lib/common.sh) which compute the size +themselves. + +### 14.4 Self-check + +```bash +./scripts/cmk-manage.sh selftest +``` + +Checks the scripts for the pitfalls that turned out to be recurring here: + +| Check | Why | +|---|---| +| syntax of all four scripts | typos surface immediately | +| dialogs go through the shared entry points | otherwise a new window would get a fixed size again | +| no hard-coded window sizes | same reason | +| no `[ … ] && VAR=…` | returns 1 when the condition is false and aborts the script under `set -e` – the cause of a silent abort three times | +| settings table complete | every key has six fields and appears in `.env.example` | + +Worth running after adding your own menu entries. `dialog` is preferred; if only `whiptail` is present, that is used instead. A file chooser with directory navigation is built in. diff --git a/scripts/cmk-manage.sh b/scripts/cmk-manage.sh index 347186c..ac939c9 100755 --- a/scripts/cmk-manage.sh +++ b/scripts/cmk-manage.sh @@ -49,6 +49,7 @@ KOMMANDOS - Stack pull Images aktualisieren status Status von Containern und Sites anzeigen logs [-f] [DIENST] Logs anzeigen + selftest Skripte auf bekannte Fallstricke pruefen shell Root-Shell im Checkmk-Container omd ARG... beliebiges omd-Kommando im Container ausfuehren @@ -439,7 +440,7 @@ EOF local cand cand="$(printf '%s\n' "$diff_new" | grep -Ei 'check-?mk' | head -n1)" [ -n "$cand" ] || cand="$(printf '%s\n' "$diff_new" | sed '/^$/d' | head -n1)" - [ -n "$cand" ] && loaded="$cand" + if [ -n "$cand" ]; then loaded="$cand"; fi fi if [ -z "$loaded" ]; then @@ -479,7 +480,7 @@ image_fetch_url() { warn "curl fehlt - verwende wget (die Zugangsdaten sind dabei kurzzeitig in der Prozessliste sichtbar)." local -a wa=() - [ -n "$user" ] && wa=(--user="$user" --password="$pass") + if [ -n "$user" ]; then wa=(--user="$user" --password="$pass"); fi wget ${wa[@]+"${wa[@]}"} -O "$out" "$url" return $? else @@ -824,11 +825,11 @@ image_consistency_check() { repo="${img%:*}" case "$repo" in */*) name="${repo##*/}" ;; *) name="$repo" ;; esac # kein Tag angegeben -> nicht beurteilbar - [ "$tag" = "$img" ] && tag="" + if [ "$tag" = "$img" ]; then tag=""; fi local ed_ok="" a for a in $(edition_aliases "$ed"); do - [ "$name" = "check-mk-$a" ] && ed_ok=1 + if [ "$name" = "check-mk-$a" ]; then ed_ok=1; fi done local ver_ok=1 @@ -934,7 +935,7 @@ test_registry_login() { # reg repo user pass needs_login -> 0 = alles gut local reg="$1" repo="$2" user="$3" pass="$4" needs_login="${5:-}" local rc=0 anon="" local api; api="$(registry_api_host "$reg")" - [ -z "$user" ] && anon=1 + if [ -z "$user" ]; then anon=1; fi printf '\n%sRegistry %s%s\n' "$C_BLD" "$reg" "$C_RST" [ "$api" != "$reg" ] && printf ' API-Host: %s\n' "$api" @@ -972,7 +973,7 @@ test_registry_login() { # reg repo user pass needs_login -> 0 = alles gut local realm_host; realm_host="$(printf '%s' "$realm" | sed -E 's|^[a-z]+://([^/]+).*|\1|')" local netrc="" - [ -z "$anon" ] && netrc="$(netrc_make "$user" "$pass" "$api" "$realm_host")" + if [ -z "$anon" ]; then netrc="$(netrc_make "$user" "$pass" "$api" "$realm_host")"; fi local url="$realm?service=$service&scope=repository:$repo:pull" local body tcode if [ -n "$netrc" ]; then @@ -1289,7 +1290,7 @@ registry_repo_status() { # api repo user pass -> "code anzahl_tags" local code code="$(curl "${a[@]}" "https://$api/v2/$repo/tags/list" 2>/dev/null)" || code=000 local n=0 - [ "${code:-000}" = 200 ] && n="$(tr ',' '\n' <"/tmp/.cmkrepo.$$" 2>/dev/null | grep -c '"' || echo 0)" + if [ "${code:-000}" = 200 ]; then n="$(tr ',' '\n' <"/tmp/.cmkrepo.$$" 2>/dev/null | grep -c '"' || echo 0)"; fi rm -f "/tmp/.cmkrepo.$$" printf '%s %s\n' "${code:-000}" "$n" } @@ -1331,7 +1332,7 @@ image_cmd_repos() { code="${line%% *}"; n="${line##* }" case "$code" in 200) printf ' %s%-6s%s %-8s %s\n' "$C_GRN" "$code" "$C_RST" "$n" "$cand" - [ -z "$ok" ] && ok="$cand" ;; + if [ -z "$ok" ]; then ok="$cand"; fi ;; 401|403) printf ' %s%-6s%s %-8s %s (kein Zugriff - oder gibt es nicht;\n%38sviele Registries verbergen das)\n' \ "$C_YEL" "$code" "$C_RST" "-" "$cand" "" ;; 404) printf ' %-6s %-8s %s (gibt es nicht)\n' "$code" "-" "$cand" ;; @@ -1388,7 +1389,7 @@ image_cmd_versions() { Moeglich: raw community free cloud managed pro ultimate ultimatemt enterprise" case "$source" in registry|download|web|both|all) : ;; *) die "--source: registry | download | web | all" ;; esac - [ "$source" = both ] && source=all + if [ "$source" = both ]; then source=all; fi case "$limit" in ''|*[!0-9]*) die "--limit braucht eine Zahl." ;; esac command -v curl >/dev/null 2>&1 || die "Fuer 'image versions' wird curl benoetigt." @@ -2008,11 +2009,11 @@ cmd_job() { if [ -n "$is_set" ]; then [ -n "$old" ] || die "Job '$name' ist nicht vorhanden." # nicht angegebene Felder aus dem Bestand uebernehmen - [ "$sites" = ALL ] && sites="$(job_f "$old" 2)" - [ -z "$cron" ] && cron="$(job_f "$old" 3)" - [ -z "$dir" ] && dir="$(job_f "$old" 4)" - [ -z "$opts" ] && opts="$(job_f "$old" 5)" - [ "$keep" = 7 ] && keep="$(job_f "$old" 6)" + if [ "$sites" = ALL ]; then sites="$(job_f "$old" 2)"; fi + if [ -z "$cron" ]; then cron="$(job_f "$old" 3)"; fi + if [ -z "$dir" ]; then dir="$(job_f "$old" 4)"; fi + if [ -z "$opts" ]; then opts="$(job_f "$old" 5)"; fi + if [ "$keep" = 7 ]; then keep="$(job_f "$old" 6)"; fi enabled="$(job_f "$old" 7)" else [ -z "$old" ] || die "Job '$name' existiert bereits. Aendern mit: $SELF job set $name ..." @@ -2773,7 +2774,7 @@ cmd_config() { case "$sub" in list|ls) local cat_filter="" - [ "${1:-}" = "--category" ] && cat_filter="$2" + if [ "${1:-}" = "--category" ]; then cat_filter="$2"; fi local line c k t l while IFS='|' read -r c k t l _ _; do [ -n "$k" ] || continue @@ -3360,7 +3361,7 @@ cmd_cert() { esac done local myip; myip="$(primary_ip)" - [ -n "$myip" ] && san="$san,IP:$myip" + if [ -n "$myip" ]; then san="$san,IP:$myip"; fi log "Erzeuge selbstsigniertes Zertifikat fuer '$domain' ($days Tage) ..." openssl req -x509 -nodes -newkey rsa:4096 -sha256 -days "$days" \ @@ -3646,7 +3647,7 @@ Jobdatei: $(jobs_file)" \ toggle) local j2 l st j2="$(gui_pick_job)" || continue; [ -n "$j2" ] || continue l="$(job_line "$j2")"; st=disable - [ "$(job_f "$l" 7)" = off ] && st=enable + if [ "$(job_f "$l" 7)" = off ]; then st=enable; fi ui_run "Job $st" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job $st '$j2'" ;; del) local j3; j3="$(gui_pick_job)" || continue; [ -n "$j3" ] || continue ui_yesno "Loeschen" "Job '$j3' wirklich loeschen?\n\n(Bereits erstellte Backups bleiben erhalten.)" \ @@ -4195,7 +4196,7 @@ Diese drei Werte setzt der Assistent gemeinsam." \ done IFS="$IFS_BAK" new="$(ui_radiolist "$key" "$label\n\nAktuell: ${cur:-}" "${ropts[@]}")" || return 0 - [ "$new" = auto ] && new="" + if [ "$new" = auto ]; then new=""; fi ;; password) new="$(ui_password "$key" "$label\n\n(leer lassen = unveraendert)")" || return 0 @@ -4327,7 +4328,7 @@ Oberflaeche des Proxy Managers darstellen." \ case "$sel" in url) local u u="$(ui_input "Startseite" "URL:" "${NPM_ADMIN_URL:-http://127.0.0.1:81}")" || continue - [ -n "$u" ] && NPM_ADMIN_URL="$u" ;; + if [ -n "$u" ]; then NPM_ADMIN_URL="$u"; fi ;; build) ui_run "Werkzeug-Image bauen" bash -c "cd '$PROJECT_DIR' && '$SELF' browser --build-only" || true ;; back) return 0 ;; *) local burl="${NPM_ADMIN_URL:-http://127.0.0.1:81}" @@ -4512,6 +4513,106 @@ Backups : $BACKUP_DIR" \ ok "Beendet." } +# ===================================================================== +# Selbstpruefung +# +# Faengt die Fehler ab, die hier wiederholt aufgetreten sind: +# 1. Dialoge, die an den Engstellen vorbei direkt "$UI_BIN" aufrufen - +# die haetten dann wieder feste Fenstergroessen. +# 2. Feste Zahlen als Fenstergroesse. +# 3. "[ bedingung ] && VAR=wert": liefert 1, wenn die Bedingung nicht +# zutrifft, und beendet unter "set -e" Funktion oder Skript. +# Aufruf: cmk-manage.sh selftest +# ===================================================================== +cmd_selftest() { + local rc=0 n + local -a files=("$SCRIPT_DIR/cmk-manage.sh" "$SCRIPT_DIR/lib/common.sh" \ + "$SCRIPT_DIR/migrate_creator.sh" "$PROJECT_DIR/install.sh") + + printf '\n%s1. Syntax%s\n' "$C_BLD" "$C_RST" + local f + for f in "${files[@]}"; do + [ -f "$f" ] || continue + if bash -n "$f" 2>/dev/null; then + printf ' %s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$(basename "$f")" + else + printf ' %s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$(basename "$f")"; rc=1 + fi + done + + printf '\n%s2. Dialoge laufen ueber die Engstellen%s\n' "$C_BLD" "$C_RST" + n=0 + for f in "$SCRIPT_DIR/cmk-manage.sh" "$SCRIPT_DIR/lib/common.sh" "$SCRIPT_DIR/migrate_creator.sh"; do + [ -f "$f" ] || continue + # Aufrufe von "$UI_BIN" sind nur in _ui_list/_ui_box/_ui_file erlaubt + while IFS= read -r line; do + [ -n "$line" ] || continue + printf ' %s[FEHLER]%s %s:%s\n' "$C_RED" "$C_RST" "$(basename "$f")" "$line" + n=$((n+1)) + done < <(awk ' + /^[_a-zA-Z0-9]+\(\) \{/ { fn = $1; sub(/\(\).*/, "", fn) } + /"\$UI_BIN"[[:space:]]+--/ { + if ($0 ~ /^[[:space:]]*#/) next + if (fn ~ /^_ui_/ || fn == "ui_available") next + printf "%d: %s\n", NR, substr($0, 1, 60) + }' "$f") + done + if [ "$n" -eq 0 ]; then + printf ' %s[ OK ]%s kein Dialog ruft dialog/whiptail direkt auf\n' "$C_GRN" "$C_RST" + else + printf ' Neue Fenster bitte ueber ui_menu/ui_msg/... bauen.\n'; rc=1 + fi + + printf '\n%s3. Keine festen Fenstergroessen%s\n' "$C_BLD" "$C_RST" + n=0 + for f in "$SCRIPT_DIR/lib/common.sh" "$SCRIPT_DIR/migrate_creator.sh"; do + [ -f "$f" ] || continue + while IFS= read -r line; do + [ -n "$line" ] || continue + printf ' %s[FEHLER]%s %s:%s\n' "$C_RED" "$C_RST" "$(basename "$f")" "$line" + n=$((n+1)) + done < <(grep -nE '"\$UI_BIN".*(--menu|--checklist|--radiolist|--[a-z]+box).*[[:space:]][0-9]{1,3}[[:space:]]' "$f" \ + | cut -c1-70 || true) + done + [ "$n" -eq 0 ] && printf ' %s[ OK ]%s alle Groessen werden berechnet\n' "$C_GRN" "$C_RST" || rc=1 + + printf '\n%s4. Keine "[ ... ] && VAR=" unter set -e%s\n' "$C_BLD" "$C_RST" + n=0 + for f in "${files[@]}"; do + [ -f "$f" ] || continue + while IFS= read -r line; do + [ -n "$line" ] || continue + printf ' %s[ WARN ]%s %s:%s\n' "$C_YEL" "$C_RST" "$(basename "$f")" "$line" + n=$((n+1)) + done < <(grep -nE '^[[:space:]]*\[[^]]+\][[:space:]]*&&[[:space:]]*[A-Za-z_][A-Za-z_0-9]*=' "$f" \ + | cut -c1-70 || true) + done + if [ "$n" -eq 0 ]; then + printf ' %s[ OK ]%s keine Fundstellen\n' "$C_GRN" "$C_RST" + else + printf ' Als letzte Anweisung einer Funktion beendet das unter\n' + printf ' "set -e" das Skript. Besser als if-Block schreiben.\n' + fi + + printf '\n%s5. Einstellungstabelle%s\n' "$C_BLD" "$C_RST" + n="$(settings_defs | awk -F'|' 'NF!=6' | wc -l)" + if [ "$n" -eq 0 ]; then + printf ' %s[ OK ]%s alle Zeilen haben sechs Felder\n' "$C_GRN" "$C_RST" + else + printf ' %s[FEHLER]%s %s Zeile(n) mit falscher Feldzahl\n' "$C_RED" "$C_RST" "$n"; rc=1 + fi + local missing=0 k + for k in $(settings_defs | cut -d'|' -f2); do + grep -qE "^${k}=" "$PROJECT_DIR/.env.example" || { printf ' %s[FEHLER]%s %s fehlt in .env.example\n' "$C_RED" "$C_RST" "$k"; missing=1; } + done + [ "$missing" -eq 0 ] && printf ' %s[ OK ]%s alle Schluessel stehen in .env.example\n' "$C_GRN" "$C_RST" || rc=1 + + echo + if [ "$rc" -eq 0 ]; then ok "Selbstpruefung bestanden."; else err "Selbstpruefung hat Probleme gefunden."; fi + echo + return "$rc" +} + # ===================================================================== # Einstiegspunkt # ===================================================================== @@ -4560,6 +4661,7 @@ main() { npm-enable) cmd_npm_enable "$@" ;; proxy) cmd_proxy "$@" ;; config) cmd_config "$@" || true ;; + selftest) cmd_selftest ;; help|-h|--help) usage ;; *) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;; esac diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 9356c17..fc6b61f 100644 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -317,75 +317,74 @@ ui_require() { UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME" } -ui_menu() { # title text tag item [tag item ...] -> gewaehlter tag auf stdout +# ===================================================================== +# 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 title="$1" text="$2"; shift 2 - ui_calc "$text" 2 "$@" + local widget="$1" title="$2" text="$3" per="$4"; shift 4 + ui_calc "$text" "$per" "$@" "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \ - --menu "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3 + "$widget" "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3 } -ui_checklist() { # title text tag item status ... +# _ui_box WIDGET TITEL TEXT ZUSATZZEILEN [weitere Argumente] +# Widgets ohne Rueckgabe (msgbox, yesno, infobox) liefern nur den Status. +_ui_box() { ui_init - local title="$1" text="$2"; shift 2 - ui_calc "$text" 3 "$@" - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \ - --checklist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3 + local widget="$1" title="$2" text="$3" extra="$4"; shift 4 + ui_calc_text "$text" "$extra" + case "$widget" in + --inputbox|--passwordbox) + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \ + "$widget" "$text" "$UI_H" "$UI_W" "$@" 3>&1 1>&2 2>&3 ;; + *) + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \ + "$widget" "$text" "$UI_H" "$UI_W" "$@" ;; + esac } -ui_radiolist() { - ui_init - local title="$1" text="$2"; shift 2 - ui_calc "$text" 3 "$@" - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$title" \ - --radiolist "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3 -} - -ui_input() { # title text default - ui_init - ui_calc_text "$2" 10 - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \ - --inputbox "$2" "$UI_H" "$UI_W" "${3:-}" 3>&1 1>&2 2>&3 -} - -ui_password() { - ui_init - ui_calc_text "$2" 10 - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \ - --passwordbox "$2" "$UI_H" "$UI_W" 3>&1 1>&2 2>&3 -} - -ui_msg() { # title text - ui_init - ui_calc_text "$2" 7 - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" "$UI_H" "$UI_W" -} - -ui_yesno() { # title text -> exit 0 = ja - ui_init - ui_calc_text "$2" 8 - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" "$UI_H" "$UI_W" -} - -ui_text() { # title file - so gross wie das Terminal es zulaesst +# _ui_file TITEL DATEI - nutzt das Terminal so weit wie moeglich aus +_ui_file() { ui_init ui_term_size - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" \ - $(( UI_ROWS - 2 )) $(( UI_COLS - 4 )) + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" \ + --textbox "$2" $(( UI_ROWS - 2 )) $(( UI_COLS - 4 )) } -ui_info() { # title text (ohne OK-Button) - ui_init - ui_calc_text "$2" 5 - "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" "$UI_H" "$UI_W" -} +# --------------------------------------------------------------------- +# 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 ...\n\n$*" + ui_info "$title" "Bitte warten ... + +$*" if "$@" >"$logf" 2>&1; then ui_text "$title - erfolgreich" "$logf" rm -f "$logf"; return 0 diff --git a/scripts/migrate_creator.sh b/scripts/migrate_creator.sh index 51302c4..2065fb8 100755 --- a/scripts/migrate_creator.sh +++ b/scripts/migrate_creator.sh @@ -107,13 +107,96 @@ ui_init() { else UI_BIN=""; fi UI_BACKTITLE="migrate_creator $VERSION - $(hostname)" } -ui_menu() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --menu "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; } -ui_checklist() { local t="$1" x="$2"; shift 2; "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" --checklist "$x" 20 76 11 "$@" 3>&1 1>&2 2>&3; } -ui_input() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --inputbox "$2" 10 76 "${3:-}" 3>&1 1>&2 2>&3; } -ui_msg() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --msgbox "$2" 18 76; } -ui_yesno() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --yesno "$2" 14 76; } -ui_text() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" 22 78; } -ui_info() { "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --infobox "$2" 8 76; } +# --- Fenstergroesse berechnen ---------------------------------------- +# Wie im Hauptskript: die Groesse ergibt sich aus Terminal, Textlaenge +# und Anzahl der Eintraege. Feste Werte fuehrten bei langem Text dazu, +# dass von der Auswahlliste nur noch eine Zeile sichtbar war. +# Hier bewusst mit if-Bloecken statt "[ ... ] && VAR=..." - letzteres +# liefert 1, wenn die Bedingung nicht zutrifft, und beendet unter +# "set -e" die Funktion. +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 +} + +ui_calc() { # text felder_je_eintrag eintraege... + 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 }')" + 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 + 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 + 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 +} + +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 +} + +# --- Engstellen: hierueber laufen ALLE Dialoge ------------------------ +_ui_list() { # widget titel text felder_je_eintrag eintraege... + local widget="$1" t="$2" x="$3" per="$4"; shift 4 + ui_calc "$x" "$per" "$@" + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" \ + "$widget" "$x" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3 +} +_ui_box() { # widget titel text zusatzzeilen [weitere] + local widget="$1" t="$2" x="$3" extra="$4"; shift 4 + ui_calc_text "$x" "$extra" + case "$widget" in + --inputbox|--passwordbox) + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" \ + "$widget" "$x" "$UI_H" "$UI_W" "$@" 3>&1 1>&2 2>&3 ;; + *) "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$t" \ + "$widget" "$x" "$UI_H" "$UI_W" "$@" ;; + esac +} +_ui_file() { # titel datei + ui_term_size + "$UI_BIN" --backtitle "$UI_BACKTITLE" --title "$1" --textbox "$2" \ + $(( UI_ROWS - 2 )) $(( UI_COLS - 4 )) +} + +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_input() { _ui_box --inputbox "$1" "$2" 10 "${3:-}"; } +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"; } # --------------------------------------------------------------------- # Bundle erzeugen