99# Author: Tom from Berlin
1010# License: MIT
1111# Repository: https://github.com/TomfromBerlin/zsh-disk-guard
12+ # ──────────────────────────────────────────────────────────────────
13+ # "You can lead a horse to water, but you can't make it read warnings."
14+ # — Ancient IT Wisdom
15+ # ──────────────────────────────────────────────────────────────────
1216# ===================================================================
1317# ──────────────────────────────────────────────────────────────────
1418# Version Check
@@ -253,6 +257,13 @@ _zsh_disk_guard_verify() {
253257 shift
254258 local sources=(" $@ " )
255259
260+ # Color definitions
261+ local GREEN=$' \e [0;32;40m'
262+ local CYAN=$' \e [0;36;4m' # underlined
263+ local YELLOW=$' \e [1;33;40m'
264+ local RED=$' \e [0;31;40m'
265+ local NC=$' \e [0m'
266+
256267 _zsh_disk_guard_debug " Checking target: $target "
257268 [[ -z " $target " ]] && return 0
258269
@@ -295,7 +306,7 @@ _zsh_disk_guard_verify() {
295306 _zsh_disk_guard_debug " Quick check: $( _zsh_disk_guard_format_size $estimated_size ) "
296307 if (( estimated_size < ZSH_DISK_GUARD_DEEP_THRESHOLD )) ; then
297308 if (( usage >= ZSH_DISK_GUARD_THRESHOLD )) ; then
298- echo " ⚠️ Warning: Partition $mountpoint is ${usage} % full!" >&2
309+ printf ' \n%s\n ' " ⚠️ ${RED} Warning${NC} : Partition ${CYAN} $ mountpoint${NC} is ${YELLOW}${ usage} %${NC} full!" >&2
299310 if [[ -o interactive || -t 0 ]]; then
300311 read -q " REPLY?Continue anyway? [y/N] "
301312 echo
@@ -436,7 +447,7 @@ _zsh_disk_guard_progress_bar() {
436447 # Display progress bar at bottom of terminal
437448 printf ' \e[s' # Save cursor position
438449 printf ' \e[%d;1H' " $LINES " # Move to bottom row, first column
439- printf ' %s%s %s%s ' " $bar " " $perc_color " " $suffix " " $NC "
450+ printf ' %s Files: %d ( %s%d%%%s) ' " $bar " " $file_count " " $ perc_color" " $perc_done " " $NC "
440451 printf ' \e[K' # Clear rest of line
441452 printf ' \e[u' # Restore cursor position
442453}
@@ -496,13 +507,72 @@ _zsh_disk_guard_cp() {
496507 local LC_ALL=C
497508
498509 # If plugin is disabled, just run normal cp
499- (( ZSH_DISK_GUARD_ENABLED )) || { command cp " $@ " ; return $? ; }
510+ (( ZSH_DISK_GUARD_ENABLED )) || { cp " $@ " ; return $? ; }
500511
501512 local args=(" $@ " )
502513 local target=" ${args[-1]} "
503514 local sources=(" ${args[@]: 0:- 1} " )
504515
505- # Verify disk space before starting
516+ # Color definitions
517+ local GREEN=$' \e [0;32;40m'
518+ local CYAN=$' \e [0;36;1m'
519+ local YELLOW=$' \e [1;33;40m'
520+ local RED=$' \e [0;31;40m'
521+ local NC=$' \e [0m'
522+
523+ # ──────────────────────────────────────────────────────────────
524+ # Check for missing source files BEFORE any other operation
525+ # ──────────────────────────────────────────────────────────────
526+ local missing_files=()
527+ local existing_sources=()
528+
529+ for source in " ${sources[@]} " ; do
530+ if [[ ! -e " $source " ]]; then
531+ missing_files+=(" ${source: t} " )
532+ else
533+ existing_sources+=(" $source " )
534+ fi
535+ done
536+
537+ # If files are missing, warn user and ask whether to continue
538+ if (( ${# missing_files[@]} > 0 )) ; then
539+ printf ' \n%s\n' " ⚠️ ${RED} Warning${NC} : ${YELLOW}${# missing_files[@]}${NC} source file(s) ${RED} not found${NC} :" >&2
540+ for file in " ${missing_files[@]} " ; do
541+ printf ' %s %s\n' " ❌" " ${CYAN}${file}${NC} " >&2
542+ done
543+ printf ' \n'
544+
545+ if (( ${# existing_sources[@]} > 0 )) ; then
546+ read -q " reply?Continue with remaining ${YELLOW}${# existing_sources[@]}${NC} file(s)? [y/N] " < /dev/tty
547+ echo
548+ if [[ " $reply " != [Yy] ]]; then
549+ printf ' %s\n' " Operation cancelled."
550+ # Restore settings before returning
551+ if (( reporttime_was_set )) ; then
552+ REPORTTIME=$saved_reporttime
553+ else
554+ unset REPORTTIME
555+ fi
556+ (( xtrace_was_set )) && set -x
557+ return 1
558+ fi
559+ else
560+ printf ' %s\n' " ❌ ${RED} Error${NC} : No valid source files found. Operation cancelled." >&2
561+ # Restore settings before returning
562+ if (( reporttime_was_set )) ; then
563+ REPORTTIME=$saved_reporttime
564+ else
565+ unset REPORTTIME
566+ fi
567+ (( xtrace_was_set )) && set -x
568+ return 1
569+ fi
570+
571+ # Update sources to only include existing files
572+ sources=(" ${existing_sources[@]} " )
573+ fi
574+
575+ # Verify disk space before starting (only for existing files)
506576 _zsh_disk_guard_verify " $target " " ${sources[@]} " || return 1
507577
508578 local total_files=${# sources[@]}
@@ -523,16 +593,17 @@ _zsh_disk_guard_cp() {
523593
524594 # Display current file with size
525595 local size_display=$( _zsh_disk_guard_format_size $source_size )
526- printf ' → %s (%s)\n ' " ${source: t} " " $size_display "
596+ printf ' \n → %s (%s)' " ${source: t} " " $size_display "
527597
528598 local target_file=" $target /${source: t} "
529599
530600 # Check if target file exists and prompt for overwrite (interactive mode only)
531601 if [[ -f " $target_file " ]] && [[ -o interactive ]]; then
532- read -q " reply?File exists. Overwrite ${target_file: t} ? [y/N] " < /dev/tty
602+ printf ' \n%s\n' " ⚠️ ${RED} Warning${NC} : ${CYAN}${target_file: t}${NC} already exists in ${CYAN}${args[-1]}${NC} !" >&2
603+ read -q " reply? Overwrite ${target_file: t} ? [y/N] " < /dev/tty
533604 echo
534605 if [[ " $reply " != [Yy] ]]; then
535- printf " Skipped: %s\n" " ${source: t} "
606+ printf ' %s\n' " Skipped: ${source: t} "
536607 continue
537608 fi
538609 fi
@@ -579,7 +650,7 @@ _zsh_disk_guard_cp() {
579650 if (( cp_status != 0 )) ; then
580651 printf ' \n❌ Error copying %s (exit code: %d)\n' " ${source: t} " " $cp_status " >&2
581652 _zsh_disk_guard_deinit_term
582- # Restore settings
653+ # Restore REPORTTIME settings
583654 if (( reporttime_was_set )) ; then
584655 REPORTTIME=$saved_reporttime
585656 else
@@ -598,7 +669,6 @@ _zsh_disk_guard_cp() {
598669
599670 # Ensure 100% at the end
600671 _zsh_disk_guard_progress_bar 100 100 $total_files
601-
602672 _zsh_disk_guard_deinit_term
603673
604674 # Calculate elapsed time and format it cleanly
@@ -622,7 +692,11 @@ _zsh_disk_guard_cp() {
622692 fi
623693
624694 # Display success summary
625- printf ' \n✅ Done! Copied %s in %s\n\n' " $( _zsh_disk_guard_format_size $total_bytes_copied ) " " $elapsed_display "
695+ if [[ $total_bytes_copied = 0 ]]; then
696+ printf ' \n%s\n\n' " ℹ️ Nothing has changed!"
697+ else
698+ printf ' \n\n✅ Done! Copied %s in %s\n\n' " $( _zsh_disk_guard_format_size $total_bytes_copied ) " " $elapsed_display "
699+ fi
626700
627701 # Restore REPORTTIME to original state
628702 if (( reporttime_was_set )) ; then
@@ -660,7 +734,66 @@ _zsh_disk_guard_mv() {
660734 local target=" ${args[-1]} "
661735 local sources=(" ${args[@]: 0:- 1} " )
662736
663- # Verify disk space before starting
737+ # Color definitions
738+ local GREEN=$' \e [0;32;40m'
739+ local CYAN=$' \e [0;36;1m'
740+ local YELLOW=$' \e [1;33;40m'
741+ local RED=$' \e [0;31;40m'
742+ local NC=$' \e [0m'
743+
744+ # ──────────────────────────────────────────────────────────────
745+ # Check for missing source files BEFORE any other operation
746+ # ──────────────────────────────────────────────────────────────
747+ local missing_files=()
748+ local existing_sources=()
749+
750+ for source in " ${sources[@]} " ; do
751+ if [[ ! -e " $source " ]]; then
752+ missing_files+=(" ${source: t} " )
753+ else
754+ existing_sources+=(" $source " )
755+ fi
756+ done
757+
758+ # If files are missing, warn user and ask whether to continue
759+ if (( ${# missing_files[@]} > 0 )) ; then
760+ printf ' \n%s\n' " ⚠️ ${RED} Warning${NC} : ${YELLOW}${# missing_files[@]}${NC} source file(s) ${RED} not found${NC} :" >&2
761+ for file in " ${missing_files[@]} " ; do
762+ printf ' %s %s\n' " ❌" " ${CYAN}${file}${NC} " >&2
763+ done
764+ printf ' \n'
765+
766+ if (( ${# existing_sources[@]} > 0 )) ; then
767+ read -q " reply?Continue with remaining ${YELLOW}${# existing_sources[@]}${NC} file(s)? [y/N] " < /dev/tty
768+ echo
769+ if [[ " $reply " != [Yy] ]]; then
770+ printf ' %s\n' " Operation cancelled."
771+ # Restore settings before returning
772+ if (( reporttime_was_set )) ; then
773+ REPORTTIME=$saved_reporttime
774+ else
775+ unset REPORTTIME
776+ fi
777+ (( xtrace_was_set )) && set -x
778+ return 1
779+ fi
780+ else
781+ printf ' %s\n' " ❌ ${RED} Error${NC} : No valid source files found. Operation cancelled." >&2
782+ # Restore settings before returning
783+ if (( reporttime_was_set )) ; then
784+ REPORTTIME=$saved_reporttime
785+ else
786+ unset REPORTTIME
787+ fi
788+ (( xtrace_was_set )) && set -x
789+ return 1
790+ fi
791+
792+ # Update sources to only include existing files
793+ sources=(" ${existing_sources[@]} " )
794+ fi
795+
796+ # Verify disk space before starting (only for existing files)
664797 _zsh_disk_guard_verify " $target " " ${sources[@]} " || return 1
665798
666799 local total_files=${# sources[@]}
@@ -687,10 +820,11 @@ _zsh_disk_guard_mv() {
687820
688821 # Check if target file exists and prompt for overwrite (interactive mode only)
689822 if [[ -f " $target_file " ]] && [[ -o interactive ]]; then
690- read -q " reply?File exists. Overwrite ${target_file: t} ? [y/N] " < /dev/tty
823+ printf ' \n%s\n' " ⚠️ ${RED} Warning${NC} : ${CYAN}${target_file: t}${NC} already exists in ${CYAN}${args[-1]}${NC} !" >&2
824+ read -q " reply? Overwrite ${target_file: t} ? [y/N] " < /dev/tty
691825 echo
692826 if [[ " $reply " != [Yy] ]]; then
693- printf " Skipped: %s\n" " ${source: t} "
827+ printf ' %s\n' " Skipped: ${source: t} "
694828 continue
695829 fi
696830 fi
@@ -703,35 +837,41 @@ _zsh_disk_guard_mv() {
703837 if (( source_size > 0 )) ; then
704838 while kill -0 $mv_pid 2> /dev/null; do
705839 if [[ -f " $target_file " ]]; then
840+ # Get current size of destination file
706841 local cur_size=$( set +x; command stat -c%s " $target_file " 2> /dev/null || command stat -f%z " $target_file " 2> /dev/null || echo 0)
707842
843+ # Calculate progress: (completed_files * 100 + current_file_percent) / total_files
708844 local cur_file_pct=$(( cur_size * 100 / source_size))
709845 (( cur_file_pct > 100 )) && cur_file_pct=100
710846
711847 local tot_pct=$(( (file_idx - 1 ) * 100 + cur_file_pct ))
712848 local overall_pct=$(( tot_pct / total_files ))
713849
850+ # Show progress with file count
714851 _zsh_disk_guard_progress_bar $overall_pct 100 $total_files
715852 else
853+ # File doesn't exist yet, show progress for completed files
716854 _zsh_disk_guard_progress_bar $(( file_idx - 1 )) $total_files $total_files
717855 fi
718856 sleep 0.1
719857 done
720858 else
859+ # Unknown size or directory, just show file count progress
721860 while kill -0 $mv_pid 2> /dev/null; do
722861 _zsh_disk_guard_progress_bar $file_idx $total_files $total_files
723862 sleep 0.2
724863 done
725864 fi
726865
866+ # Wait for mv to complete and get exit status
727867 wait $mv_pid
728868 local mv_status=$?
729869
730870 # Check if mv failed
731871 if (( mv_status != 0 )) ; then
732872 printf ' \n❌ Error moving %s (exit code: %d)\n' " ${source: t} " " $mv_status " >&2
733873 _zsh_disk_guard_deinit_term
734- # Restore settings
874+ # Restore REPORTTIME settings
735875 if (( reporttime_was_set )) ; then
736876 REPORTTIME=$saved_reporttime
737877 else
@@ -744,30 +884,40 @@ _zsh_disk_guard_mv() {
744884 # Add to total bytes moved
745885 (( total_bytes_moved += source_size ))
746886
887+ # Update progress bar for completed file
747888 _zsh_disk_guard_progress_bar $(( file_idx * 100 / total_files )) 100 $total_files
748889 done
749890
891+ # Ensure 100% at the end
750892 _zsh_disk_guard_progress_bar 100 100 $total_files
751893 _zsh_disk_guard_deinit_term
752894
753895 # Calculate elapsed time and format it cleanly
754896 local elapsed=$(( SECONDS - start_time))
755897 local elapsed_display
756- if (( elapsed < 60 )) ; then
757- elapsed_display=" ${elapsed} s"
758- elif (( elapsed < 3600 )) ; then
759- local minutes=$(( elapsed / 60 ))
760- local seconds=$(( elapsed % 60 ))
761- elapsed_display=" ${minutes} m ${seconds} s"
898+
899+ # Convert to integer to avoid floating point issues
900+ local elapsed_int=${elapsed% .* }
901+
902+ if (( elapsed_int < 60 )) ; then
903+ elapsed_display=$( printf ' %ds' " $elapsed_int " )
904+ elif (( elapsed_int < 3600 )) ; then
905+ local minutes=$(( elapsed_int / 60 ))
906+ local seconds=$(( elapsed_int % 60 ))
907+ elapsed_display=$( printf ' %dm %ds' " $minutes " " $seconds " )
762908 else
763- local hours=$(( elapsed / 3600 ))
764- local minutes=$(( (elapsed % 3600 ) / 60 ))
765- local seconds=$(( elapsed % 60 ))
766- elapsed_display=" ${ hours} h ${ minutes} m ${ seconds} s "
909+ local hours=$(( elapsed_int / 3600 ))
910+ local minutes=$(( (elapsed_int % 3600 ) / 60 ))
911+ local seconds=$(( elapsed_int % 60 ))
912+ elapsed_display=$( printf ' %dh %dm %ds ' " $ hours" " $ minutes" " $ seconds" )
767913 fi
768914
769915 # Display success summary
770- printf ' \n✅ Done! Moved %s in %s\n\n' " $( _zsh_disk_guard_format_size $total_bytes_moved ) " " $elapsed_display "
916+ if [[ $total_bytes_moved = 0 ]]; then
917+ printf ' \n%s\n\n' " ℹ️ Nothing has changed!"
918+ else
919+ printf ' \n✅ Done! Moved %s in %s\n\n' " $( _zsh_disk_guard_format_size $total_bytes_moved ) " " $elapsed_display "
920+ fi
771921
772922 # Restore REPORTTIME to original state
773923 if (( reporttime_was_set )) ; then
@@ -784,7 +934,7 @@ _zsh_disk_guard_mv() {
784934
785935# ──────────────────────────────────────────────────────────────────
786936# Wrapper for 'rsync' command with disk guard
787- # Note: rsync has its own progress display, so we don't add our own
937+ # Note: rsync has its own progress display, so we don't need one to add
788938# ──────────────────────────────────────────────────────────────────
789939_zsh_disk_guard_rsync () {
790940 local LC_ALL=C
@@ -815,7 +965,7 @@ _zsh_disk_guard_rsync() {
815965 return $?
816966 fi
817967
818- # Verify disk space, then run rsync with its own progress
968+ # Verify disk space, then run rsync
819969 _zsh_disk_guard_verify " $target " " ${sources[@]} " || return 1
820970 command rsync " $@ "
821971 return $?
0 commit comments