dotfiles/scripts/clip
rpriven 5b6af65def
Organize scripts and clean up dotfiles
Changes:
- Added 80+ scripts with organized structure
  - payloads/ for third-party pentesting tools
  - pentesting/ for custom security scripts
  - Daily drivers remain flat for fast access
- Converted wes() function to proper script
- Removed .sh extensions from pentesting scripts
- Cleaned up aliases (removed 31 redundant lines)
- Added kanata/, build artifacts to gitignore
- Removed old fre.sh scripts and empty a.out
- Updated configs: helix, tmux, zsh, ulauncher, redshift

Security: All sensitive data excluded via gitignore
2025-11-07 14:48:21 -07:00

528 lines
13 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
# Script Name: clip
# Description: Smart clipboard manager with history, search, and categories
# Usage: clip # Show history with fzf search
# clip pin # Pin current clipboard item
# clip cat 5 # Show 5th history item
# clip search "192" # Search history
# clip clear # Clear history
VERSION="1.0.0"
CLIP_DIR="$HOME/.clipboard"
HISTORY_FILE="$CLIP_DIR/history.txt"
PINS_FILE="$CLIP_DIR/pins.txt"
MAX_HISTORY=100
# Colors
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly RED='\033[0;31m'
readonly CYAN='\033[0;36m'
readonly MAGENTA='\033[0;35m'
readonly BOLD='\033[1m'
readonly NC='\033[0m'
# Initialize clip directory
init_clip() {
if [[ ! -d "$CLIP_DIR" ]]; then
mkdir -p "$CLIP_DIR"
touch "$HISTORY_FILE"
touch "$PINS_FILE"
fi
}
show_help() {
echo -e "${BOLD}clip${NC} - Smart Clipboard Manager v${VERSION}"
echo
echo -e "${BOLD}USAGE:${NC}"
echo " clip [COMMAND]"
echo
echo -e "${BOLD}COMMANDS:${NC}"
echo -e " ${CYAN}(no args)${NC} Show history with fzf search"
echo -e " ${CYAN}pin${NC} Pin current clipboard item"
echo -e " ${CYAN}pins${NC} Show pinned items"
echo -e " ${CYAN}cat N${NC} Show Nth history item"
echo -e " ${CYAN}search TERM${NC} Search history"
echo -e " ${CYAN}list${NC} List recent history (last 20)"
echo -e " ${CYAN}save${NC} Save current clipboard to history"
echo -e " ${CYAN}clear${NC} Clear history"
echo -e " ${CYAN}delete N${NC} Delete Nth history item"
echo -e " ${CYAN}unpin N${NC} Delete Nth pinned item"
echo -e " ${CYAN}stats${NC} Show statistics"
echo -e " ${CYAN}-h, --help${NC} Show this help"
echo
echo -e "${BOLD}EXAMPLES:${NC}"
echo " clip # Interactive search"
echo " clip pin # Pin important item"
echo " clip search \"192.168\" # Find IP addresses"
echo " clip cat 1 # Show most recent"
echo " clip delete 5 # Delete 5th item from history"
echo " clip unpin 1 # Delete 1st pinned item"
echo
echo -e "${BOLD}FEATURES:${NC}"
echo " - Automatic history (monitors clipboard)"
echo " - Pattern detection (URLs, IPs, hashes)"
echo " - Security: Auto-expire sensitive data"
echo " - Pin important items"
echo " - fzf integration for search"
echo
echo -e "${BOLD}NOTES:${NC}"
echo " History: $HISTORY_FILE"
echo " Pins: $PINS_FILE"
echo " Max history: $MAX_HISTORY items"
}
# Detect content type
detect_type() {
local content="$1"
# URL
if [[ "$content" =~ ^https?:// ]]; then
echo "url"
# IPv4
elif [[ "$content" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]; then
echo "ip"
# Hash (32 or 40 or 64 hex chars)
elif [[ "$content" =~ ^[a-f0-9]{32}$ ]] || [[ "$content" =~ ^[a-f0-9]{40}$ ]] || [[ "$content" =~ ^[a-f0-9]{64}$ ]]; then
echo "hash"
# Code (contains common code markers)
elif [[ "$content" =~ (function|const|var|class|def|import|export) ]]; then
echo "code"
# Credential patterns (don't save these!)
elif [[ "$content" =~ (password|secret|token|key|bearer|api[_-]?key) ]]; then
echo "credential"
else
echo "text"
fi
}
# Save item to history
save_to_history() {
local content="$1"
local type=$(detect_type "$content")
# Don't save credentials
if [[ "$type" == "credential" ]]; then
echo -e "${YELLOW}${NC} Sensitive data detected - not saved to history" >&2
return 0
fi
# Don't save empty
if [[ -z "$content" ]]; then
return 0
fi
# Don't save if it's already the most recent
if [[ -f "$HISTORY_FILE" ]]; then
last_entry=$(tail -1 "$HISTORY_FILE" | cut -d'|' -f3-)
if [[ "$last_entry" == "$content" ]]; then
return 0
fi
fi
# Save: timestamp|type|content
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp|$type|$content" >> "$HISTORY_FILE"
# Trim history to max size
if [[ $(wc -l < "$HISTORY_FILE") -gt $MAX_HISTORY ]]; then
tail -n $MAX_HISTORY "$HISTORY_FILE" > "$HISTORY_FILE.tmp"
mv "$HISTORY_FILE.tmp" "$HISTORY_FILE"
fi
}
# Get current clipboard
get_clipboard() {
if command -v xsel &>/dev/null; then
xsel --output --clipboard
elif command -v xclip &>/dev/null; then
xclip -selection clipboard -o
else
echo "Error: No clipboard tool found" >&2
return 1
fi
}
# Set clipboard
set_clipboard() {
if command -v xsel &>/dev/null; then
echo -n "$1" | xsel --input --clipboard
elif command -v xclip &>/dev/null; then
echo -n "$1" | xclip -selection clipboard
else
echo "Error: No clipboard tool found" >&2
return 1
fi
}
# Interactive history browser with fzf
show_history_fzf() {
if ! command -v fzf &>/dev/null; then
echo -e "${RED}Error:${NC} fzf not found" >&2
echo "Install it with: sudo apt install fzf" >&2
exit 1
fi
if [[ ! -f "$HISTORY_FILE" ]] || [[ ! -s "$HISTORY_FILE" ]]; then
echo -e "${YELLOW}No history yet${NC}" >&2
exit 0
fi
# Format for fzf: index | time | type | preview
selected=$(tac "$HISTORY_FILE" | awk -F'|' '
{
idx = NR
time = $1
type = $2
content = $3
for (i=4; i<=NF; i++) content = content "|" $i
# Truncate preview
preview = content
if (length(preview) > 60) {
preview = substr(preview, 1, 60) "..."
}
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
}
' | fzf --height=60% --layout=reverse \
--header="Select item to copy (ESC to cancel)" \
--preview='echo {}' \
--preview-window=up:3:wrap)
if [[ -n "$selected" ]]; then
# Extract the full content
index=$(echo "$selected" | awk '{print $1}')
full_content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
# Copy to clipboard
set_clipboard "$full_content"
echo -e "${GREEN}${NC} Copied to clipboard"
fi
}
# Pin current clipboard
pin_item() {
local content=$(get_clipboard)
if [[ -z "$content" ]]; then
echo -e "${RED}Error:${NC} Clipboard is empty" >&2
exit 1
fi
type=$(detect_type "$content")
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp|$type|$content" >> "$PINS_FILE"
echo -e "${GREEN}${NC} Pinned item (type: $type)"
}
# Show pins
show_pins() {
if [[ ! -f "$PINS_FILE" ]] || [[ ! -s "$PINS_FILE" ]]; then
echo -e "${YELLOW}No pinned items${NC}"
exit 0
fi
echo -e "${BOLD}${CYAN}Pinned Items:${NC}"
echo
if command -v fzf &>/dev/null; then
# Interactive selection
selected=$(cat "$PINS_FILE" | awk -F'|' '
{
idx = NR
time = $1
type = $2
content = $3
for (i=4; i<=NF; i++) content = content "|" $i
preview = content
if (length(preview) > 60) {
preview = substr(preview, 1, 60) "..."
}
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
}
' | fzf --height=60% --layout=reverse \
--header="Select pinned item to copy (ESC to cancel)" \
--preview='echo {}')
if [[ -n "$selected" ]]; then
index=$(echo "$selected" | awk '{print $1}')
full_content=$(sed -n "${index}p" "$PINS_FILE" | cut -d'|' -f3-)
set_clipboard "$full_content"
echo -e "${GREEN}${NC} Copied pinned item to clipboard"
fi
else
# Just list them
cat "$PINS_FILE" | awk -F'|' '
{
idx = NR
time = $1
type = $2
content = $3
for (i=4; i<=NF; i++) content = content "|" $i
preview = content
if (length(preview) > 60) {
preview = substr(preview, 1, 60) "..."
}
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
}
'
fi
}
# Show specific item
show_item() {
local index=$1
if [[ ! -f "$HISTORY_FILE" ]]; then
echo -e "${RED}Error:${NC} No history" >&2
exit 1
fi
content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
if [[ -z "$content" ]]; then
echo -e "${RED}Error:${NC} No item at index $index" >&2
exit 1
fi
echo "$content"
}
# Search history
search_history() {
local query="$1"
if [[ ! -f "$HISTORY_FILE" ]]; then
echo -e "${RED}Error:${NC} No history" >&2
exit 1
fi
echo -e "${BOLD}${CYAN}Search results for: ${query}${NC}"
echo
grep -i "$query" "$HISTORY_FILE" | awk -F'|' '
{
time = $1
type = $2
content = $3
for (i=4; i<=NF; i++) content = content "|" $i
preview = content
if (length(preview) > 60) {
preview = substr(preview, 1, 60) "..."
}
printf "%s | %-10s | %s\n", time, "[" type "]", preview
}
'
}
# List recent items
list_recent() {
if [[ ! -f "$HISTORY_FILE" ]]; then
echo -e "${YELLOW}No history${NC}"
exit 0
fi
echo -e "${BOLD}${CYAN}Recent Clipboard History (last 20):${NC}"
echo
tail -20 "$HISTORY_FILE" | tac | awk -F'|' '
{
idx = NR
time = $1
type = $2
content = $3
for (i=4; i<=NF; i++) content = content "|" $i
preview = content
if (length(preview) > 60) {
preview = substr(preview, 1, 60) "..."
}
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
}
'
}
# Statistics
show_stats() {
if [[ ! -f "$HISTORY_FILE" ]]; then
echo -e "${YELLOW}No history${NC}"
exit 0
fi
total=$(wc -l < "$HISTORY_FILE")
pins=$(wc -l < "$PINS_FILE" 2>/dev/null || echo 0)
echo -e "${BOLD}${CYAN}Clipboard Statistics:${NC}"
echo
echo " Total items: $total"
echo " Pinned items: $pins"
echo " Max history: $MAX_HISTORY"
echo
echo -e "${BOLD}${CYAN}By Type:${NC}"
awk -F'|' '{print $2}' "$HISTORY_FILE" | sort | uniq -c | sort -rn | awk '
{printf " %-15s %d\n", $2, $1}'
}
# Delete specific item from history
delete_item() {
local index=$1
if [[ ! -f "$HISTORY_FILE" ]]; then
echo -e "${RED}Error:${NC} No history" >&2
exit 1
fi
total=$(wc -l < "$HISTORY_FILE")
if [[ $index -lt 1 ]] || [[ $index -gt $total ]]; then
echo -e "${RED}Error:${NC} Invalid index (1-$total)" >&2
exit 1
fi
# Show what we're deleting
content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
preview="${content:0:60}"
if [[ ${#content} -gt 60 ]]; then
preview="${preview}..."
fi
echo -e "${YELLOW}Delete item $index:${NC} $preview"
echo -n "Continue? (y/N) "
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
# Delete line (remember tac reverses order)
actual_line=$((total - index + 1))
sed -i "${actual_line}d" "$HISTORY_FILE"
echo -e "${GREEN}${NC} Deleted item $index"
else
echo "Cancelled"
fi
}
# Delete pinned item
delete_pin() {
local index=$1
if [[ ! -f "$PINS_FILE" ]] || [[ ! -s "$PINS_FILE" ]]; then
echo -e "${YELLOW}No pinned items${NC}"
exit 0
fi
total=$(wc -l < "$PINS_FILE")
if [[ $index -lt 1 ]] || [[ $index -gt $total ]]; then
echo -e "${RED}Error:${NC} Invalid index (1-$total)" >&2
exit 1
fi
# Show what we're deleting
content=$(sed -n "${index}p" "$PINS_FILE" | cut -d'|' -f3-)
preview="${content:0:60}"
if [[ ${#content} -gt 60 ]]; then
preview="${preview}..."
fi
echo -e "${YELLOW}Unpin item $index:${NC} $preview"
echo -n "Continue? (y/N) "
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
sed -i "${index}d" "$PINS_FILE"
echo -e "${GREEN}${NC} Unpinned item $index"
else
echo "Cancelled"
fi
}
# Clear history
clear_history() {
echo -n "Clear clipboard history? (y/N) "
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
> "$HISTORY_FILE"
echo -e "${GREEN}${NC} History cleared"
else
echo "Cancelled"
fi
}
# Initialize
init_clip
# Parse command
if [[ $# -eq 0 ]]; then
show_history_fzf
exit 0
fi
case $1 in
-h|--help|help)
show_help
;;
save)
content=$(get_clipboard)
save_to_history "$content"
echo -e "${GREEN}${NC} Saved to history"
;;
pin)
pin_item
;;
pins)
show_pins
;;
cat)
if [[ $# -lt 2 ]]; then
echo -e "${RED}Error:${NC} Item index required" >&2
exit 1
fi
show_item "$2"
;;
search|s)
if [[ $# -lt 2 ]]; then
echo -e "${RED}Error:${NC} Search query required" >&2
exit 1
fi
shift
search_history "$*"
;;
list|ls|l)
list_recent
;;
delete|del|rm)
if [[ $# -lt 2 ]]; then
echo -e "${RED}Error:${NC} Item index required" >&2
exit 1
fi
delete_item "$2"
;;
unpin)
if [[ $# -lt 2 ]]; then
echo -e "${RED}Error:${NC} Item index required" >&2
exit 1
fi
delete_pin "$2"
;;
clear)
clear_history
;;
stats)
show_stats
;;
*)
echo -e "${RED}Error:${NC} Unknown command: $1" >&2
echo "Run 'clip --help' for usage" >&2
exit 1
;;
esac