commit 8388a00ab366dfa0660ed11d6560959a44686def
parent 09e654731efb4645277baa7c3653606c8cf2bd26
Author: Chris Down <chris@chrisdown.name>
Date: Sun, 22 Apr 2018 10:10:57 +0100
Merge branch 'release/5.2.0'
Diffstat:
M | README.md | | | 2 | ++ |
A | clipdel | | | 79 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | clipmenu | | | 36 | +++++++++++++++++++++++++++--------- |
M | clipmenud | | | 12 | +++++++++--- |
4 files changed, 117 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
@@ -21,6 +21,8 @@ invoke clipmenu in exactly the same way to get the same effect, like so:
clipmenu -i -fn Terminus:size=8 -nb '#002b36' -nf '#839496' -sb '#073642' -sf '#93a1a1'
+You can remove clips with the `clipdel` utility, see `clipdel --help`.
+
# How does it work?
The code is fairly simple and easy to follow, you may find it easier to read
diff --git a/clipdel b/clipdel
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+: "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
+CM_REAL_DELETE=0
+[[ $1 == -d ]] && CM_REAL_DELETE=1
+
+major_version=5
+
+shopt -s nullglob
+
+cache_dir=$CM_DIR/clipmenu.$major_version.$USER
+cache_file_prefix=$cache_dir/line_cache
+lock_file=$cache_dir/lock
+lock_timeout=2
+
+if [[ $1 == --help ]] || [[ $1 == -h ]]; then
+ cat << 'EOF'
+clipdel deletes clipmenu entries matching a regex. By default, just lists what
+it would delete, pass -d to do it for real.
+
+".*" is special, it will just nuke the entire data directory, including the
+line caches and all other state.
+
+Arguments:
+
+ -d Delete for real.
+
+Environment variables:
+
+- $CM_DIR: specify the base directory to store the cache dir in (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp)
+EOF
+ exit 0
+fi
+
+line_cache_files=( "$cache_file_prefix"_* )
+
+if (( ${#line_cache_files[@]} == 0 )); then
+ printf '%s\n' "No line cache files found, no clips exist" >&2
+ exit 0 # Well, this is a kind of success...
+fi
+
+# https://github.com/koalaman/shellcheck/issues/1141
+# shellcheck disable=SC2124
+raw_pattern=${@: -1}
+esc_pattern=${raw_pattern/\#/'\#'}
+
+exec {lock_fd}> "$lock_file"
+
+if (( CM_REAL_DELETE )) && [[ "$raw_pattern" == ".*" ]]; then
+ flock -x -w "$lock_timeout" "$lock_fd" || exit
+ rm -rf -- "$cache_dir"
+ exit 0
+else
+ mapfile -t matches < <(
+ cat "${line_cache_files[@]}" | cut -d' ' -f2- | sort -u |
+ sed -n "\\#${esc_pattern}#p"
+ )
+
+ if (( CM_REAL_DELETE )); then
+ flock -x -w "$lock_timeout" "$lock_fd" || exit
+
+ for match in "${matches[@]}"; do
+ ck=$(cksum <<< "$match")
+ rm -f -- "$cache_dir/$ck"
+ done
+
+ for file in "${line_cache_files[@]}"; do
+ temp=$(mktemp)
+ cut -d' ' -f2- < "$file" | sed "\\#${esc_pattern}#d" > "$temp"
+ mv -- "$temp" "$file"
+ done
+
+ flock -u "$lock_fd"
+ else
+ if (( ${#matches[@]} )); then
+ printf '%s\n' "${matches[@]}"
+ fi
+ fi
+fi
diff --git a/clipmenu b/clipmenu
@@ -30,14 +30,28 @@ if [[ "$CM_LAUNCHER" == rofi ]]; then
set -- -dmenu "$@"
fi
-# It's okay to hardcode `-l 8` here as a sensible default without checking
-# whether `-l` is also in "$@", because the way that dmenu works allows a later
-# argument to override an earlier one. That is, if the user passes in `-l`, our
-# one will be ignored.
-chosen_line=$(
- cat "$cache_file_prefix"_* /dev/null | LC_ALL=C sort -rnk 1 |
- cut -d' ' -f2- | awk '!seen[$0]++' | "$CM_LAUNCHER" -l 8 "$@"
-)
+list_clips() {
+ cat "$cache_file_prefix"_* /dev/null | LC_ALL=C sort -rnk 1 | cut -d' ' -f2- | awk '!seen[$0]++'
+}
+
+if [[ "$CM_LAUNCHER" == rofi-script ]]; then
+ if ! (( $# )); then
+ list_clips
+ exit
+ else
+ # https://github.com/koalaman/shellcheck/issues/1141
+ # shellcheck disable=SC2124
+ chosen_line="${@: -1}"
+ fi
+else
+ # It's okay to hardcode `-l 8` here as a sensible default without checking
+ # whether `-l` is also in "$@", because the way that dmenu works allows a later
+ # argument to override an earlier one. That is, if the user passes in `-l`, our
+ # one will be ignored.
+ chosen_line=$(
+ list_clips | "$CM_LAUNCHER" -l 8 "$@"
+ )
+fi
[[ $chosen_line ]] || exit 1
@@ -45,7 +59,11 @@ file=$cache_dir/$(cksum <<< "$chosen_line")
if ! [[ -f "$file" ]]; then
# We didn't find this in cache
- printf 'FATAL: %s not in cache\n' "$chosen_line" >&2
+ printf 'FATAL: %s not in cache (%s missing)\n' "$chosen_line" "$file" >&2
+ printf 'Please report the following debug information:\n\n' >&2
+ wc -l "$cache_file_prefix"_* >&2
+ grep -nFR "$chosen_line" "$cache_dir" >&2
+ stat "$file" >&2
exit 2
fi
diff --git a/clipmenud b/clipmenud
@@ -181,15 +181,21 @@ while true; do
rm -- "${last_filename[$selection]}"
fi
- last_data[$selection]=$data
- last_filename[$selection]=$filename
-
first_line=$(get_first_line "$data")
debug "New clipboard entry on $selection selection: \"$first_line\""
cache_file_output="$(date +%s%N) $first_line"
filename="$cache_dir/$(cksum <<< "$first_line")"
+
+ last_data[$selection]=$data
+ last_filename[$selection]=$filename
+
+ # Recover without restart if we deleted the entire clip dir.
+ # It's ok that this only applies to the final directory.
+ # shellcheck disable=SC2174
+ mkdir -p -m0700 "$cache_dir"
+
debug "Writing $data to $filename"
printf '%s' "$data" > "$filename"