forked from cdown/clipmenu
/
clipmenud
executable file
·76 lines (64 loc) · 3.24 KB
/
clipmenud
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/bin/bash
# A typical file name restriction is 255 bytes (tmpfs/extX/ntfs/...).
# Timestamp + line count will typically require less than 40 of those.
readonly LINE_LENGTH_LIMIT=210
# Use ⁄ ("fraction slash") for replacing slashes in file names.
readonly SLASH_REPLACER='⁄'
readonly CACHE_DIR=/tmp/clipmenu.$USER/
mkdir -p -m0700 "$CACHE_DIR"
declare -A last_data
declare -A last_filename
while sleep "${CLIPMENUD_SLEEP:-0.5}"; do
for selection in clipboard primary; do
# TODO Evaluating existence just once, would (currently) result in a 2 % performance gain.
# Entirely removing the conditon, would increase performance by 28 %.
if type -p xsel &>/dev/null; then
data=$(xsel --"$selection"; printf x)
# Take ownership of the clipboard, in case the original application
# is unable to serve the clipboard request (due to being suspended,
# etc).
#
# Primary is excluded from the change of ownership as applications
# sometimes act up if clipboard focus is taken away from them --
# for example, urxvt will unhilight text, which is undesirable.
if [[ $selection != primary ]]; then
xsel --"$selection" | xsel -i --"$selection"
fi
else
data=$(xclip -o -sel "$selection"; printf x)
# See above comments about taking ownership of the clipboard for
# context.
if [[ $selection != primary ]]; then
xclip -o -sel "$selection" | xclip -i -sel "$selection"
fi
fi
# We add and remove the x so that trailing newlines are not stripped.
# Otherwise, they would be stripped by the very nature of how POSIX
# defines command substitution.
data=${data%x}
# If $data is blank, return to top of the loop.
[[ $data == *[^[:blank:]]* ]] || continue
# If $data is unchanged, return to top of the loop.
[[ ${last_data[$selection]} == "$data" ]] && continue
# If we were in the middle of doing a selection when the previous poll
# ran, then we may have got a partial clip. This considers selections
# from left to right as well as selections from right to left.
possible_partial=${last_data[$selection]}
if [[ $possible_partial && $data == *"$possible_partial"* ]]; then
rm -- "${last_filename[$selection]}"
fi
lines=$(echo "$data" | wc -l)
# This ALIGNS the line count at the left of any clipboard entry.
filename="$CACHE_DIR/$(printf %2d $lines)"
# We look for the first line matching regex /./ here because we want the
# first line that can provide reasonable context to the user. That is, if
# you have 5 leading lines of whitespace, displaying " (6 lines)" is much
# less useful than displaying "foo (6 lines)", where "foo" is the first
# line in the entry with actionable context.
first_line=$(echo "$data" | sed -n '/./{p;q}' | sed "s#\/#$SLASH_REPLACER#g" | cut -c1-"$LINE_LENGTH_LIMIT")
filename+=" $first_line"
last_data[$selection]=$data
last_filename[$selection]=$filename
printf '%s' "$data" > "$filename"
done
done