shclip/shclip-daemon

206 lines
5.1 KiB
Bash
Executable File

#!/bin/sh
# shclip, by yosh
# https://git.unix.dog/yosh/shclip
set -eu
TAB=" "
BN="${0##*/}"
# config file if exists
SHCLIP_CONFIG_DIR="${SHCLIP_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/shclip}"
. "$SHCLIP_CONFIG_DIR/config"
SHCLIP_MAX_CLIPS="${SHCLIP_MAX_CLIPS:-25}"
SHCLIP_MEMORY_LIMIT="${SHCLIP_MEMORY_LIMIT:-4100}" # memory limit in kb
SHCLIP_ENABLED="${SHCLIP_ENABLED-1}"
SHCLIP_FILTER_ENABLED="${SHCLIP_FILTER_ENABLED-}"
SHCLIP_SYNC_CLIPBOARD="${SHCLIP_SYNC_CLIPBOARD-1}"
SHCLIP_CACHE_PASSWORDS="${SHCLIP_CACHE_PASSWORDS-}"
SHCLIP_TEMP_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/shclip-$USER-$DISPLAY"
errecho() { printf '%s\n' "$*" >&2; }
exists() { command -v "$@" >/dev/null 2>&1; }
notify() {
errecho "$*"
exists notify-send && notify-send "$*"
:
}
fail() {
notify "error: $BN: $*"
exit 1
}
cleanup() {
trap 'exit' INT HUP QUIT TERM EXIT
set +e
rmdir "$SHCLIP_TEMP_DIR/lock"
kill "$clipnotify_pid"
exit
}
toggle() {
if [ -z "$SHCLIP_ENABLED" ]; then
SHCLIP_ENABLED=1
notify "shclip enabled!"
else
SHCLIP_ENABLED=""
notify "shclip disabled!"
fi
kill "$clipnotify_pid"
}
filter_toggle() {
if [ -z "$SHCLIP_FILTER_ENABLED" ]; then
SHCLIP_FILTER_ENABLED=1
notify "shclip filter enabled!"
else
SHCLIP_FILTER_ENABLED=""
notify "shclip filter disabled!"
fi
kill "$clipnotify_pid"
}
# setup process
setup() {
mkdir -p -m 700 "$SHCLIP_TEMP_DIR"
# test the temp/lock directory
if mkdir -m 700 "$SHCLIP_TEMP_DIR/lock"; then
errecho "successfully acquired lock"
else
fail "cannot acquire lock. is another instance running?
if not, delete the directory at $SHCLIP_TEMP_DIR/lock"
fi
trap 'cleanup' INT HUP QUIT TERM EXIT
trap 'toggle' USR1
trap 'filter_toggle' USR2
BUFFER="$SHCLIP_TEMP_DIR/buffer"
ORDER="$SHCLIP_TEMP_DIR/order"
PID="$SHCLIP_TEMP_DIR/pid"
printf '%s' $$ > "$PID"
# create ORDER file
if [ ! -f "$ORDER" ]; then
digits=${#SHCLIP_MAX_CLIPS}
i=1
while [ "$i" -le "$SHCLIP_MAX_CLIPS" ]; do
printf "%0${digits}d.clip\n" "$i"
i="$((i + 1))"
done > "$ORDER"
fi
}
# reclip top of the stack
reclip() {
n="$(head -n 1 "$ORDER")"
mime="$(file -bi "$SHCLIP_TEMP_DIR/$n")"
xclip -sel clipboard -t "${mime%%;*}" "$SHCLIP_TEMP_DIR/$n"
}
updateclip() {
# some good variables to have
firstind="$(head -n 1 "$ORDER")" # top of clip stack
lastind="$(tail -n 1 "$ORDER")" # bottom of stack
targets="$(xclip -sel clipboard -t TARGETS -o)" || { reclip && return 0; } # targets of clip, will also return if clip doesn't exist
# this covers if we've copied a file in a GUI file manager
# probably don't want that in your history
case "$targets" in
*gnome-copied-files*) return 0 ;;
*image/png*) xclip -sel clipboard -t image/png -o > "$BUFFER" ;;
*x-kde-passwordManagerHint*)
if [ -n "$SHCLIP_CACHE_PASSWORDS" ]; then
xclip -sel clipboard -t x-kde-passwordManagerHint -o > "$BUFFER"
else return 0
fi
;;
*) xclip -sel clipboard -o > "$BUFFER" ;;
esac
# check if clip exceeds memory limit
sz="$(du -k "$BUFFER")"
if [ "${sz%%${TAB}*}" -gt "$SHCLIP_MEMORY_LIMIT" ]; then
notify 'clip exceeds memory threshold! not caching...'
rm -f "$BUFFER"
return 0
fi
# buffer mime matching
mime="$(file -bi "$BUFFER")"
case "${mime%%;}" in
inode/x-empty*) # empty clip
reclip
return 0
;;
text*)
# apply text filters
[ -n "$SHCLIP_FILTER_ENABLED" ] && [ -f "$SHCLIP_CONFIG_DIR/filter.sh" ] && \
sh "$SHCLIP_CONFIG_DIR/filter.sh" < "$BUFFER" | diff "$BUFFER" - | patch "$BUFFER"
xclip -sel clipboard "$BUFFER"
[ -n "$SHCLIP_SYNC_CLIPBOARD" ] && xclip -sel primary "$BUFFER"
;;
*) ;;
esac
# top of stack & buffer are identical, don't cache
if cmp "$BUFFER" "$SHCLIP_TEMP_DIR/$firstind" >/dev/null 2>&1; then
errecho 'duplicate clip! not caching...'
rm -f "$BUFFER"
return 0
fi
mv -f "$BUFFER" "$SHCLIP_TEMP_DIR/$lastind"
# edit order file in place
ed -s "$ORDER" <<-EOF
${SHCLIP_MAX_CLIPS}m0
wq
EOF
}
[ -z "${DISPLAY:-}" ] && fail 'Cannot find $DISPLAY. Is your X server running?'
# parse options
while getopts :hn:f:s:p:c:m OPT; do
case $OPT in
h) cat >&2 <<-EOF
please refer to README for general usage:
https://git.unix.dog/yosh/shclip
options that exist (overrides config file):
-h show this help
-n <on|off> enable/disable shclip on startup
-f <on|off> enable/disable filtering
-s <on|off> enable/disable syncing clipboard to primary
-p <on|off> enable/disable caching kde passwords
-c <num> set max clips to <num> clips
-m <num> set memory limit to <num> kibibytes
EOF
;;
n) [ "$OPTARG" = "on" ] && SHCLIP_ENABLED=1 || SHCLIP_ENABLED="" ;;
f) [ "$OPTARG" = "on" ] && SHCLIP_FILTER_ENABLED=1 || SHCLIP_FILTER_ENABLED="" ;;
s) [ "$OPTARG" = "on" ] && SHCLIP_SYNC_CLIPBOARD=1 || SHCLIP_SYNC_CLIPBOARD="" ;;
p) [ "$OPTARG" = "on" ] && SHCLIP_CACHE_PASSWORDS=1 || SHCLIP_CACHE_PASSWORDS="" ;;
c) SHCLIP_MAX_CLIPS=$OPTARG ;;
m) SHCLIP_MEMORY_LIMIT=$OPTARG ;;
*) fail "$BN: unknown option: -$OPTARG" ;;
esac
done
setup
while
if [ -z "$SHCLIP_ENABLED" ]; then
:
else
updateclip
fi
do
set +e
clipnotify -s clipboard &
clipnotify_pid=$!
wait
set -e
done