From b73e742c5058ac6ce29183636edd201fc294db50 Mon Sep 17 00:00:00 2001 From: Chris Hobbs Date: Fri, 9 Mar 2018 20:50:48 +0000 Subject: [PATCH 1/7] Allow use as a rofi script (#68) * Allow use as a rofi script * fixup! Allow use as a rofi script * fixup! Allow use as a rofi script * fixup! Allow use as a rofi script --- clipmenu | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/clipmenu b/clipmenu index 3d245a4..b039207 100755 --- 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 From 72760da7a46ac570c220eb333dad40be1b5f68b7 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 11 Mar 2018 19:56:25 -0400 Subject: [PATCH 2/7] Add clipdel utility --- clipdel | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 clipdel diff --git a/clipdel b/clipdel new file mode 100755 index 0000000..a0079cc --- /dev/null +++ b/clipdel @@ -0,0 +1,83 @@ +#!/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 + mapfile -t match_cksums < <( + for match in "${matches[@]}"; do cksum <<< "${line}"; done + ) + + flock -x -w "$lock_timeout" "$lock_fd" || exit + + for match in "${matches[@]}"; do + ck=$(cksum <<< "$line") + 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 From 486dbe31fa2725890a825f21b6bfbd1078a30de4 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 11 Mar 2018 19:58:58 -0400 Subject: [PATCH 3/7] Recover without restarting if we deleted the entire clip directory --- clipmenud | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clipmenud b/clipmenud index 3f0e950..3c4d27b 100755 --- a/clipmenud +++ b/clipmenud @@ -190,6 +190,12 @@ while true; do cache_file_output="$(date +%s%N) $first_line" filename="$cache_dir/$(cksum <<< "$first_line")" + + # 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" From aae0e6e816f5643177cbe1067afb7c814afeb64b Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 11 Mar 2018 20:21:53 -0400 Subject: [PATCH 4/7] Add note about clipdel to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 41f3796..dc9ba7d 100644 --- 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 From 0b003d4ad2b889e6a6201d3f4eeceaea254f0529 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Sun, 11 Mar 2018 20:24:37 -0400 Subject: [PATCH 5/7] Fix errant handling of specific-file deletion --- clipdel | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clipdel b/clipdel index a0079cc..ebdc51b 100755 --- a/clipdel +++ b/clipdel @@ -57,14 +57,10 @@ else ) if (( CM_REAL_DELETE )); then - mapfile -t match_cksums < <( - for match in "${matches[@]}"; do cksum <<< "${line}"; done - ) - flock -x -w "$lock_timeout" "$lock_fd" || exit for match in "${matches[@]}"; do - ck=$(cksum <<< "$line") + ck=$(cksum <<< "$match") rm -f -- "$cache_dir/$ck" done From d82337d226939a5cbeeb6de1b6708b39bb7a0296 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Tue, 10 Apr 2018 11:07:01 +0100 Subject: [PATCH 6/7] Add more debugging information when cache entry is missing This would help with #73. --- clipmenu | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clipmenu b/clipmenu index b039207..0475705 100755 --- a/clipmenu +++ b/clipmenu @@ -59,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 From 95cf774b0d83fe61f64bebf4d65b0f69a9719330 Mon Sep 17 00:00:00 2001 From: Gravemind Date: Thu, 19 Apr 2018 10:00:21 +0000 Subject: [PATCH 7/7] Fix wrong file deleted after partial selection (#76) It was deleting the file before the partial selection because `last_filename` was saved before filename was actually ready (so was actually saving the one before the last). Fixes #75. --- clipmenud | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clipmenud b/clipmenud index 3c4d27b..4eba9ac 100755 --- a/clipmenud +++ b/clipmenud @@ -181,9 +181,6 @@ 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\"" @@ -191,6 +188,9 @@ while true; do 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