Standardized indentation and moved url encoding/decoding to a more

standardized location.
This commit is contained in:
Bruce Hill 2020-08-06 15:25:48 -07:00
parent f154424db2
commit 9d18bcc775

419
bin
View File

@ -26,243 +26,269 @@ force_flag=
[ -z "$RS" ] && RS='\n'
fail() {
echo "$@" 1>&2
exit 1
echo "$@" 1>&2
exit 1
}
show_usage() {
cat << EOU
Usage: $progname [OPTION]... FILE...
cat <<-EOU
Usage: $progname [OPTION]... FILE...
Move files into the trash bin.
Move files into the trash bin.
Options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-v, --verbose explain what is being done
-i, --interactive prompt before moving every file
-f, --force ignore non-existent files, never prompt
-r, -R, --recursive ignored (for compatability with rm)
-u, --untrash restore file(s) from the trash
-e, --empty choose files to empty from the trash
-E, --empty-all empty all the files in the trash folder(s) (default: ~)
-l, --list list files in trash folder(s) (default: ~)
-- Any arguments after '--' will be treated as filenames
EOU
Options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-v, --verbose explain what is being done
-i, --interactive prompt before moving every file
-f, --force ignore non-existent files, never prompt
-r, -R, --recursive ignored (for compatability with rm)
-u, --untrash restore file(s) from the trash
-e, --empty choose files to empty from the trash
-E, --empty-all empty all the files in the trash folder(s) (default: ~)
-l, --list list files in trash folder(s) (default: ~)
-- Any arguments after '--' will be treated as filenames
EOU
}
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
[ -e "$f" ] || continue
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
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
[ -e "$f" ] || continue
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() {
[ "$force_flag" = "-f" ] && return 0
if type ask >/dev/null; then
ask -n "$@"
else
# Get one character of input
tput civis >/dev/tty;
printf '\033[1m%s\033[0m' "$2" >/dev/tty;
stty -icanon -echo >/dev/tty 2>/dev/tty;
REPLY="$(dd bs=1 count=1 2>/dev/null </dev/tty)"
stty icanon echo >/dev/tty 2>/dev/tty
tput cvvis >/dev/tty
[ "$REPLY" = "y" ]
fi
[ "$force_flag" = "-f" ] && return 0
if type ask >/dev/null; then
ask -n "$@"
else
# Get one character of input
tput civis >/dev/tty;
printf '\033[1m%s\033[0m' "$2" >/dev/tty;
stty -icanon -echo >/dev/tty 2>/dev/tty;
REPLY="$(dd bs=1 count=1 2>/dev/null </dev/tty)"
stty icanon echo >/dev/tty 2>/dev/tty
tput cvvis >/dev/tty
[ "$REPLY" = "y" ]
fi
}
has_arg() {
if type arg >/dev/null; then
arg "$@" >/dev/null
else
target="$1"
while [ $# -gt 0 ]; do
[ "$1" = "$target" ] && return 0
[ "$1" = "--" ] && break
done
false
fi
if type arg >/dev/null; then
arg "$@" >/dev/null
else
target="$1"
while [ $# -gt 0 ]; do
[ "$1" = "$target" ] && return 0
[ "$1" = "--" ] && break
done
false
fi
}
encoder=
if command -v php &> /dev/null; then
encoder=php
elif command -v python3 &> /dev/null; then
encoder=python
elif command -v perl &> /dev/null; then
encoder=perl
else
echo "No URL encoder found! Please install php, python3, or perl!" 1>&2
exit 1
fi
urlencode() {
case "$encoder" in
php) php -r 'echo urlencode($argv[1]);' -- "$1" ;;
perl) perl -MURI::Escape -e 'print uri_escape $ARGV[0]' -- "$1" ;;
python) python3 -c 'import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))' "$1";;
esac
}
urldecode() {
case "$encoder" in
php) php -r 'echo urldecode($argv[1]);' -- "$1" ;;
perl) perl -MURI::Escape -e 'print uri_unescape $ARGV[0]' -- "$1" ;;
python) python3 -c 'import urllib.parse, sys; print(urllib.parse.unquote(sys.argv[1]))' "$1";;
esac
}
is_valid_trashdir() {
[ -d "$1" ] && expr "$(stat -c '%a' "$1")" : '^1[0-7]\{3\}' >/dev/null && ! [ -L "$1" ]
[ -d "$1" ] && expr "$(stat -c '%a' "$1")" : '^1[0-7]\{3\}' >/dev/null && ! [ -L "$1" ]
}
get_trashdir() {
file="$(readlink -f -- "$1")"
if [ -e "$file" ]; then
file_base="$(df -h "$file" | awk 'NR==2 {print $NF}')"
else
file_base="$(awk -v f="$file" 'substr(f,1,length($2)+1)==($2 "/") && $2>best {best=$2} END {print best}' /proc/mounts)"
fi
home_base="$(df -h "$HOME" | awk 'NR==2 {print $NF}')"
if [ "$file_base" = "$home_base" ]; then
echo "$HOME/.Trash"
elif is_valid_trashdir "$file_base/.Trash"; then
echo "$file_base/.Trash"
else
echo "$file_base/.Trash-$(id -u)"
fi
file="$(readlink -f -- "$1")"
if [ -e "$file" ]; then
file_base="$(df -h "$file" | awk 'NR==2 {print $NF}')"
else
file_base="$(awk -v f="$file" 'substr(f,1,length($2)+1)==($2 "/") && $2>best {best=$2} END {print best}' /proc/mounts)"
fi
home_base="$(df -h "$HOME" | awk 'NR==2 {print $NF}')"
if [ "$file_base" = "$home_base" ]; then
echo "$HOME/.Trash"
elif is_valid_trashdir "$file_base/.Trash"; then
echo "$file_base/.Trash"
else
echo "$file_base/.Trash-$(id -u)"
fi
}
can_trash() {
filename="$1"
if [ ! -e "$filename" ]; then
if [ "$force_flag" != "-f" ]; then
fail "$progname: cannot move '$filename' to trash: No such file or directory"
fi
return 0
fi
filename="$1"
if [ ! -e "$filename" ]; then
if [ "$force_flag" != "-f" ]; then
fail "$progname: cannot move '$filename' to trash: No such file or directory"
fi
return 0
fi
if [ "$interactive_flag" = "-i" ]; then
[ -d "$filename" ] && type=directory || type="file"
confirm "$progname: move $type '$filename' to trash?"
fi
if [ "$interactive_flag" = "-i" ]; then
[ -d "$filename" ] && type=directory || type="file"
confirm "$progname: move $type '$filename' to trash?"
fi
}
init_trashdir() {
trashdir=$1
if ! [ -d "$trashdir" ]; then
mkdir -m 1755 "$trashdir" || fail "Could not create trash directory"
fi
mkdir -p "$trashdir/files" || fail "$progname: unable to write to $trashdir"
mkdir -p "$trashdir/info" || fail "$progname: unable to write to $trashdir"
trashdir=$1
if ! [ -d "$trashdir" ]; then
mkdir -m 1755 "$trashdir" || fail "Could not create trash directory"
fi
mkdir -p "$trashdir/files" || fail "$progname: unable to write to $trashdir"
mkdir -p "$trashdir/info" || fail "$progname: unable to write to $trashdir"
}
path_from_trashinfo() {
#sed -n 's/^Path=//p' "$1" | php -r 'echo urldecode(fgets(STDIN));'
sed -n 's/^Path=//p' "$1" | perl -MURI::Escape -ne 'print uri_unescape $_'
urldecode "$(sed -n 's/^Path=//p' "$1")"
}
date_from_trashinfo() {
sed -n 's/^DeletionDate=//p' "$1"
sed -n 's/^DeletionDate=//p' "$1"
}
trashinfo_for_file() {
#php -r 'echo urlencode($argv[1]);' -- "$1"
cat <<END
[Trash Info]
Path=$(perl -MURI::Escape -e 'print uri_escape $ARGV[0]' -- "$1")
DeletionDate=$(date +"%FT%H:%M:%S")
END
cat <<-END
[Trash Info]
Path=$(urlencode "$1")
DeletionDate=$(date +"%FT%H:%M:%S")
END
}
trash_file() {
filename=$1
dir=${filename%/*}
trashdir="$(get_trashdir "$dir")"
init_trashdir "$trashdir"
filename=$1
dir=${filename%/*}
trashdir="$(get_trashdir "$dir")"
init_trashdir "$trashdir"
trashname="${filename##*/}"
origname="${trashname%%.*}"
if [ -z "$origname" ]; then
origname="dotfile"
fi
ext=".${trashname##*.}"
if [ "$ext" = ".$trashname" ]; then
ext=""
fi
trashname="${filename##*/}"
origname="${trashname%%.*}"
if [ -z "$origname" ]; then
origname="dotfile"
fi
ext=".${trashname##*.}"
if [ "$ext" = ".$trashname" ]; then
ext=""
fi
# Use -u (unsafe) option because we cannot mv a directory into a
# file. This is technically "unsafe" but mv will ask for
# confirmation when overwriting.
deletedfile=$(mktemp -u "$trashdir/files/${origname}_XXXXXXXX")$ext
deletedbase=$(basename "$deletedfile")
deletedinfo="$trashdir/info/$deletedbase.trashinfo"
# Use -u (unsafe) option because we cannot mv a directory into a
# file. This is technically "unsafe" but mv will ask for
# confirmation when overwriting.
deletedfile=$(mktemp -u "$trashdir/files/${origname}_XXXXXXXX")$ext
deletedbase=$(basename "$deletedfile")
deletedinfo="$trashdir/info/$deletedbase.trashinfo"
trashinfo_for_file "$filename" > "$deletedinfo" || \
fail "$progname: unable to create trash info for $filename at $deletedinfo"
trashinfo_for_file "$filename" > "$deletedinfo" || \
fail "$progname: unable to create trash info for $filename at $deletedinfo"
# Note that the trashinfo file will have the ownership and
# permissions of the person who deleted the file, and not
# necessarily of the original file.
# Note that the trashinfo file will have the ownership and
# permissions of the person who deleted the file, and not
# necessarily of the original file.
if ! mv $verbose_flag -- "$filename" "$deletedfile"; then
rm "$deletedinfo"
fail "$progname: unable to move $filename to $deletedfile"
fi
if ! mv $verbose_flag -- "$filename" "$deletedfile"; then
rm "$deletedinfo"
fail "$progname: unable to move $filename to $deletedfile"
fi
}
untrash_files() {
[ $# -eq 0 ] && fail "No files provided to untrash"
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
[ $# -eq 0 ] && fail "No files provided to untrash"
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() {
[ $# -eq 0 ] && fail "No files provided to empty"
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
[ $# -eq 0 ] && fail "No files provided to empty"
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() {
[ $# -eq 0 ] && set "$PWD"
for target; do
td="$(get_trashdir "$(readlink -f -- "$target")")"
[ -d "$td/files" ] && [ -n "$(ls -A "$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'
done
[ $# -eq 0 ] && set "$PWD"
for target; do
td="$(get_trashdir "$(readlink -f -- "$target")")"
[ -d "$td/files" ] && [ -n "$(ls -A "$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'
done
}
list_trash() {
[ $# -eq 0 ] && set "$PWD"
for target; do
td="$(get_trashdir "$(readlink -f -- "$target")")"
for f in "$td/files"/*; do
[ -e "$f" ] || continue
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
[ $# -eq 0 ] && set "$PWD"
for target; do
td="$(get_trashdir "$(readlink -f -- "$target")")"
for f in "$td/files"/*; do
[ -e "$f" ] || continue
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
}
if [ $# -eq 0 ]; then
show_usage
fail "Run 'man $progname' for more information."
show_usage
fail "Run 'man $progname' for more information."
fi
(has_arg -v "$@" || has_arg --verbose "$@") && verbose_flag=-v
@ -277,25 +303,26 @@ action=trash
(has_arg -E "$@" || has_arg --empty-all "$@") && action=emptyall
while a="$(expr ";$1" : "^;\(--\|-[VhvirRfueEl]\+\|--version\|--help\|--verbose\|--interactive\|--recursive\|--force\|--untrash\|--empty\|--empty-all\|--list\)$")"; do
shift
[ "$a" = "--" ] && break
shift
[ "$a" = "--" ] && break
done
case "$action" in
help) show_usage ;;
version) echo "$version" ;;
list) list_trash "$@" ;;
untrash) untrash_files "$@" ;;
emptyfiles) empty_files "$@" ;;
emptyall) empty_trash "$@" ;;
trash)
for f; do
filename="$(readlink -f -- "$f")"
if ! [ -e "$filename" ]; then
[ "$force_flag" = "-f" ] || fail "File does not exist: $filename"
else
can_trash "$filename" && trash_file "$filename" || exit 1
fi
done
;;
help) show_usage ;;
version) echo "$version" ;;
list) list_trash "$@" ;;
untrash) untrash_files "$@" ;;
emptyfiles) empty_files "$@" ;;
emptyall) empty_trash "$@" ;;
trash)
for f; do
filename="$(readlink -f -- "$f")"
if ! [ -e "$filename" ]; then
[ "$force_flag" = "-f" ] || fail "File does not exist: $filename"
else
can_trash "$filename" && trash_file "$filename" || exit 1
fi
done
;;
esac
# vim: ts=2:noexpandtab:ft=sh