diff --git a/tests.sh b/tests.sh index 4e5dfeb..6d61ace 100755 --- a/tests.sh +++ b/tests.sh @@ -5,23 +5,38 @@ tmp="tests" mkdir -p "$tmp" trap 'rm -rf "$tmp"' EXIT -testdirs="1.testdir 2.testdir 3.testdir 4.testdir" -testfiles="1.testfile 2.testfile 3.testfile" +testdirs="d1 d2 d3 d4" +testfiles="f1 f2 f3" for d in $testdirs; do mkdir -p "$tmp/$d" for f in $testfiles; do touch "$tmp/$d/$f"; done done -./trash -v "$tmp/1.testdir/1.testfile" "$tmp/1.testdir/2.testfile" "$tmp/2.testdir" "$tmp/3.testdir" -./trash -vfu 1.testfile -./trash -vfu 2.testdir +./trash -v "$tmp/d1/f1" "$tmp/d1/f2" "$tmp/d2" "$tmp/d3" +./trash -v -f -u "$tmp/d1/f1" +./trash -v -f -u "$tmp/d2" - [ -e "$tmp/1.testdir/1.testfile" ] -! [ -e "$tmp/1.testdir/2.testfile" ] - [ -e "$tmp/1.testdir/3.testfile" ] - [ -e "$tmp/2.testdir" ] - [ -e "$tmp/2.testdir/1.testfile" ] -! [ -e "$tmp/3.testdir" ] - [ -e "$tmp/4.testdir" ] +[ -e "$tmp/d1/f1" ] +! [ -e "$tmp/d1/f2" ] +[ -e "$tmp/d1/f3" ] +[ -e "$tmp/d2" ] +[ -e "$tmp/d2/f1" ] +! [ -e "$tmp/d3" ] +[ -e "$tmp/d4" ] -echo "Tests passed!" +./trash -f -v -e "$tmp/d1/f2" "$tmp/d3" + +echo "old" > "$tmp/over.txt" +./trash "$tmp/over.txt" +printf '\033[2mPausing 2sec to let file deletion time advance...\033[0m\n' +sleep 2 +echo "new" > "$tmp/over.txt" +./trash -v "$tmp/over.txt" +./trash -v -u "$tmp/over.txt" +! [ -e "$tmp/over.txt" ] +[ -e "$tmp/over.txt" -a "$(cat "$tmp/over.txt")" = "new" ] +rm -f "$tmp/over.txt" +./trash -v -u "$tmp/over.txt" +[ -e "$tmp/over.txt" -a "$(cat "$tmp/over.txt")" = "old" ] + +printf '\033[32;1mTests passed!\033[0m\n' diff --git a/trash b/trash index d15f687..d47c6f0 100755 --- a/trash +++ b/trash @@ -25,6 +25,7 @@ force_flag= [ -z "$FS" ] && FS='\t' [ -z "$RS" ] && RS='\n' set -o pipefail +shopt -s nullglob fail() { echo "$@" 1>&2 @@ -52,12 +53,27 @@ Options: EOU } -pick_trashfiles() { - td="$1"; shift - prompt="$1"; shift - find "$td"/info -mindepth 1 -maxdepth 1 -print0 | \ - fzf -1 --read0 --prompt="$prompt" --query="$@" --multi \ - --preview="sh -c 'f=\"$td/files/\${1%.trashinfo}\"; [ -d \"\$f\" ] && tree \"\$f\" || echo \"\$f\" && cat \"\$f\"' -- {}" +find_trashfile() { + target0="$1" + target="$(readlink -f -- "$target0")" + td="$(get_trashdir "$target")" + if [ "$(dirname "$target")" = "$td/files" ] && [ -e "$target" ]; then + echo "$target" + else + best_file= + best_date=0 + for f in "$td/files"/*; do + info="$td/info/$(basename "$f").trashinfo" + path="$(path_from_trashinfo "$info")" + date="$(date +"%s" -d "$(date_from_trashinfo "$info")")" + if [ "$path" = "$target" -a \( -z "$best_file" -o "$date" -gt "$best_date" \) ]; then + best_file="$f" + best_date="$date" + fi + done + [ -z "$best_file" ] && return 1 + echo "$best_file" + fi } confirm() { @@ -152,13 +168,6 @@ DeletionDate=$(date +"%FT%H:%M:%S") END } -exit_if_trash_empty() { - if ! [ -d "$1" ] || [ -z "$(ls -A "$1/files")" ]; then - echo "Nothing in the trash!" - exit - fi -} - trash_file() { filename=$1 dir=${filename%/*} @@ -196,47 +205,56 @@ trash_file() { } untrash_files() { - td="$(get_trashdir "$(readlink -f -- "${1:-$PWD}")")" - [ -d "$td/info" ] || return 1 - pick_trashfiles "$td" "Pick file(s) to untrash: " "$@" | \ - while read -r info; do - orig="$(path_from_trashinfo "$td/info/$info")" - mv -i "$td/files/${info%.trashinfo}" "$orig" && rm -f "$td/info/$info" - done + for target; do + if ! file="$(find_trashfile "$target")"; then + [ "$force_flag" = "-f" ] || fail "No such file: $target" + fi + info="$(dirname "$file")/../info/$(basename "$file").trashinfo" + orig="$(path_from_trashinfo "$info")" + mv ${force_flag:--i} $verbose_flag "$file" "$orig" || fail "Could not restore file: $file" + rm -rf $verbose_flag "$info" || fail "Could not clean up trash info file: $info" + done } empty_files() { - td="$(get_trashdir "$(readlink -f -- "${1:-$PWD}")")" - exit_if_trash_empty "$td" - pick_trashfiles "$td" "Pick file(s) to permanently delete: " "$@" | \ - while read -r info; do - orig="$(path_from_trashinfo "$td/info/$info")" - rm -r $verbose_flag "$td/info/$info" "$td/files/${info%.trashinfo}" || fail "Could not remove file" - done || return 1 - printf '\033[1mDeleted!\033[0m\n' + for target; do + if ! file="$(find_trashfile "$target")"; then + [ "$force_flag" = "-f" ] || fail "No such file: $target" + fi + info="$(dirname "$file")/../info/$(basename "$file").trashinfo" + orig="$(path_from_trashinfo "$info")" + confirm "Delete file $file?" || exit 1 + rm -r $force_flag $verbose_flag -- "$file" "$info" || fail "Could not empty file: $file" + done } empty_trash() { - td="$(get_trashdir "$(readlink -f -- "${1:-$PWD}")")" - exit_if_trash_empty "$td" - ( printf '\033[1mThe following %s of files will be deleted:\033[0m\n' \ + [ $# -eq 0 ] && set "$PWD" + for target; do + td="$(get_trashdir "$(readlink -f -- "$target")")" + [ -d "$td/files" ] || continue + [ "$force_flag" != "-f" ] && + ( printf '\033[1mThe following %s of files will be deleted:\033[0m\n' \ "$(du -h --summarize "$td/files" | cut -f1)B" && - printf ' \033[33m%s\033[0m\n' "$td"/files/*) | more - if ! confirm "Do you want to proceed?"; then - echo "Aborted." - exit 1 - fi - rm -r $verbose_flag "$td"/files/* "$td"/info/* || exit 1 - printf '\033[1mDeleted!\033[0m\n' + printf ' \033[33m%s\033[0m\n' "$td"/files/*) | more + if ! confirm "Do you want to proceed?"; then + echo "Aborted." + exit 1 + fi + rm -r $verbose_flag "$td"/files/* "$td"/info/* || exit 1 + printf '\033[1mDeleted!\033[0m\n' + done } list_trash() { - td="$(get_trashdir "$(readlink -f -- "${1:-$PWD}")")" - exit_if_trash_empty "$td" - for f in "$td/files"/*; do - printf '%s'"$FS"'%s'"$FS"'%s'"$RS" \ - "$f" "$(date_from_trashinfo "$td/info/$(basename "$f").trashinfo")" \ - "$(path_from_trashinfo "$td/info/$(basename "$f").trashinfo")" + [ $# -eq 0 ] && set "$PWD" + for target; do + td="$(get_trashdir "$(readlink -f -- "$target")")" + for f in "$td/files"/*; do + printf '%s'"$FS"'%s'"$FS"'%s'"$RS" \ + "$f" "$(date_from_trashinfo "$td/info/$(basename "$f").trashinfo")" \ + "$(path_from_trashinfo "$td/info/$(basename "$f").trashinfo")" + done done }