From c075133295f65420c25a9e319487485dcf70ef4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:07:51 +0000 Subject: [PATCH 01/10] Initial plan From 974a4ce5fae67ddc2936ef44a9df4b4d92eaefc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:15:13 +0000 Subject: [PATCH 02/10] Implement cancel detection and exit for all dialog functions - Add SCRIPT_DIALOG_CANCEL_EXIT_CODE environment variable (default 1) - Update README to document the cancel exit code variable - Implement exit status checking in inputbox, password, userandpassword - Implement exit status checking in display-file, checklist, radiolist - Implement exit status checking in filepicker, folderpicker, datepicker - Scripts will now exit with the configured code when dialogs are cancelled Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- README.md | 1 + script-dialog.sh | 121 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b792d7b..4c46a0a 100755 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Global Variables | **INTERFACE** | Override or Detected | the GUI or TUI to use | | **GUI** | Detected | Whether the interface is a GUI not TUI | | **DETECTED_DESKTOP** | Detected | Desktop in use | +| **SCRIPT_DIALOG_CANCEL_EXIT_CODE** | Optional override | Exit code when a dialog is cancelled (default: 1) | | **NOCOLORS** | Optional override | disables otherwise-detected use of colored/bolded text basic CLI | | **NOSYMBOLS** | Optional override |disables otherwise-detected use of unicode symbols in TUIs | | **ZENITY_HEIGHT** | Optional override | height of zenity dialogs | diff --git a/script-dialog.sh b/script-dialog.sh index cf2636b..f44fc6a 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -148,6 +148,11 @@ if echo "test" | read -ri "test" 2>/dev/null; then NO_READ_DEFAULT="" fi +# Set default cancel exit code if not already set +if [ -z ${SCRIPT_DIALOG_CANCEL_EXIT_CODE+x} ]; then + SCRIPT_DIALOG_CANCEL_EXIT_CODE=1 +fi + ################################ # Variables @@ -715,16 +720,27 @@ function inputbox() { TEST_STRING="${QUESTION_SYMBOL} $1" _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then INPUT=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then INPUT=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT="$(zenity --entry --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --text="$1" --entry-text "$2")" + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then INPUT="$(kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --inputbox "$1" "$2")" + exit_status=$? else read ${NO_READ_DEFAULT+-i "$2"} -rep "${SYMBOL}${bold}$1: ${normal}" INPUT + exit_status=$? + fi + + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi echo "$INPUT" @@ -777,23 +793,37 @@ function userandpassword() { if [ "$PASS_TEXT" == "" ]; then PASS_TEXT="Password"; fi local CREDS=() + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then CREDS[0]=$(inputbox "$USER_TEXT" "$SUGGESTED_USERNAME") CREDS[1]=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$PASS_TEXT" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then mapfile -t CREDS < <( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --insecure --mixedform "Login:" "$RECMD_LINES" "$RECMD_COLS" 0 "Username: " 1 1 "$SUGGESTED_USERNAME" 1 11 22 0 0 "Password :" 2 1 "" 2 11 22 0 1 3>&1 1>&2 2>&3 ) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then ENTRY=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --password --username "$SUGGESTED_USERNAME") + exit_status=$? CREDS[0]=$(echo "$ENTRY" | cut -d'|' -f1) CREDS[1]=$(echo "$ENTRY" | cut -d'|' -f2) elif [ "$INTERFACE" == "kdialog" ]; then CREDS[0]=$(inputbox "$USER_TEXT" "$SUGGESTED_USERNAME") CREDS[1]=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --password "$PASS_TEXT") + exit_status=$? else read ${NO_READ_DEFAULT+-i "$SUGGESTED_USERNAME"} -rep "${QUESTION_SYMBOL}${bold}$USER_TEXT: ${normal}" "CREDS[0]" - read -srp "${bold}${PASSWORD_SYMBOL}$PASS_TEXT: ${normal}" "CREDS[1]" + exit_status=$? + if [ $exit_status -eq 0 ]; then + read -srp "${bold}${PASSWORD_SYMBOL}$PASS_TEXT: ${normal}" "CREDS[1]" + exit_status=$? + fi echo fi + + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi eval "$__uservar"="'${CREDS[0]}'" eval "$__passvar"="'${CREDS[1]}'" @@ -830,17 +860,29 @@ function password() { TEST_STRING="${PASSWORD_SYMBOL}$1" _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then PASSWORD=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then PASSWORD=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then PASSWORD=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --password) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then PASSWORD=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --password "$1") + exit_status=$? else read -srp "${PASSWORD_SYMBOL}${bold}$ACTIVITY: ${normal}" PASSWORD + exit_status=$? + fi + + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi + echo "$PASSWORD" } @@ -878,16 +920,27 @@ function display-file() { local height=${3-${ZENITY_HEIGHT-640}} _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="$height" --width="$width" --text-info --filename="$1" + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --textbox "$1" "$width" "$height" + exit_status=$? else less "$1" 3>&1 1>&2 2>&3 + exit_status=$? + fi + + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi } @@ -953,10 +1006,13 @@ function checklist() { fi fi + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then mapfile -t CHOSEN_ITEMS < <( whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then IFS=$'\n' read -r -d '' -a CHOSEN_LIST < <( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --separate-output --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -979,6 +1035,7 @@ function checklist() { shift done IFS=$'|' read -r -d '' -a CHOSEN_LIST < <( zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="${ZENITY_HEIGHT-512}" ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --list --text "$TEXT" --checklist --column "" --column "Value" --column "Description" "${OPTIONS[@]}" ) + exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -988,6 +1045,7 @@ function checklist() { elif [ "$INTERFACE" == "kdialog" ]; then mapfile -t CHOSEN_ITEMS < <( kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --checklist "$TEXT" "$@") + exit_status=$? else printf "%s\n $TEXT:\n" "${QUESTION_SYMBOL}$ACTIVITY" 3>&1 1>&2 2>&3 local CHOSEN_ITEMS=() @@ -1003,6 +1061,11 @@ function checklist() { done fi + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi + echo "${CHOSEN_ITEMS[@]}" } @@ -1069,10 +1132,13 @@ function radiolist() { fi fi + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then CHOSEN_ITEM=$( whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --radiolist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then CHOSEN_ITEM=$( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --quoted --radiolist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then OPTIONS=() while test ${#} -gt 0; do @@ -1088,8 +1154,10 @@ function radiolist() { shift done CHOSEN_ITEM=$( zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="${ZENITY_HEIGHT-512}" ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --list --text "$TEXT" --radiolist --column "" --column "Value" --column "Description" "${OPTIONS[@]}") + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then CHOSEN_ITEM=$( kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --radiolist "$TEXT" "$@") + exit_status=$? else echo -e "${QUESTION_SYMBOL}$ACTIVITY: " 3>&1 1>&2 2>&3 OPTIONS=() @@ -1106,12 +1174,19 @@ function radiolist() { shift done read -rp "$(echo -e "${OPTIONS[*]}${QUESTION_SYMBOL}${bold}$TEXT: ${normal}")" CHOSEN_ITEM + exit_status=$? if [[ "$CHOSEN_ITEM" == "" ]]; then CHOSEN_ITEM="$DEFAULT" fi fi + # Exit script if dialog was cancelled (check both exit status and empty result) + # For radio lists, empty response indicates cancel + if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi + echo "$CHOSEN_ITEM" } @@ -1305,40 +1380,45 @@ function filepicker() { fi fi _calculate-gui-title + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2012 read -r -d '' -a files < <(ls -lBhpa "$1" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) + exit_status=$? FILE="$1/$SELECTED" - #exitstatus=$? - #if [ $exitstatus != 0 ]; then - #echo "CANCELLED!" - #exit; - #fi - elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --fselect "$1"/ 14 48) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --filename "$1"/ ) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then if [ "$2" == "save" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getsavefilename "$1"/ ) else #elif [ "$2" == "open" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getopenfilename "$1"/ ) fi + exit_status=$? else read -erp "${DOCUMENT_SYMBOL}You need to $2 a file in $1/. Hit enter to browse this folder" ls -lBhpa "$1" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED + exit_status=$? # TODO: Add validation - handle empty SELECTED or when SELECTED is a folder FILE=$1/$SELECTED fi + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi + # Ignore choice and relaunch dialog if [[ "$SELECTED" == "./" ]]; then FILE=$(filepicker "$1" "$2") @@ -1380,24 +1460,23 @@ function folderpicker() { GUI_ICON=$XDG_ICO_FOLDER_OPEN fi _calculate-gui-title + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2010 read -r -d '' -a files < <(ls -lBhpa "$1" | grep "^d" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) + exit_status=$? FILE="$1/$SELECTED" - #exitstatus=$? - #if [ $exitstatus != 0 ]; then - #echo "CANCELLED!" - #exit; - #fi - elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --dselect "$1"/ 14 48) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --directory --filename "$1"/ ) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getexistingdirectory "$1"/ ) + exit_status=$? else read -erp "${FOLDER_SYMBOL}You need to select a folder in $1/. Hit enter to browse this folder" @@ -1405,12 +1484,18 @@ function folderpicker() { ls -lBhpa "$1" | grep "^d" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED + exit_status=$? # TODO: Add validation - handle empty SELECTED or parent directory (..) FILE=$1/$SELECTED fi + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi + echo "$FILE" } @@ -1452,19 +1537,23 @@ function datepicker() { MONTH=0 YEAR=0 + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then local SYMBOL=$CALENDAR_SYMBOL STANDARD_DATE=$(inputbox "Input Date (DD/MM/YYYY)" "$NOW") elif [ "$INTERFACE" == "dialog" ]; then STANDARD_DATE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --calendar "${CALENDAR_SYMBOL}Choose Date" 0 40) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT_DATE=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --calendar "Select Date") + exit_status=$? MONTH=$(echo "$INPUT_DATE" | cut -d'/' -f1) DAY=$(echo "$INPUT_DATE" | cut -d'/' -f2) YEAR=$(echo "$INPUT_DATE" | cut -d'/' -f3) STANDARD_DATE="$DAY/$MONTH/$YEAR" elif [ "$INTERFACE" == "kdialog" ]; then INPUT_DATE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --calendar "Select Date") + exit_status=$? TEXT_MONTH=$(echo "$INPUT_DATE" | cut -d' ' -f2) if [ "$TEXT_MONTH" == "Jan" ]; then MONTH=1 @@ -1497,6 +1586,12 @@ function datepicker() { STANDARD_DATE="$DAY/$MONTH/$YEAR" else read ${NO_READ_DEFAULT+-i "$NOW"} -rep "${CALENDAR_SYMBOL}${bold}Date (DD/MM/YYYY): ${normal}" STANDARD_DATE + exit_status=$? + fi + + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi echo "$STANDARD_DATE" From 5afb9a8b5e05315b80e7a92fda5f6f2fb5e5f7a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:18:42 +0000 Subject: [PATCH 03/10] Fix radiolist cancel detection to avoid false positives in CLI mode - Move exit status checks inside each interface branch - CLI mode now only checks exit status, not empty string - GUI/TUI modes check both exit status and empty response - Prevents false positive cancellation when valid empty selection in CLI Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/script-dialog.sh b/script-dialog.sh index f44fc6a..6fa6233 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -1136,9 +1136,17 @@ function radiolist() { if [ "$INTERFACE" == "whiptail" ]; then CHOSEN_ITEM=$( whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --radiolist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) exit_status=$? + # For TUI interfaces, empty response indicates cancel + if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi elif [ "$INTERFACE" == "dialog" ]; then CHOSEN_ITEM=$( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --quoted --radiolist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) exit_status=$? + # For TUI interfaces, empty response indicates cancel + if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi elif [ "$INTERFACE" == "zenity" ]; then OPTIONS=() while test ${#} -gt 0; do @@ -1155,9 +1163,17 @@ function radiolist() { done CHOSEN_ITEM=$( zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="${ZENITY_HEIGHT-512}" ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --list --text "$TEXT" --radiolist --column "" --column "Value" --column "Description" "${OPTIONS[@]}") exit_status=$? + # For GUI interfaces, empty response indicates cancel + if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi elif [ "$INTERFACE" == "kdialog" ]; then CHOSEN_ITEM=$( kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --radiolist "$TEXT" "$@") exit_status=$? + # For GUI interfaces, empty response indicates cancel + if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi else echo -e "${QUESTION_SYMBOL}$ACTIVITY: " 3>&1 1>&2 2>&3 OPTIONS=() @@ -1179,12 +1195,11 @@ function radiolist() { if [[ "$CHOSEN_ITEM" == "" ]]; then CHOSEN_ITEM="$DEFAULT" fi - fi - - # Exit script if dialog was cancelled (check both exit status and empty result) - # For radio lists, empty response indicates cancel - if [ $exit_status -ne 0 ] || [[ -z "$CHOSEN_ITEM" ]]; then - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + + # For CLI interface, only check exit status + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi fi echo "$CHOSEN_ITEM" From 4e7d4de2695f750a9a095ebd4fe859656f427f03 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:20:12 +0000 Subject: [PATCH 04/10] Fix: Quote variable expansion for safety - Quote ${SCRIPT_DIALOG_CANCEL_EXIT_CODE+x} to prevent issues with special characters - Improves shell safety and robustness Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script-dialog.sh b/script-dialog.sh index 6fa6233..7026e22 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -149,7 +149,7 @@ if echo "test" | read -ri "test" 2>/dev/null; then fi # Set default cancel exit code if not already set -if [ -z ${SCRIPT_DIALOG_CANCEL_EXIT_CODE+x} ]; then +if [ -z "${SCRIPT_DIALOG_CANCEL_EXIT_CODE+x}" ]; then SCRIPT_DIALOG_CANCEL_EXIT_CODE=1 fi From 74e7afd9e92268582ea430da29f0886fb021a04d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:29:39 +0000 Subject: [PATCH 05/10] Refactor exit status handling per PR feedback - Update pause function to use SCRIPT_DIALOG_CANCEL_EXIT_CODE instead of exit 0 - Move exit_status=$? to after if-else chains for cleaner code - Applied to: inputbox, password, display-file, checklist, filepicker, folderpicker, datepicker - userandpassword has special handling for different interfaces - radiolist kept its per-branch checks due to empty response handling - All functions maintain proper cancel detection Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 74 +++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 51 deletions(-) diff --git a/script-dialog.sh b/script-dialog.sh index 7026e22..9bcd2c7 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -596,31 +596,31 @@ function pause() { if whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-button "Continue" --no-button "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS"; then return 0 else - exit 0 + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi elif [ "$INTERFACE" == "dialog" ]; then if dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-label "Continue" --no-label "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS"; then return 0 else - exit 0 + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi elif [ "$INTERFACE" == "zenity" ]; then if zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --question --text "$MESSAGE" --ok-label="Continue" --cancel-label="Quit"; then return 0 else - exit 0 + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi elif [ "$INTERFACE" == "kdialog" ]; then if kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --yes-label "Continue" --no-label "Quit" --yesno "$MESSAGE"; then return 0 else - exit 0 + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi else echo -ne "${QUESTION_SYMBOL}${bold}$MESSAGE (press Enter to continue, q to quit): ${normal}" 3>&1 1>&2 2>&3 read -r answer if [[ "${answer,,}" == "q" ]]; then - exit 0 + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi return 0 fi @@ -720,23 +720,18 @@ function inputbox() { TEST_STRING="${QUESTION_SYMBOL} $1" _calculate-tui-size - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then INPUT=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then INPUT=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT="$(zenity --entry --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --text="$1" --entry-text "$2")" - exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then INPUT="$(kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --inputbox "$1" "$2")" - exit_status=$? else read ${NO_READ_DEFAULT+-i "$2"} -rep "${SYMBOL}${bold}$1: ${normal}" INPUT - exit_status=$? fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -793,32 +788,37 @@ function userandpassword() { if [ "$PASS_TEXT" == "" ]; then PASS_TEXT="Password"; fi local CREDS=() - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then CREDS[0]=$(inputbox "$USER_TEXT" "$SUGGESTED_USERNAME") CREDS[1]=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$PASS_TEXT" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then mapfile -t CREDS < <( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --insecure --mixedform "Login:" "$RECMD_LINES" "$RECMD_COLS" 0 "Username: " 1 1 "$SUGGESTED_USERNAME" 1 11 22 0 0 "Password :" 2 1 "" 2 11 22 0 1 3>&1 1>&2 2>&3 ) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then ENTRY=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --password --username "$SUGGESTED_USERNAME") - exit_status=$? + local exit_status=$? CREDS[0]=$(echo "$ENTRY" | cut -d'|' -f1) CREDS[1]=$(echo "$ENTRY" | cut -d'|' -f2) + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi elif [ "$INTERFACE" == "kdialog" ]; then CREDS[0]=$(inputbox "$USER_TEXT" "$SUGGESTED_USERNAME") CREDS[1]=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --password "$PASS_TEXT") - exit_status=$? else read ${NO_READ_DEFAULT+-i "$SUGGESTED_USERNAME"} -rep "${QUESTION_SYMBOL}${bold}$USER_TEXT: ${normal}" "CREDS[0]" - exit_status=$? + local exit_status=$? if [ $exit_status -eq 0 ]; then read -srp "${bold}${PASSWORD_SYMBOL}$PASS_TEXT: ${normal}" "CREDS[1]" exit_status=$? fi echo + # Exit script if dialog was cancelled + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -860,23 +860,18 @@ function password() { TEST_STRING="${PASSWORD_SYMBOL}$1" _calculate-tui-size - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then PASSWORD=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then PASSWORD=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then PASSWORD=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --password) - exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then PASSWORD=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --password "$1") - exit_status=$? else read -srp "${PASSWORD_SYMBOL}${bold}$ACTIVITY: ${normal}" PASSWORD - exit_status=$? fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -920,23 +915,18 @@ function display-file() { local height=${3-${ZENITY_HEIGHT-640}} _calculate-tui-size - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" - exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="$height" --width="$width" --text-info --filename="$1" - exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --textbox "$1" "$width" "$height" - exit_status=$? else less "$1" 3>&1 1>&2 2>&3 - exit_status=$? fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1006,13 +996,10 @@ function checklist() { fi fi - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then mapfile -t CHOSEN_ITEMS < <( whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) - exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then IFS=$'\n' read -r -d '' -a CHOSEN_LIST < <( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --separate-output --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) - exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -1035,7 +1022,6 @@ function checklist() { shift done IFS=$'|' read -r -d '' -a CHOSEN_LIST < <( zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="${ZENITY_HEIGHT-512}" ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --list --text "$TEXT" --checklist --column "" --column "Value" --column "Description" "${OPTIONS[@]}" ) - exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -1045,7 +1031,6 @@ function checklist() { elif [ "$INTERFACE" == "kdialog" ]; then mapfile -t CHOSEN_ITEMS < <( kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --checklist "$TEXT" "$@") - exit_status=$? else printf "%s\n $TEXT:\n" "${QUESTION_SYMBOL}$ACTIVITY" 3>&1 1>&2 2>&3 local CHOSEN_ITEMS=() @@ -1060,6 +1045,7 @@ function checklist() { shift done fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1395,39 +1381,34 @@ function filepicker() { fi fi _calculate-gui-title - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2012 read -r -d '' -a files < <(ls -lBhpa "$1" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) - exit_status=$? FILE="$1/$SELECTED" elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --fselect "$1"/ 14 48) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --filename "$1"/ ) - exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then if [ "$2" == "save" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getsavefilename "$1"/ ) else #elif [ "$2" == "open" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getopenfilename "$1"/ ) fi - exit_status=$? else read -erp "${DOCUMENT_SYMBOL}You need to $2 a file in $1/. Hit enter to browse this folder" ls -lBhpa "$1" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED - exit_status=$? # TODO: Add validation - handle empty SELECTED or when SELECTED is a folder FILE=$1/$SELECTED fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1475,23 +1456,18 @@ function folderpicker() { GUI_ICON=$XDG_ICO_FOLDER_OPEN fi _calculate-gui-title - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2010 read -r -d '' -a files < <(ls -lBhpa "$1" | grep "^d" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) - exit_status=$? FILE="$1/$SELECTED" elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --dselect "$1"/ 14 48) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --directory --filename "$1"/ ) - exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getexistingdirectory "$1"/ ) - exit_status=$? else read -erp "${FOLDER_SYMBOL}You need to select a folder in $1/. Hit enter to browse this folder" @@ -1499,12 +1475,12 @@ function folderpicker() { ls -lBhpa "$1" | grep "^d" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED - exit_status=$? # TODO: Add validation - handle empty SELECTED or parent directory (..) FILE=$1/$SELECTED fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1552,23 +1528,19 @@ function datepicker() { MONTH=0 YEAR=0 - local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then local SYMBOL=$CALENDAR_SYMBOL STANDARD_DATE=$(inputbox "Input Date (DD/MM/YYYY)" "$NOW") elif [ "$INTERFACE" == "dialog" ]; then STANDARD_DATE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --calendar "${CALENDAR_SYMBOL}Choose Date" 0 40) - exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT_DATE=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --calendar "Select Date") - exit_status=$? MONTH=$(echo "$INPUT_DATE" | cut -d'/' -f1) DAY=$(echo "$INPUT_DATE" | cut -d'/' -f2) YEAR=$(echo "$INPUT_DATE" | cut -d'/' -f3) STANDARD_DATE="$DAY/$MONTH/$YEAR" elif [ "$INTERFACE" == "kdialog" ]; then INPUT_DATE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --calendar "Select Date") - exit_status=$? TEXT_MONTH=$(echo "$INPUT_DATE" | cut -d' ' -f2) if [ "$TEXT_MONTH" == "Jan" ]; then MONTH=1 @@ -1601,8 +1573,8 @@ function datepicker() { STANDARD_DATE="$DAY/$MONTH/$YEAR" else read ${NO_READ_DEFAULT+-i "$NOW"} -rep "${CALENDAR_SYMBOL}${bold}Date (DD/MM/YYYY): ${normal}" STANDARD_DATE - exit_status=$? fi + local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then From f4306e876c2388f9a06f36f40ede62f2b448f00d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:50:03 +0000 Subject: [PATCH 06/10] Reduce redundancy in pause function - Removed repetitive if-then-return-else-exit chains - Consolidated to single exit status check after if-else chain - Changed CLI branch to use compact test expression [[ "${answer,,}" != "q" ]] - Reduced function from ~42 lines to 30 lines - Maintains all functionality while being more maintainable Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/script-dialog.sh b/script-dialog.sh index 9bcd2c7..c185b52 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -593,36 +593,23 @@ function pause() { _calculate-tui-size if [ "$INTERFACE" == "whiptail" ]; then - if whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-button "Continue" --no-button "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS"; then - return 0 - else - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" - fi + whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-button "Continue" --no-button "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS" elif [ "$INTERFACE" == "dialog" ]; then - if dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-label "Continue" --no-label "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS"; then - return 0 - else - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" - fi + dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --yes-label "Continue" --no-label "Quit" --yesno "${QUESTION_SYMBOL}$MESSAGE" "$RECMD_LINES" "$RECMD_COLS" elif [ "$INTERFACE" == "zenity" ]; then - if zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --question --text "$MESSAGE" --ok-label="Continue" --cancel-label="Quit"; then - return 0 - else - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" - fi + zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --question --text "$MESSAGE" --ok-label="Continue" --cancel-label="Quit" elif [ "$INTERFACE" == "kdialog" ]; then - if kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --yes-label "Continue" --no-label "Quit" --yesno "$MESSAGE"; then - return 0 - else - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" - fi + kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --yes-label "Continue" --no-label "Quit" --yesno "$MESSAGE" else echo -ne "${QUESTION_SYMBOL}${bold}$MESSAGE (press Enter to continue, q to quit): ${normal}" 3>&1 1>&2 2>&3 read -r answer - if [[ "${answer,,}" == "q" ]]; then - exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" - fi - return 0 + [[ "${answer,,}" != "q" ]] + fi + local exit_status=$? + + # Exit script if user chose to quit + if [ $exit_status -ne 0 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" fi } From c0d4103378c1dc09e83299200f3591a9dbf9f5b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:09:37 +0000 Subject: [PATCH 07/10] Fix cancel detection by restoring exit status capture in branches The previous refactoring broke cancel detection by moving exit_status=$? after the fi statement. In bash, $? after fi contains the exit status of the if statement itself (0), not the command inside. Fixed by restoring immediate exit status capture after each command in each branch for: - inputbox - password - display-file - checklist - filepicker - folderpicker - datepicker This reverts the problematic part of commit 74e7afd while keeping the good parts (pause function improvements and other functions that don't use command substitution). Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/script-dialog.sh b/script-dialog.sh index c185b52..12594d6 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -707,18 +707,23 @@ function inputbox() { TEST_STRING="${QUESTION_SYMBOL} $1" _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then INPUT=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then INPUT=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --inputbox "${SYMBOL} $1" "$RECMD_LINES" "$RECMD_COLS" "$2" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT="$(zenity --entry --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --text="$1" --entry-text "$2")" + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then INPUT="$(kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --inputbox "$1" "$2")" + exit_status=$? else read ${NO_READ_DEFAULT+-i "$2"} -rep "${SYMBOL}${bold}$1: ${normal}" INPUT + exit_status=$? fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -847,18 +852,23 @@ function password() { TEST_STRING="${PASSWORD_SYMBOL}$1" _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then PASSWORD=$(whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then PASSWORD=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --passwordbox "$1" "$RECMD_LINES" "$RECMD_COLS" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then PASSWORD=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --password) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then PASSWORD=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --password "$1") + exit_status=$? else read -srp "${PASSWORD_SYMBOL}${bold}$ACTIVITY: ${normal}" PASSWORD + exit_status=$? fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -902,18 +912,23 @@ function display-file() { local height=${3-${ZENITY_HEIGHT-640}} _calculate-tui-size + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --textbox "$1" "$RECMD_LINES" "$RECMD_COLS" + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="$height" --width="$width" --text-info --filename="$1" + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --textbox "$1" "$width" "$height" + exit_status=$? else less "$1" 3>&1 1>&2 2>&3 + exit_status=$? fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -983,10 +998,13 @@ function checklist() { fi fi + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then mapfile -t CHOSEN_ITEMS < <( whiptail --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? elif [ "$INTERFACE" == "dialog" ]; then IFS=$'\n' read -r -d '' -a CHOSEN_LIST < <( dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" $([ "$RECMD_SCROLL" == true ] && echo "--scrolltext") --separate-output --checklist "${QUESTION_SYMBOL}$TEXT" $RECMD_LINES $RECMD_COLS "$NUM_OPTIONS" "$@" 3>&1 1>&2 2>&3) + exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -1009,6 +1027,7 @@ function checklist() { shift done IFS=$'|' read -r -d '' -a CHOSEN_LIST < <( zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" --height="${ZENITY_HEIGHT-512}" ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --list --text "$TEXT" --checklist --column "" --column "Value" --column "Description" "${OPTIONS[@]}" ) + exit_status=$? local CHOSEN_ITEMS=() for value in "${CHOSEN_LIST[@]}" @@ -1018,6 +1037,7 @@ function checklist() { elif [ "$INTERFACE" == "kdialog" ]; then mapfile -t CHOSEN_ITEMS < <( kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --checklist "$TEXT" "$@") + exit_status=$? else printf "%s\n $TEXT:\n" "${QUESTION_SYMBOL}$ACTIVITY" 3>&1 1>&2 2>&3 local CHOSEN_ITEMS=() @@ -1032,7 +1052,6 @@ function checklist() { shift done fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1368,34 +1387,39 @@ function filepicker() { fi fi _calculate-gui-title + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2012 read -r -d '' -a files < <(ls -lBhpa "$1" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) + exit_status=$? FILE="$1/$SELECTED" elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --fselect "$1"/ 14 48) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --filename "$1"/ ) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then if [ "$2" == "save" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getsavefilename "$1"/ ) else #elif [ "$2" == "open" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getopenfilename "$1"/ ) fi + exit_status=$? else read -erp "${DOCUMENT_SYMBOL}You need to $2 a file in $1/. Hit enter to browse this folder" ls -lBhpa "$1" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED + exit_status=$? # TODO: Add validation - handle empty SELECTED or when SELECTED is a folder FILE=$1/$SELECTED fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1443,18 +1467,23 @@ function folderpicker() { GUI_ICON=$XDG_ICO_FOLDER_OPEN fi _calculate-gui-title + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then # shellcheck disable=SC2010 read -r -d '' -a files < <(ls -lBhpa "$1" | grep "^d" | awk -F ' ' ' { print $9 " " $5 } ') SELECTED=$(whiptail --clear --backtitle "$APP_NAME" --title "$GUI_TITLE" --cancel-button Cancel --ok-button Select --menu "$ACTIVITY" $((8+RECMD_LINES)) $((6+RECMD_COLS)) $RECMD_LINES "${files[@]}" 3>&1 1>&2 2>&3) + exit_status=$? FILE="$1/$SELECTED" elif [ "$INTERFACE" == "dialog" ]; then FILE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --dselect "$1"/ 14 48) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then FILE=$(zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --file-selection --directory --filename "$1"/ ) + exit_status=$? elif [ "$INTERFACE" == "kdialog" ]; then FILE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --getexistingdirectory "$1"/ ) + exit_status=$? else read -erp "${FOLDER_SYMBOL}You need to select a folder in $1/. Hit enter to browse this folder" @@ -1462,12 +1491,12 @@ function folderpicker() { ls -lBhpa "$1" | grep "^d" 3>&1 1>&2 2>&3 #| less read -erp "Enter name of file to $2 in $1/: " SELECTED + exit_status=$? # TODO: Add validation - handle empty SELECTED or parent directory (..) FILE=$1/$SELECTED fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then @@ -1515,19 +1544,23 @@ function datepicker() { MONTH=0 YEAR=0 + local exit_status=0 if [ "$INTERFACE" == "whiptail" ]; then local SYMBOL=$CALENDAR_SYMBOL STANDARD_DATE=$(inputbox "Input Date (DD/MM/YYYY)" "$NOW") elif [ "$INTERFACE" == "dialog" ]; then STANDARD_DATE=$(dialog --clear --backtitle "$APP_NAME" --title "$ACTIVITY" --stdout --calendar "${CALENDAR_SYMBOL}Choose Date" 0 40) + exit_status=$? elif [ "$INTERFACE" == "zenity" ]; then INPUT_DATE=$(zenity --title="$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --calendar "Select Date") + exit_status=$? MONTH=$(echo "$INPUT_DATE" | cut -d'/' -f1) DAY=$(echo "$INPUT_DATE" | cut -d'/' -f2) YEAR=$(echo "$INPUT_DATE" | cut -d'/' -f3) STANDARD_DATE="$DAY/$MONTH/$YEAR" elif [ "$INTERFACE" == "kdialog" ]; then INPUT_DATE=$(kdialog --title="$GUI_TITLE" --icon "$GUI_ICON" --calendar "Select Date") + exit_status=$? TEXT_MONTH=$(echo "$INPUT_DATE" | cut -d' ' -f2) if [ "$TEXT_MONTH" == "Jan" ]; then MONTH=1 @@ -1560,8 +1593,8 @@ function datepicker() { STANDARD_DATE="$DAY/$MONTH/$YEAR" else read ${NO_READ_DEFAULT+-i "$NOW"} -rep "${CALENDAR_SYMBOL}${bold}Date (DD/MM/YYYY): ${normal}" STANDARD_DATE + exit_status=$? fi - local exit_status=$? # Exit script if dialog was cancelled if [ $exit_status -ne 0 ]; then From c44e9fb97f8b5b12eefb5ca7e16de743016fed73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:26:04 +0000 Subject: [PATCH 08/10] Change default cancel exit code to 124 and document in test.sh - Changed SCRIPT_DIALOG_CANCEL_EXIT_CODE default from 1 to 124 - Updated README.md to reflect new default value - Added documentation to test.sh explaining cancellation behavior - Exit code 124 is more distinctive and follows timeout convention - All shellcheck tests pass Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- README.md | 2 +- script-dialog.sh | 2 +- test.sh | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c46a0a..a1e6a23 100755 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Global Variables | **INTERFACE** | Override or Detected | the GUI or TUI to use | | **GUI** | Detected | Whether the interface is a GUI not TUI | | **DETECTED_DESKTOP** | Detected | Desktop in use | -| **SCRIPT_DIALOG_CANCEL_EXIT_CODE** | Optional override | Exit code when a dialog is cancelled (default: 1) | +| **SCRIPT_DIALOG_CANCEL_EXIT_CODE** | Optional override | Exit code when a dialog is cancelled (default: 124) | | **NOCOLORS** | Optional override | disables otherwise-detected use of colored/bolded text basic CLI | | **NOSYMBOLS** | Optional override |disables otherwise-detected use of unicode symbols in TUIs | | **ZENITY_HEIGHT** | Optional override | height of zenity dialogs | diff --git a/script-dialog.sh b/script-dialog.sh index 12594d6..70b42e4 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -150,7 +150,7 @@ fi # Set default cancel exit code if not already set if [ -z "${SCRIPT_DIALOG_CANCEL_EXIT_CODE+x}" ]; then - SCRIPT_DIALOG_CANCEL_EXIT_CODE=1 + SCRIPT_DIALOG_CANCEL_EXIT_CODE=124 fi diff --git a/test.sh b/test.sh index b9131a5..37097b7 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,9 @@ #!/usr/bin/env bash +# Test script for script-dialog library +# NOTE: Cancelling any dialog will exit this script with the configured +# SCRIPT_DIALOG_CANCEL_EXIT_CODE (default: 124). This is by design to +# demonstrate the cancelability feature. + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #GUI=false; terminal=false # force relaunching as if launching from GUI without a GUI interface installed, but only do this for testing From e3f968a2ddaa7061337bee2384446a25f8cf094e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:35:53 +0000 Subject: [PATCH 09/10] Add exit propagation for command substitutions in test.sh When functions are called in command substitution $(...), they run in a subshell. If the function calls exit inside the subshell, it only exits the subshell, not the parent script. Fixed by adding || exit "$?" after all command substitutions that call dialog functions: - inputbox - datepicker - checklist - radiolist - filepicker - folderpicker This ensures cancellation properly exits test.sh with the correct exit code. Updated documentation to explain the pattern. Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- test.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test.sh b/test.sh index 37097b7..a6e2de8 100755 --- a/test.sh +++ b/test.sh @@ -3,6 +3,10 @@ # NOTE: Cancelling any dialog will exit this script with the configured # SCRIPT_DIALOG_CANCEL_EXIT_CODE (default: 124). This is by design to # demonstrate the cancelability feature. +# +# Functions that return values via echo (used with command substitution) +# require the '|| exit "$?"' pattern to propagate cancellation from the +# subshell to the parent script. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -32,7 +36,7 @@ else fi ACTIVITY="Name" -NAME=$(inputbox "What's your name?" "$USER") +NAME=$(inputbox "What's your name?" "$USER") || exit "$?" message-info "Nice to meet you, $NAME" @@ -60,7 +64,7 @@ userandpassword S_USER S_PASS "$SUGGESTED_USERNAME" message-info $"So, that was:\n user: $S_USER\n password: $S_PASS" ACTIVITY="Enter Birthday" -ANSWER=$(datepicker) +ANSWER=$(datepicker) || exit "$?" message-info "Cool, it's on $ANSWER" @@ -69,7 +73,7 @@ CONFIG_OPTS=$( checklist "Select the appropriate network options for this comput "NET OUT" "Allow connections to other hosts" ON \ "NET_IN" "Allow connections from other hosts" OFF \ "LOCAL_MOUNT" "Allow mounting of local drives" OFF \ - "REMOTE_MOUNT" "Allow mounting of remote drives" OFF ) + "REMOTE_MOUNT" "Allow mounting of remote drives" OFF ) || exit "$?" message-info "So you chose to enable: ${CONFIG_OPTS[*]}" @@ -78,18 +82,18 @@ ANSWER=$(radiolist "Favorite Primary Color? " 4 \ "blue" "Blue" OFF \ "yellow" "Yellow" OFF \ "green" "Green" ON \ - "red" "Red" OFF ) + "red" "Red" OFF ) || exit "$?" message-info "So you like $ANSWER, neat." -ANSWER=$(filepicker "$HOME" "open") +ANSWER=$(filepicker "$HOME" "open") || exit "$?" message-info "File selected was ${ANSWER[*]}" ACTIVITY="Test Script" display-file "$0" -ANSWER=$(folderpicker "$HOME") +ANSWER=$(folderpicker "$HOME") || exit "$?" message-info "Folder selected was ${ANSWER[*]}" From de5ec685542f4a927b80c32a2cd67d69c5113d17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:42:37 +0000 Subject: [PATCH 10/10] Add cancellation checking for yesno function (kdialog and zenity) - Added exit code checking in zenity branch of yesno function - Added exit code checking in kdialog branch of yesno function - When user cancels (closes window or clicks cancel), exit code > 1 - Script now exits with SCRIPT_DIALOG_CANCEL_EXIT_CODE (124) - Zenity returns 5 for timeout, -1/255 for cancel/close - KDialog returns values > 1 for cancel/error - Added documentation comments explaining the behavior Co-authored-by: lunarcloud <1565970+lunarcloud@users.noreply.github.com> --- script-dialog.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script-dialog.sh b/script-dialog.sh index 70b42e4..a2a519f 100755 --- a/script-dialog.sh +++ b/script-dialog.sh @@ -653,9 +653,17 @@ function yesno() { elif [ "$INTERFACE" == "zenity" ]; then zenity --title "$GUI_TITLE" $ZENITY_ICON_ARG "$GUI_ICON" ${ZENITY_HEIGHT+--height=$ZENITY_HEIGHT} ${ZENITY_WIDTH+--width=$ZENITY_WIDTH} --question --text "$1" answer=$? + # Exit if cancelled (zenity returns 5 for timeout, -1/255 for cancel/close) + if [ $answer -gt 1 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi elif [ "$INTERFACE" == "kdialog" ]; then kdialog --title "$GUI_TITLE" --icon "$GUI_ICON" --yesno "$1" answer=$? + # Exit if cancelled (kdialog returns values > 1 for cancel/error) + if [ $answer -gt 1 ]; then + exit "$SCRIPT_DIALOG_CANCEL_EXIT_CODE" + fi else echo -ne "${QUESTION_SYMBOL}${bold}$1 (y/n): ${normal}" 3>&1 1>&2 2>&3 read -r answer