diff --git a/redesign/nvim.sh b/redesign/nvim.sh new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/redesign/nvim.sh @@ -0,0 +1 @@ + diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100755 index 0000000..936c76c --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,227 @@ +#!/bin/bash + +# ============================================================================= +# Backup Tool — Argument-Driven rsync Wrapper +# ============================================================================= +# Description: +# A modular backup script using rsync with smart CLI parsing +# Supports: local/system/user backups, remote SSH push/pull, link-dest snapshots +# ============================================================================= + +# Load CLI parser (must be in same or relative path) +source ./lib/cli/arg_parser.sh + +# Flag check utility +get_flag() { + local name="$1" + [[ -n "${FLAG_GROUPS[$name]}" && "${FLAG_GROUPS[$name]}" == "true" ]] +} + +# ---------------------------------------------------------------------------- +# Define valid flags +# ---------------------------------------------------------------------------- +VALID_FLAGS=( + "source" "target" "dry-run" "verbose" "compress" "system" + "ssh-send" "ssh-receive" "link" "ntfs" "name" "exclude" +) + +# ---------------------------------------------------------------------------- +# Default values and constants +# ---------------------------------------------------------------------------- +SYSTEM_EXCLUDES=( + "/dev/*" "/proc/*" "/sys/*" "/tmp/*" "/run/*" "/mnt/*" "/media/*" + "swapfile" "lost+found" ".cache" "Downloads" ".ecryptfs" +) + +# ---------------------------------------------------------------------------- +# Parse CLI args +# ---------------------------------------------------------------------------- +parse_grouped_args "$@" + +# ---------------------------------------------------------------------------- +# Validate flags +# ---------------------------------------------------------------------------- +for key in "${!FLAG_GROUPS[@]}"; do + found=false + for valid in "${VALID_FLAGS[@]}"; do + [[ "$key" == "$valid" ]] && found=true && break + done + if [[ "$found" == false ]]; then + echo "Error: Unknown flag '--$key'" + exit 1 + fi +done + +# ---------------------------------------------------------------------------- +# Extract values from flags +# ---------------------------------------------------------------------------- +IFS=' ' read -r -a SOURCE <<<"${FLAG_GROUPS["source"]}" +IFS=' ' read -r -a TARGET <<<"${FLAG_GROUPS["target"]}" +IFS=' ' read -r -a EXCLUDES <<<"${FLAG_GROUPS["exclude"]}" + +BACKUP_NAME="" +[[ -n "${FLAG_GROUPS["name"]}" ]] && BACKUP_NAME="${FLAG_GROUPS["name"]}" + +DRY_RUN=false +VERBOSE=false +COMPRESS=false +SYSTEM=false +NTFS=false + +get_flag "dry-run" && DRY_RUN=true +get_flag "verbose" && VERBOSE=true +get_flag "compress" && COMPRESS=true +get_flag "system" && SYSTEM=true +get_flag "ntfs" && NTFS=true + +# ---------------------------------------------------------------------------- +# Required argument checks +# ---------------------------------------------------------------------------- +if [[ -z "${SOURCE[0]}" || -z "${TARGET[0]}" ]]; then + echo "Error: --source and --target are required." + exit 1 +elif [[ ${#SOURCE[@]} -gt 1 || ${#TARGET[@]} -gt 1 ]]; then + echo "Error: --source and --target must be single paths only." + exit 1 +fi + +# ---------------------------------------------------------------------------- +# Backup naming (target path suffix) +# ---------------------------------------------------------------------------- +if [[ -n "$BACKUP_NAME" ]]; then + TARGET_PATH="${TARGET[0]%/}/$BACKUP_NAME" +else + TARGET_PATH="${TARGET[0]}" +fi + +# ---------------------------------------------------------------------------- +# SSH configuration (push or pull) +# ---------------------------------------------------------------------------- +SSH_SEND="${FLAG_GROUPS["ssh-send"]}" +SSH_RECEIVE="${FLAG_GROUPS["ssh-receive"]}" + +if [[ -n "$SSH_SEND" && -n "$SSH_RECEIVE" ]]; then + echo "Error: Only one of --ssh-send or --ssh-receive can be set." + exit 1 +fi + +if [[ -n "$SSH_SEND" && ! "$SSH_SEND" =~ ^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+$ ]]; then + echo "Error: --ssh-send must be in user@host format." + exit 1 +fi + +if [[ -n "$SSH_RECEIVE" && ! "$SSH_RECEIVE" =~ ^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+$ ]]; then + echo "Error: --ssh-receive must be in user@host format." + exit 1 +fi + +if [[ -n "$SSH_SEND" ]]; then + TARGET_PATH="$SSH_SEND:$TARGET_PATH" +elif [[ -n "$SSH_RECEIVE" ]]; then + SOURCE[0]="$SSH_RECEIVE:${SOURCE[0]}" +fi + +# ---------------------------------------------------------------------------- +# Link-dest configuration (incremental snapshots) +# ---------------------------------------------------------------------------- +LINK_DIR="" +if [[ -n "${FLAG_GROUPS["link"]}" ]]; then + LINK_DIR="${FLAG_GROUPS["link"]}" + if [[ ! "$LINK_DIR" =~ ^/.* ]]; then + echo "Error: --link must be an absolute path." + exit 1 + fi +fi + +# ---------------------------------------------------------------------------- +# System-specific excludes +# ---------------------------------------------------------------------------- +if [[ "$SYSTEM" == true ]]; then + EXCLUDES+=("${SYSTEM_EXCLUDES[@]}") +fi + +# ---------------------------------------------------------------------------- +# Build rsync command +# ---------------------------------------------------------------------------- +CMD=("rsync" "-a" "--delete") + +if [[ "$NTFS" == false ]]; then + CMD+=("-A" "-X") +fi + +[[ "$SYSTEM" == true ]] && CMD+=("-H") +CMD+=("-P") +CMD+=("--delete-excluded") +$VERBOSE && CMD+=("-v") +$DRY_RUN && CMD+=("--dry-run") +$COMPRESS && CMD+=("-z") +[[ -n "$LINK_DIR" ]] && CMD+=("--link-dest=$LINK_DIR") + +# Add excludes here +if [[ ${#EXCLUDES[@]} -gt 0 ]]; then + for ex in "${EXCLUDES[@]}"; do + CMD+=("--exclude=$ex") + done +fi + +CMD+=("${SOURCE[0]}" "$TARGET_PATH") + +# ---------------------------------------------------------------------------- +# Summary Output +# ---------------------------------------------------------------------------- +echo "┌──────────────────────────────────────────┐" +echo "│ Starting Backup Process │" +echo "└──────────────────────────────────────────┘" +echo + +echo "Directories:" +echo " ├─ Source : ${SOURCE[0]}" +echo " └─ Target : $TARGET_PATH" +echo + +echo "Mode:" +if [[ "$SYSTEM" == true ]]; then + echo " └─ System Backup : Default excludes applied" +else + echo " └─ User Backup : No system-specific excludes" +fi +echo + +echo "Configuration:" +flag_printed=false +[[ "$DRY_RUN" == true ]] && echo " ├─ Dry-run : enabled" && flag_printed=true +[[ "$VERBOSE" == true ]] && echo "${flag_printed:+ ├─ }Verbose : enabled" && flag_printed=true +[[ "$COMPRESS" == true ]] && echo " └─ Compression : enabled" +[[ "$flag_printed" == false ]] && echo " └─ No Configuration set" +echo + +echo "Snapshot:" +echo " ├─ Backup Name : ${BACKUP_NAME:-none}" +echo " └─ Link From : ${LINK_DIR:-none}" +echo + +echo "Excludes:" +if [[ ${#EXCLUDES[@]} -gt 0 ]]; then + last_index=$((${#EXCLUDES[@]} - 1)) + for i in "${!EXCLUDES[@]}"; do + prefix=$([[ "$i" -eq "$last_index" ]] && echo " └─" || echo " ├─") + echo "$prefix ${EXCLUDES[$i]}" + done +else + echo " └─ No Excludes Set" +fi + +echo +echo "┌──────────────────────────────────────────┐" +echo "│ Executing Rsync Process │" +echo "└──────────────────────────────────────────┘" +echo "Command : ${CMD[*]}" +echo + +# Uncomment to execute +"${CMD[@]}" +echo + +echo "┌──────────────────────────────────────────┐" +echo "│ Backup Process Completed │" +echo "└──────────────────────────────────────────┘" diff --git a/scripts/lib/cli/arg_parser.sh b/scripts/lib/cli/arg_parser.sh new file mode 100755 index 0000000..720b15f --- /dev/null +++ b/scripts/lib/cli/arg_parser.sh @@ -0,0 +1,184 @@ +#!/bin/bash + +# ============================================================================= +# Bash CLI Argument Parser — Greedy Grouping, Flag-Agnostic +# ============================================================================= +# +# Overview: +# Parses command-line arguments into key/value groups. +# Every flag (long or short) collects values until the next flag. +# You do NOT need to define which flags expect values. +# Short flags can be grouped (-abc) and are treated as booleans. +# +# Supported input formats: +# --key value1 value2 → key = [value1, value2] +# --key=value → key = value +# -k value → k = value +# -k=value → k = value +# -abc → a=true, b=true, c=true +# +# Not supported: +# -kvalue → this is parsed as combined short flags (k, v, a…) +# Global positionals not linked to any flag (can be added separately if needed) +# +# Example usage: +# ./arg_analyse.sh --mode fast --files input1.txt input2.txt -vx --output=result.txt +# +# Output: +# --mode: +# [0]: fast +# --files: +# [0]: input1.txt +# [1]: input2.txt +# --v: +# [boolean flag] +# --x: +# [boolean flag] +# --output: +# [0]: result.txt +# +# ============================================================================= + +# STORAGE: key = flag name, value = string or boolean +declare -A FLAG_GROUPS + +# ----------------------------------------------------------------------------- +# FUNCTION: parse_grouped_args +# Description: +# Parses input arguments and fills the FLAG_GROUPS associative array. +# ----------------------------------------------------------------------------- +parse_grouped_args() { + local current_flag="" + local -a current_values=() + + while [[ $# -gt 0 ]]; do + local token="$1" + + # ------------------------------------------------------------------------- + # Case 1: --key=value or -k=value (explicit assignment form) + # This handles both long and short flags passed as --flag=value or -f=value + # ------------------------------------------------------------------------- + # Breakdown of regex: + # ^ → start of the string + # --? → match one or two dashes (e.g. "-" or "--") + # ([^=]+) → capture group 1: one or more characters that are NOT "=" + # → this becomes the flag name (e.g. "output") + # = → literal equals sign + # (.*) → capture group 2: any number of characters (can be empty) + # → this becomes the first value for the flag + # + # Examples: + # --file=abc.txt → key = "file", val = "abc.txt" + # -x=1 → key = "x", val = "1" + if [[ "$token" =~ ^--?([^=]+)=(.*)$ ]]; then + # Before processing this --flag=value, we must flush any previous group + if [[ -n "$current_flag" ]]; then + if [[ ${#current_values[@]} -eq 0 ]]; then + FLAG_GROUPS["$current_flag"]="true" + else + FLAG_GROUPS["$current_flag"]="${current_values[*]}" + fi + fi + + # Capture the key and value from the current --key=value or -k=value + local key="${BASH_REMATCH[1]}" # group 1 from regex (flag name) + local val="${BASH_REMATCH[2]}" # group 2 from regex (value after =) + + # Set current flag context to continue collecting values if any follow + current_flag="$key" + current_values=("$val") # start a new value group with first value from assignment + + # ------------------------------------------------------------------------- + # Case 2: --long flag (starts a new group) + # ------------------------------------------------------------------------- + elif [[ "$token" == --* ]]; then + # Save previous flag (if any) + if [[ -n "$current_flag" ]]; then + if [[ ${#current_values[@]} -eq 0 ]]; then + FLAG_GROUPS["$current_flag"]="true" + else + FLAG_GROUPS["$current_flag"]="${current_values[*]}" + fi + fi + current_flag="${token#--}" # Remove leading "--" + current_values=() + + # ------------------------------------------------------------------------- + # Case 3: -short or -grouped flags (-x or -abc) + # ------------------------------------------------------------------------- + elif [[ "$token" == -* && "$token" != "--" ]]; then + # Save previous group + if [[ -n "$current_flag" ]]; then + if [[ ${#current_values[@]} -eq 0 ]]; then + FLAG_GROUPS["$current_flag"]="true" + else + FLAG_GROUPS["$current_flag"]="${current_values[*]}" + fi + current_flag="" + current_values=() + fi + + local chars="${token#-}" + + if [[ "${#chars}" -gt 1 ]]; then + # -abc → treat each letter as a boolean flag + for ((i = 0; i < ${#chars}; i++)); do + FLAG_GROUPS["${chars:i:1}"]="true" + done + else + # Single short flag (-x) may collect values + current_flag="$chars" + current_values=() + fi + + # ------------------------------------------------------------------------- + # Case 4: Value (non-flag) + # ------------------------------------------------------------------------- + else + current_values+=("$token") + fi + + shift + done + + # Final flush (for last group) + if [[ -n "$current_flag" ]]; then + if [[ ${#current_values[@]} -eq 0 ]]; then + FLAG_GROUPS["$current_flag"]="true" + else + FLAG_GROUPS["$current_flag"]="${current_values[*]}" + fi + fi +} + +# ----------------------------------------------------------------------------- +# FUNCTION: print_grouped_args +# Description: +# Pretty-prints the FLAG_GROUPS key/value structure. +# ----------------------------------------------------------------------------- +print_grouped_args() { + echo "Parsed Arguments:" + for key in "${!FLAG_GROUPS[@]}"; do + IFS=' ' read -r -a values <<<"${FLAG_GROUPS[$key]}" + echo "--$key:" + if [[ "${FLAG_GROUPS[$key]}" == "true" ]]; then + echo " [boolean flag]" + elif [[ ${#values[@]} -eq 0 ]]; then + echo " [empty]" + else + for i in "${!values[@]}"; do + echo " [$i]: ${values[$i]}" + done + fi + done +} + +# ----------------------------------------------------------------------------- +# TEST CASE (for development) +# Comment this out to use real CLI args +# ----------------------------------------------------------------------------- +#set -- --name Kerem yollu siki --age=33 --flag -vx --output result.txt -t alpha beta + +# Run parser and print results +#parse_grouped_args "$@" +#print_grouped_args diff --git a/scripts/colors.sh b/scripts/lib/cli/colors.sh similarity index 67% rename from scripts/colors.sh rename to scripts/lib/cli/colors.sh index a43bd7f..e3a29d7 100755 --- a/scripts/colors.sh +++ b/scripts/lib/cli/colors.sh @@ -1,6 +1,6 @@ #!/bin/bash -COLORS_SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +COLORS_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) black='30' red='31' @@ -35,14 +35,12 @@ themeInstall="$yellow" themeSkip="$lightGreen" themeComment="$lightGreen" -color_start() -{ - local -n ref1=$1 - printf $colorDelimiterBegin$ref1$colorDelimiterEnd +color_start() { + local -n ref1=$1 + printf $colorDelimiterBegin$ref1$colorDelimiterEnd } -color_stop() -{ - printf $colorDelimiterBegin$fontNormal$colorDelimiterEnd - printf $colorStop +color_stop() { + printf $colorDelimiterBegin$fontNormal$colorDelimiterEnd + printf $colorStop } diff --git a/scripts/graphics.sh b/scripts/lib/cli/graphics.sh similarity index 100% rename from scripts/graphics.sh rename to scripts/lib/cli/graphics.sh diff --git a/scripts/wsl/wsl/windowsTools/goto.sh b/scripts/wsl/goto.sh similarity index 100% rename from scripts/wsl/wsl/windowsTools/goto.sh rename to scripts/wsl/goto.sh diff --git a/scripts/wsl/wsl/windowsTools/mail.sh b/scripts/wsl/mail.sh similarity index 100% rename from scripts/wsl/wsl/windowsTools/mail.sh rename to scripts/wsl/mail.sh diff --git a/scripts/wsl/wsl/windowsTools/onenote.sh b/scripts/wsl/onenote.sh similarity index 100% rename from scripts/wsl/wsl/windowsTools/onenote.sh rename to scripts/wsl/onenote.sh diff --git a/scripts/wsl/wsl/windowsTools/windowsOpen.sh b/scripts/wsl/windowsOpen.sh similarity index 100% rename from scripts/wsl/wsl/windowsTools/windowsOpen.sh rename to scripts/wsl/windowsOpen.sh diff --git a/tools/backup.sh b/tools/backup.sh deleted file mode 100755 index 513be1f..0000000 --- a/tools/backup.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash - -#sudo apt-get install -y rsync -OK=0 -response="na" - -echo "#######################################" -echo "What kind of backup do you want to do:" -echo "Create new from ssh -> ns " -echo "Create new from local -> nl " -echo "Create itteration from ssh -> is" -echo "Create itteration from local -> il" -echo "Restore from local to local -> rl" -echo "#######################################" - - # loop until we get a good answer and break out - while [ "$OK" = 0 ] - do - read -p "Please choose : ns OR nl OR is OR il OR rl OR quit: " input - # Test the input - if [ "$input" = "ns" ] || [ "$input" = "nS" ] || [ "$input" = "Ns" ] || [ "$input" = "NS" ] - then - response="ns" - OK=1 - elif [ "$input" = "nl" ] || [ "$input" = "nL" ] || [ "$input" = "Nl" ] || [ "$input" = "NL" ] - then - response="nl" - OK=1 - elif [ "$input" = "is" ] || [ "$input" = "iS" ] || [ "$input" = "iS" ] || [ "$input" = "IS" ] - then - response="is" - OK=1 - elif [ "$input" = "il" ] || [ "$input" = "iL" ] || [ "$input" = "Il" ] || [ "$input" = "IL" ] - then - response="il" - OK=1 - elif [ "$input" = "rl" ] || [ "$input" = "rL" ] || [ "$input" = "Rl" ] || [ "$input" = "RL" ] - then - response="rl" - OK=1 - elif [ "$input" = "help" ] || [ "$input" = "h" ] - then - echo "What kinf of backup do you want to do:" - echo "Create new from ssh -> ns " - echo "Create new from local -> nl " - echo "Create itteration from ssh -> is" - echo "Create itteration from local -> il" - echo "Restore from local to local -> rl" - elif [ "$input" = "q" ] || [ "$input" = "quit" ] - then - exit - else - # Invalid input - echo "INPUT ERROR: Must be ns OR nl OR is OR il OR rl OR quit: Please try again." - fi - done - - -if [ "$response" = "ns" ]; -then - read -p "Source ip address: " -i -e sourceIp - read -p "Source user name: " -i -e sourceUser - read -p "Source directory: " -i "/" -e sourceDir - read -p "Target directory: " -i "/" -e targetDir - sudo rsync -aAXP -e ssh --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","swapfile","lost+found",".cache","Downloads",".ecryptfs"} $sourceUser@$sourceIp:$sourceDir $targetDir - -elif [ "$response" = "nl" ]; -then - read -p "Source directory: " -i "/" -e sourceDir - read -p "Target directory: " -i "/" -e targetDir - sudo rsync -aAXP --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","swapfile","lost+found",".cache","Downloads",".ecryptfs"} $sourceDir $targetDir - -elif [ "$response" = "is" ]; -then - read -p "Source ip address: " -i -e sourceIp - read -p "Source user name: " -i -e sourceUser - read -p "Source directory: " -i "/" -e sourceDir - read -p "Target current directory: " -i "/" -e linkDir - read -p "Target itteration directory: " -i "/" -e targetDir - sudo rsync -aAXPH -e ssh --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","swapfile","lost+found",".cache","Downloads",".ecryptfs"} --link-dest=$linkDir $sourceUser@$sourceIp:$sourceDir $targetDir - -elif [ "$response" = "il" ]; -then - read -p "Source directory: " -i "/" -e sourceDir - read -p "Target current directory: " -i "/" -e linkDir - read -p "Target itteration directory: " -i "/" -e targetDir - sudo rsync -aAXPH --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","swapfile","lost+found",".cache","Downloads",".ecryptfs"} --link-dest=$linkDir $sourceDir $targetDir - -elif [ "$response" = "rl" ]; -then - lsblk - read -p "Target Device to be mounted: " -i "/dev/" -e deviceName - read -p "Target device mount direcotry: " -i "/mnt/" -e targetDir - read -p "Backup directory: " -i "/" -e sourceDir - sudo mount $deviceName $targetDir - ls $targetDir - sudo rsync -aAXP --delete --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","swapfile","lost+found",".cache","Downloads",".ecryptfs"} $sourceDir $targetDir -fi -exit