about summary refs log tree commit diff stats
path: root/modules/home.legacy/conf/lf/commands/scripts/cow_cp.sh
diff options
context:
space:
mode:
Diffstat (limited to 'modules/home.legacy/conf/lf/commands/scripts/cow_cp.sh')
-rwxr-xr-xmodules/home.legacy/conf/lf/commands/scripts/cow_cp.sh105
1 files changed, 105 insertions, 0 deletions
diff --git a/modules/home.legacy/conf/lf/commands/scripts/cow_cp.sh b/modules/home.legacy/conf/lf/commands/scripts/cow_cp.sh
new file mode 100755
index 00000000..98675b9e
--- /dev/null
+++ b/modules/home.legacy/conf/lf/commands/scripts/cow_cp.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+# shellcheck source=/dev/null
+SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+
+# shellcheck disable=SC2269
+f="$f"
+# shellcheck disable=SC2269
+fx="$fx"
+# shellcheck disable=SC2269
+fs="$fs"
+# shellcheck disable=SC2269
+id="$id"
+
+# source: https://github.com/gokcehan/lf/wiki/Tips#use-copy-on-write-when-possible
+#
+# # FIXME: Add this. The hardest part is in checking, if a file can be reflinked, as fuse and bind mount are hard to
+#          backtrack <2023-08-29>
+
+# # This was very helpful for debugging:
+# log_file="$HOME/lf-reflink-log-$(date +'%Y-%m-%d_%H-%M-%S')"
+# [ -f "$log_file" ] || touch "$log_file"
+# exec 1>> $log_file 2>&1
+# set -x
+
+# In theory, this may fail,
+# but I tested it on selection with 10k files - everything worked (bash)
+# FIXME: This will very likely fail on dash, when the file number > 255 <2023-08-29>
+set -- "$(cat ~/.local/share/lf/files)"
+
+mode="$1"
+shift
+
+if [ "$mode" = 'copy' ]; then
+    # Reflink if all items of selection and the destination are on the
+    # same mount point and it is CoW fs.
+    # (to make sure reflink never fails in first place, so we don't have to
+    # clean up)
+
+    src_targets="$(df --output=target -- "$@" | sed '1d' | sort -u)"
+
+    if [ "$(df --output=target -- "$PWD" | tail -n 1)" = \
+        "$(echo "$src_targets" | tail -n 1)" ] &&
+        (("$(echo "$src_targets" | wc -l)" == 1)) &&
+        [[ "$(df --output=fstype -- "$PWD" | tail -n 1)" =~ ^(btrfs|xfs|zfs)$ ]]; then
+
+        echo 'selected copy and cp reflink paste'
+
+        start=$(date '+%s')
+
+        # Handle same names in dst
+        # TODO parallelism, idk - but exit/return/break won't stop the loop from subshell...
+        for i in "$@"; do
+            name="${i##*/}"
+            original="$name"
+
+            count=0
+            while [ -w "$PWD/$name" ]; do
+                count=$((count + 1))
+                name="$original.~$count~"
+            done
+
+            set +e
+            cp_out="$(cp -rn --reflink=always -- "$i" "$PWD/$name" 2>&1)"
+            set -e
+
+            if [ -n "$cp_out" ]; then
+                lf -remote "send $id echoerr $cp_out"
+                exit 0
+            fi
+        done
+
+        finish=$(($(date '+%s') - start))
+        t=''
+        if ((finish > 2)); then
+            t="${finish}s"
+        fi
+
+        # Or just skip a file when names are the same.
+        # (A LOT faster if you e.g. pasting selection of 10k files)
+        # cp -rn --reflink=always -- "$@" .
+
+        lf -remote "send clear"
+
+        green=$'\u001b[32m'
+        reset=$'\u001b[0m'
+        lf -remote "send $id echo ${green}reflinked!${reset} $t"
+    else
+        echo 'selected copy and lf native paste'
+        lf -remote "send $id paste"
+        lf -remote "send clear"
+    fi
+
+elif [ "$mode" = 'move' ]; then
+    echo 'selected move and lf native paste'
+    lf -remote "send $id paste"
+    lf -remote "send clear"
+fi
+
+# # for debug
+# set +x
+
+lf -remote "send load"
+
+# vim: ft=sh