From b4efbba050426bc7490cc0bd13939790d61b73f3 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sun, 20 Sep 2026 17:48:12 +0200 Subject: [PATCH] Alle Fenster ueber drei gemeinsame Stellen, Selbstpruefung dazu Die berechnete Fenstergroesse galt bisher nur fuer die Dialoge, die ich dafuer einzeln angefasst hatte. Eine Pruefung des ganzen Projekts zeigte zwei Luecken: * in common.sh gab es neun verstreute Aufrufstellen von dialog/whiptail * migrate_creator.sh hatte noch durchgehend feste Groessen (20 76 11, 10 76, 18 76, 14 76, 22 78, 8 76) Beide Skripte rufen dialog/whiptail jetzt nur noch ueber drei Stellen auf: _ui_list Listenfenster (menu, checklist, radiolist) _ui_box Textfenster (msgbox, yesno, inputbox, passwordbox, infobox) _ui_file Dateianzeige (textbox) Die berechnen die Groesse selbst. Damit stimmt sie auch fuer Fenster, die spaeter dazukommen - man muss nur eine der ui_*-Huellen benutzen. Neu: "cmk-manage.sh selftest" haelt das nach. Geprueft werden Syntax aller vier Skripte, Dialoge an den Engstellen vorbei, fest eingetragene Fenstergroessen, das Muster "[ ... ] && VAR=..." und die Vollstaendigkeit der Einstellungstabelle gegenueber .env.example. Der Lauf fand sofort 19 Altlasten des Musters "[ ... ] && VAR=...", das hier schon dreimal einen stillen Abbruch verursacht hat - alle in if-Bloecke umgeschrieben und stichprobenartig geprueft, auch die Faelle mit case-Abschluss und Array-Zuweisung. Danach laeuft die Selbstpruefung vollstaendig durch. Regression: alle CLI-Kommandos, zwoelf Menuefunktionen und das Rendern von sechs Fenstern unter whiptail in einem echten Pseudo-Terminal. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 23 +++++- README_en.md | 22 ++++++ scripts/cmk-manage.sh | 140 ++++++++++++++++++++++++++++++++----- scripts/lib/common.sh | 103 ++++++++++++++------------- scripts/migrate_creator.sh | 97 +++++++++++++++++++++++-- 5 files changed, 306 insertions(+), 79 deletions(-) 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