@@ -336,8 +336,11 @@ run_preflight_checks() {
336
336
if [[ " $test_mode " == " true" ]]; then printf " ${C_GREEN} ✅ Local disk space OK.${C_RESET} \n" ; fi
337
337
fi
338
338
}
339
+ print_header () {
340
+ printf " \n%b--- %s ---%b\n" " ${C_BOLD} " " $1 " " ${C_RESET} "
341
+ }
339
342
run_restore_mode () {
340
- printf " ${C_BOLD}${C_CYAN} --- RESTORE MODE ACTIVATED --- ${C_RESET} \n "
343
+ print_header " RESTORE MODE ACTIVATED"
341
344
run_preflight_checks " restore"
342
345
local DIRS_ARRAY; read -ra DIRS_ARRAY <<< " $BACKUP_DIRS"
343
346
local RECYCLE_OPTION=" [ Restore from Recycle Bin ]"
@@ -347,40 +350,46 @@ run_restore_mode() {
347
350
fi
348
351
all_options+=(" Cancel" )
349
352
printf " ${C_YELLOW} Available backup sets to restore from:${C_RESET} \n"
353
+ PS3=" Your choice: "
350
354
select dir_choice in " ${all_options[@]} " ; do
351
355
if [[ -n " $dir_choice " ]]; then break ;
352
356
else echo " Invalid selection. Please try again." ; fi
353
357
done
358
+ PS3=" #? "
354
359
local full_remote_source=" "
355
360
local default_local_dest=" "
356
361
local item_for_display=" "
357
362
local restore_path=" "
358
363
local is_full_directory_restore=false
359
364
if [[ " $dir_choice " == " $RECYCLE_OPTION " ]]; then
360
- printf " ${C_BOLD}${C_CYAN} --- Browse Recycle Bin --- ${C_RESET} \n "
365
+ print_header " Browse Recycle Bin"
361
366
local remote_recycle_path=" ${BOX_DIR%/ } /${RECYCLE_BIN_DIR%/ } "
362
367
local date_folders; date_folders=$( ssh " ${SSH_OPTS_ARRAY[@]} " " ${SSH_DIRECT_OPTS[@]} " " $BOX_ADDR " " ls -1 \" $remote_recycle_path \" " 2> /dev/null) || true
363
368
local valid_folders=()
364
369
for f in $date_folders ; do
365
- if [[ " $f " =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{6}$ ]]; then
366
- valid_folders+=( " $f " )
367
- fi
370
+ case " $f " in
371
+ [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9])
372
+ valid_folders+=( " $f " )
373
+ ;;
374
+ esac
368
375
done
369
376
date_folders=(" ${valid_folders[@]} " )
370
377
if [[ ${# date_folders[@]} -eq 0 ]]; then
371
378
echo " ❌ No validly-named backup folders found in the recycle bin." >&2 ; return 1
372
379
fi
373
380
printf " ${C_YELLOW} Select a backup run (date_time) to browse:${C_RESET} \n"
381
+ PS3=" Your choice: "
374
382
select date_choice in " ${date_folders[@]} " " Cancel" ; do
375
383
if [[ " $date_choice " == " Cancel" ]]; then echo " Restore cancelled." ; return 0;
376
384
elif [[ -n " $date_choice " ]]; then break ;
377
385
else echo " Invalid selection. Please try again." ; fi
378
386
done
387
+ PS3=" #? "
379
388
local remote_date_path=" ${remote_recycle_path} /${date_choice} "
380
- printf " ${C_BOLD} --- Files available from ${date_choice} (showing first 20) --- ${C_RESET} \n "
389
+ print_header " Files available from ${date_choice} (showing first 20)"
381
390
local remote_listing_source=" ${BOX_ADDR} :${remote_date_path} /"
382
391
rsync -r -n --out-format=' %n' -e " $SSH_CMD " " $remote_listing_source " . 2> /dev/null | head -n 20 || echo " No files found for this date."
383
- printf " ${C_BOLD} --------------------------------------------------------${ C_RESET}\n "
392
+ printf " %b --------------------------------------------------------%b\n " " ${C_BOLD} " " ${ C_RESET}"
384
393
printf " ${C_YELLOW} Enter the full original path of the item to restore (e.g., home/user/file.txt): ${C_RESET} " ; read -r specific_path
385
394
if [[ " $specific_path " == /* || " $specific_path " =~ (^| /)\.\. (/| $) ]]; then
386
395
echo " ❌ Invalid restore path: must be relative and contain no '..'" >&2 ; return 1
@@ -401,7 +410,7 @@ run_restore_mode() {
401
410
case " $choice " in
402
411
entire) is_full_directory_restore=true; break ;;
403
412
specific)
404
- printf -v specific_path_prompt " Enter the path relative to '%s' to restore: " " $dir_choice " ; printf " ${C_YELLOW} %s${C_RESET} " " $specific_path_prompt " ; read -er specific_path
413
+ printf -v specific_path_prompt " Enter the path relative to '%s' to restore (e.g., subfolder/file.txt) : " " $dir_choice " ; printf " ${C_YELLOW} %s${C_RESET} " " $specific_path_prompt " ; read -er specific_path
405
414
if [[ " $specific_path " == /* || " $specific_path " =~ (^| /)\.\. (/| $) ]]; then
406
415
echo " ❌ Invalid restore path: must be relative and contain no '..'" >&2 ; return 1
407
416
fi
@@ -427,13 +436,11 @@ run_restore_mode() {
427
436
fi
428
437
fi
429
438
local final_dest
430
- echo -e " \n${C_BOLD} --------------------------------------------------------"
431
- echo -e " Restore Destination"
432
- echo -e " --------------------------------------------------------${C_RESET} "
433
- echo -e " Enter the absolute destination path for the restore.\n"
434
- echo -e " ${C_YELLOW} Default (original location):${C_RESET} "
435
- echo -e " ${C_CYAN}${default_local_dest}${C_RESET} \n"
436
- echo -e " Press [Enter] to use the default path, or enter a new one."
439
+ print_header " Restore Destination"
440
+ printf " Enter the absolute destination path for the restore.\n\n"
441
+ printf " %bDefault (original location):%b\n" " ${C_YELLOW} " " ${C_RESET} "
442
+ printf " %b%s%b\n\n" " ${C_CYAN} " " $default_local_dest " " ${C_RESET} "
443
+ printf " Press [Enter] to use the default path, or enter a new one.\n"
437
444
read -rp " > " final_dest
438
445
: " ${final_dest:= $default_local_dest } "
439
446
local path_validation_attempts=0
@@ -502,17 +509,17 @@ run_restore_mode() {
502
509
dest_user=" "
503
510
fi
504
511
fi
505
- printf " \n ${C_BOLD} Restore Summary: ${C_RESET} \n "
512
+ print_header " Restore Summary"
506
513
printf " Source: %s\n" " $item_for_display "
507
- printf " Destination: ${C_BOLD} %s ${C_RESET} \n " " $final_dest "
508
- printf " \n ${C_BOLD}${C_YELLOW} --- PERFORMING DRY RUN (NO CHANGES MADE) --- ${C_RESET} \n "
514
+ printf " Destination: %b%s%b\n " " ${C_BOLD} " " $final_dest " " ${C_RESET} "
515
+ print_header " PERFORMING DRY RUN (NO CHANGES MADE)"
509
516
log_message " Starting restore dry-run of ${item_for_display} from ${full_remote_source} to ${final_dest} "
510
517
local rsync_restore_opts=(-avhi --safe-links --progress --exclude-from=" $EXCLUDE_FILE_TMP " -e " $SSH_CMD " )
511
518
if ! rsync " ${rsync_restore_opts[@]} " " ${extra_rsync_opts[@]} " --dry-run " $full_remote_source " " $final_dest " ; then
512
519
printf " ${C_RED} ❌ DRY RUN FAILED. Rsync reported an error. Check connectivity and permissions.${C_RESET} \n" >&2
513
520
log_message " Restore dry-run failed for ${item_for_display} " ; return 1
514
521
fi
515
- printf " ${C_BOLD}${C_GREEN} --- DRY RUN COMPLETE --- ${C_RESET} \n "
522
+ print_header " DRY RUN COMPLETE"
516
523
while true ; do
517
524
printf " \n${C_YELLOW} Proceed with restoring %s to '%s'? [yes/no]: ${C_RESET} " " $item_for_display " " $final_dest " ; read -r confirmation
518
525
case " ${confirmation,,} " in
@@ -521,7 +528,7 @@ run_restore_mode() {
521
528
* ) echo " Please answer 'yes' or 'no'." ;;
522
529
esac
523
530
done
524
- printf " \n ${C_BOLD} --- EXECUTING RESTORE --- ${C_RESET} \n "
531
+ print_header " EXECUTING RESTORE"
525
532
log_message " Starting actual restore of ${item_for_display} from ${full_remote_source} to ${final_dest} "
526
533
if rsync " ${rsync_restore_opts[@]} " " ${extra_rsync_opts[@]} " " $full_remote_source " " $final_dest " ; then
527
534
log_message " Restore completed successfully."
0 commit comments