dmenu-recent (2216B)
1 #!/bin/sh 2 set -eu 3 4 DMENU_CMD="dmenu -i -p > -fn monospace:size=12 -nb #222222 -nf #bbbbbb -sb #005577 -sf #eeeeee" 5 TERMINAL="st -e" 6 MAX_RECENT=199 # Number of recent commands to track 7 8 CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/dmenu-recent" 9 RECENT_CACHE="$CACHE_DIR/recent" 10 REST_CACHE="$CACHE_DIR/all" 11 KNOWN_TYPES=" background terminal terminal_hold " # end spaces needed for case match in get_types 12 13 CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/dmenu-recent" 14 mkdir -p "$CACHE_DIR" 15 mkdir -p "$CONFIG_DIR" 16 touch "$RECENT_CACHE" 17 18 IFS=: 19 if stest -dqr -n "$REST_CACHE" $PATH 2>/dev/null; then 20 stest -flx $PATH | sort -u | grep -vf "$RECENT_CACHE" > "$REST_CACHE" 21 fi 22 23 IFS=' ' 24 CMD=$(cat "$RECENT_CACHE" "$REST_CACHE" | $DMENU_CMD) || exit 25 26 # Sanitize the input 27 CMD_NAME=$(printf "%s" "$CMD" | cut -f1 -d' ') # Extract the command name 28 if ! command -v "$CMD_NAME" >/dev/null 2>&1; then 29 echo "Invalid command: $CMD_NAME" >&2 30 exit 1 31 fi 32 33 # Ensure input doesn't contain unwanted symbols 34 if printf "%s" "$CMD" | grep -q '[!$`(){};&|\*?<>]'; then 35 echo "Invalid characters in command: $CMD" >&2 36 exit 1 37 fi 38 39 if ! grep -qx "$CMD" "$RECENT_CACHE" >/dev/null 2>&1; then 40 grep -vx "$CMD" "$REST_CACHE" > "$REST_CACHE.$$" 41 mv "$REST_CACHE.$$" "$REST_CACHE" 42 fi 43 44 echo "$CMD" > "$RECENT_CACHE.$$" 45 grep -vx "$CMD" "$RECENT_CACHE" | head -n "$MAX_RECENT" >> "$RECENT_CACHE.$$" 46 mv "$RECENT_CACHE.$$" "$RECENT_CACHE" 47 48 WORD0=${CMD%% *} 49 50 MATCH="^$WORD0$" 51 52 get_type () { 53 TYPE="" 54 while TYPE=$(echo "$KNOWN_TYPES" | xargs -n1 | $DMENU_CMD -p 'Type:'); do 55 case " $KNOWN_TYPES " in 56 *" $TYPE "*) ;; 57 *) continue ;; 58 esac 59 echo "$WORD0" >> "$CONFIG_DIR/$TYPE" 60 break 61 done 62 echo $TYPE 63 } 64 65 TYPE=$(grep -lx "$MATCH" -R "$CONFIG_DIR" 2>/dev/null || true) 66 67 if [ -z "$TYPE" ]; then 68 TYPE=$(get_type) 69 else 70 TYPE=$(basename "$TYPE") 71 if ! echo "$KNOWN_TYPES" | grep -q " $TYPE "; then 72 rm -f "$CONFIG_DIR/$TYPE" 73 TYPE=$(get_type) 74 fi 75 fi 76 77 case "$TYPE" in 78 background) 79 exec $CMD 80 ;; 81 terminal) 82 exec $TERMINAL "$CMD" 83 ;; 84 terminal_hold) 85 exec $TERMINAL sh -c "$CMD && echo 'Press Enter to kill me...' && read _" 86 ;; 87 esac