#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKUP_DIR="${SCRIPT_DIR}/backups" # Function to sanitize a konsolerc file by stripping the [MainWindow] section sanitize_konsolerc() { local target_file="$1" if [ -f "${target_file}" ]; then local cleaned_file="${target_file}.clean" awk ' /^\[MainWindow\]/ { skip=1; next } /^\[.*\]/ { skip=0 } !skip { print } ' "${target_file}" > "${cleaned_file}" mv "${cleaned_file}" "${target_file}" fi } show_diff() { local source_dir="$1" echo "" echo "==================== Changes Preview (Diff) ====================" local changes_found=0 while IFS= read -r -d '' item; do rel_path="${item#"${source_dir}"/}" dest_item="${HOME}/${rel_path}" if [ -d "${item}" ]; then continue fi if [ ! -e "${dest_item}" ]; then changes_found=1 echo -e "\n\033[1;32m[NEW FILE]\033[0m ~/${rel_path}" else if [ "${rel_path}" = ".config/konsolerc" ]; then # Compare against sanitized version of local konsolerc so [MainWindow] geometry noise is ignored local temp_local_clean temp_local_clean="$(mktemp)" sanitize_konsolerc "${dest_item}" 2>/dev/null || true awk ' /^\[MainWindow\]/ { skip=1; next } /^\[.*\]/ { skip=0 } !skip { print } ' "${dest_item}" > "${temp_local_clean}" if ! cmp -s "${item}" "${temp_local_clean}"; then changes_found=1 echo -e "\n\033[1;33m[MODIFIED]\033[0m ~/${rel_path}" if command -v git &>/dev/null; then git diff --no-index --color "${temp_local_clean}" "${item}" || true else diff -u --color=always "${temp_local_clean}" "${item}" || true fi fi rm -f "${temp_local_clean}" else if ! cmp -s "${item}" "${dest_item}"; then changes_found=1 echo -e "\n\033[1;33m[MODIFIED]\033[0m ~/${rel_path}" if command -v git &>/dev/null; then git diff --no-index --color "${dest_item}" "${item}" || true else diff -u --color=always "${dest_item}" "${item}" || true fi fi fi fi done < <(find "${source_dir}" -type f -print0) if [ "${changes_found}" -eq 0 ]; then echo -e "\n\033[1;32mNo differences detected. Current configuration matches the backup exactly.\033[0m" fi echo "================================================================" echo "" } ARCHIVE_PATH="" # 1. Check positional argument if [ $# -ge 1 ] && [ -n "$1" ]; then ARCHIVE_PATH="$1" # 2. Check if piped via stdin elif [ ! -t 0 ]; then read -r ARCHIVE_PATH || true fi # 3. Interactive prompt / selection menu if no input given if [ -z "${ARCHIVE_PATH}" ]; then if [ ! -d "${BACKUP_DIR}" ]; then echo "Error: No backups directory found at ${BACKUP_DIR}" exit 1 fi mapfile -t AVAILABLE_BACKUPS < <(find "${BACKUP_DIR}" -maxdepth 1 -name "konsole_backup_*.zip" | sort -r) if [ "${#AVAILABLE_BACKUPS[@]}" -eq 0 ]; then echo "Error: No backup archives found in ${BACKUP_DIR}." exit 1 fi echo "Available Konsole backups:" for i in "${!AVAILABLE_BACKUPS[@]}"; do echo " [$((i + 1))] $(basename "${AVAILABLE_BACKUPS[$i]}")" done echo " [q] Quit" if [ -t 0 ]; then read -r -p "Select a backup to restore [1-${#AVAILABLE_BACKUPS[@]}]: " choice || true elif [ -c /dev/tty ] && [ -r /dev/tty ] && { : < /dev/tty; } 2>/dev/null; then read -r -p "Select a backup to restore [1-${#AVAILABLE_BACKUPS[@]}]: " choice < /dev/tty || true else read -r -p "Select a backup to restore [1-${#AVAILABLE_BACKUPS[@]}]: " choice || true fi if [[ "${choice}" =~ ^[0-9]+$ ]] && [ "${choice}" -ge 1 ] && [ "${choice}" -le "${#AVAILABLE_BACKUPS[@]}" ]; then ARCHIVE_PATH="${AVAILABLE_BACKUPS[$((choice - 1))]}" else echo "Aborted." exit 0 fi fi # Expand relative paths if [[ "${ARCHIVE_PATH}" != /* ]]; then if [ -f "${BACKUP_DIR}/${ARCHIVE_PATH}" ]; then ARCHIVE_PATH="${BACKUP_DIR}/${ARCHIVE_PATH}" elif [ -f "./${ARCHIVE_PATH}" ]; then ARCHIVE_PATH="$(pwd)/${ARCHIVE_PATH}" fi fi if [ ! -f "${ARCHIVE_PATH}" ]; then echo "Error: Backup file not found: ${ARCHIVE_PATH}" exit 1 fi echo "Selected backup archive: ${ARCHIVE_PATH}" # Extract to temporary staging directory TEMP_DIR="$(mktemp -d)" trap 'rm -rf "${TEMP_DIR}"' EXIT unzip -q -o "${ARCHIVE_PATH}" -d "${TEMP_DIR}" # Extra safety: Sanitize extracted konsolerc in case restoring an older archive if [ -f "${TEMP_DIR}/.config/konsolerc" ]; then sanitize_konsolerc "${TEMP_DIR}/.config/konsolerc" fi # Interactive confirmation loop (allows viewing diffs) while true; do echo "" echo "Konsole configuration files to be restored:" find "${TEMP_DIR}" -mindepth 1 -maxdepth 3 | while read -r f; do rel="${f#"${TEMP_DIR}"/}" echo " - ~/${rel}" done echo "" if [ -t 0 ]; then read -r -p "Choose action: [r] Restore / [d] View Diff / [c] Cancel: " action || true elif [ -c /dev/tty ] && [ -r /dev/tty ] && { : < /dev/tty; } 2>/dev/null; then read -r -p "Choose action: [r] Restore / [d] View Diff / [c] Cancel: " action < /dev/tty || true else read -r -p "Choose action: [r] Restore / [d] View Diff / [c] Cancel: " action || true fi case "${action,,}" in r|y|yes) echo "Restoring configuration files..." cp -a "${TEMP_DIR}/." "${HOME}/" echo -e "\033[1;32mKonsole configurations restored successfully from $(basename "${ARCHIVE_PATH}")!\033[0m" echo "Note: If Konsole is currently running, please restart it to apply all settings." break ;; d|diff) show_diff "${TEMP_DIR}" ;; c|q|n|no|cancel) echo "Restoration cancelled." exit 0 ;; *) echo "Invalid option. Please enter 'r', 'd', or 'c'." ;; esac done