dotfiles

personal configuration files and scripts
git clone https://tongong.net/git/dotfiles.git
Log | Files | Refs | README

clipdel (2564B)


      1 #!/usr/bin/env bash
      2 
      3 : "${CM_DIR="${XDG_RUNTIME_DIR-"${TMPDIR-/tmp}"}"}"
      4 CM_REAL_DELETE=0
      5 if [[ $1 == -d ]]; then
      6     CM_REAL_DELETE=1
      7     shift
      8 fi
      9 
     10 major_version=6
     11 
     12 shopt -s nullglob
     13 
     14 cache_dir=$CM_DIR/clipmenu.$major_version.$USER
     15 cache_file=$cache_dir/line_cache
     16 lock_file=$cache_dir/lock
     17 lock_timeout=2
     18 
     19 if [[ $1 == --help ]] || [[ $1 == -h ]]; then
     20     cat << 'EOF'
     21 clipdel deletes clipmenu entries matching a regex. By default, just lists what
     22 it would delete, pass -d to do it for real. If no pattern is passed as an argument,
     23 it will try to read one from standard input.
     24 
     25 ".*" is special, it will just nuke the entire data directory, including the
     26 line caches and all other state.
     27 
     28 Arguments:
     29 
     30     -d  Delete for real.
     31 
     32 Environment variables:
     33 
     34 - $CM_DIR: specify the base directory to store the cache dir in (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp)
     35 EOF
     36     exit 0
     37 fi
     38 
     39 if ! [[ -f $cache_file ]]; then
     40     printf '%s\n' "No line cache file found, no clips exist" >&2
     41     exit 0  # Well, this is a kind of success...
     42 fi
     43 
     44 if [[ -n $1 ]]; then
     45     raw_pattern=$1
     46 elif ! [[ -t 0 ]]; then
     47     IFS= read -r raw_pattern
     48 fi
     49 
     50 esc_pattern=${raw_pattern//\#/'\#'}
     51 
     52 # We use 2 separate sed commands so "esc_pattern" matches only the 'clip' text
     53 # without the timestamp (e.g. $> clipdel '^delete_exact_match$')
     54 sed_common_command="s#^[0-9]\+ ##;\\#${esc_pattern}#"
     55 
     56 if ! [[ $raw_pattern ]]; then
     57     printf '%s\n' 'No pattern provided, see --help' >&2
     58     exit 2
     59 fi
     60 
     61 exec {lock_fd}> "$lock_file"
     62 
     63 if (( CM_REAL_DELETE )) && [[ "$raw_pattern" == ".*" ]]; then
     64     flock -x -w "$lock_timeout" "$lock_fd" || exit
     65     rm -rf -- "$cache_dir"
     66     mkdir -p -- "$cache_dir"
     67     exit 0
     68 else
     69     mapfile -t matches < <(
     70         sed -n "${sed_common_command}p" "$cache_file" |
     71             sort -u
     72     )
     73 
     74     if (( CM_REAL_DELETE )); then
     75         flock -x -w "$lock_timeout" "$lock_fd" || exit
     76 
     77         for match in "${matches[@]}"; do
     78             ck=$(cksum <<< "$match")
     79             rm -f -- "$cache_dir/$ck"
     80         done
     81 
     82         temp=$(mktemp)
     83         # sed 'h' and 'g' here means save and restore the line, so
     84         # timestamps are not removed from non-deleted lines. 'd' deletes the
     85         # line and restarts, skipping 'g'/restore.
     86         # https://www.gnu.org/software/sed/manual/html_node/Other-Commands.html#Other-Commands
     87         sed "h;${sed_common_command}d;g" "$cache_file" > "$temp"
     88         mv -- "$temp" "$cache_file"
     89 
     90         flock -u "$lock_fd"
     91     else
     92         if (( ${#matches[@]} )); then
     93             printf '%s\n' "${matches[@]}"
     94         fi
     95     fi
     96 fi