200 lines
5.2 KiB
Bash
Executable File
200 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# himi installer
|
|
# Installs the himi CLI, word lists, and shell completions
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
BIN_DIR="${HOME}/.local/bin"
|
|
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/himi"
|
|
BASH_COMPLETION_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions"
|
|
ZSH_COMPLETION_DIR="${HOME}/.zfunc"
|
|
|
|
# Define markers for removal later
|
|
MARKER_START="# >>> himi initialize >>>"
|
|
MARKER_END="# <<< himi initialize <<<"
|
|
|
|
# Colors
|
|
if [[ -t 1 ]]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
DIM='\033[0;90m'
|
|
NC='\033[0m'
|
|
else
|
|
RED='' GREEN='' YELLOW='' DIM='' NC=''
|
|
fi
|
|
|
|
err() { printf "${RED}error:${NC} %s\n" "$1" >&2; }
|
|
ok() { printf "${GREEN}✓${NC} %s\n" "$1" >&2; }
|
|
warn() { printf "${YELLOW}!${NC} %s\n" "$1" >&2; }
|
|
dim() { printf "${DIM}%s${NC}" "$1"; }
|
|
|
|
show_splash() {
|
|
local splash="${SCRIPT_DIR}/splash.txt"
|
|
[[ -f "$splash" ]] || return 0
|
|
if [[ -t 1 ]]; then
|
|
printf '\033[38;5;197m'
|
|
while IFS= read -r line; do
|
|
printf '%s\n' "$line"
|
|
sleep 0.02
|
|
done < "$splash"
|
|
printf '\033[0m'
|
|
sleep 1
|
|
else
|
|
cat "$splash"
|
|
echo
|
|
fi
|
|
}
|
|
|
|
detect_shell() {
|
|
# Check the parent process or SHELL env var instead of local vars
|
|
if [[ "${SHELL:-}" == */zsh ]]; then
|
|
echo "zsh"
|
|
elif [[ "${SHELL:-}" == */bash ]]; then
|
|
echo "bash"
|
|
else
|
|
echo "bash" # Safe default
|
|
fi
|
|
}
|
|
|
|
check_deps() {
|
|
dim "Checking dependencies... "
|
|
if ! command -v shuf &>/dev/null && ! command -v gshuf &>/dev/null; then
|
|
echo
|
|
err "himi requires GNU coreutils (shuf)."
|
|
[[ "$OSTYPE" == "darwin"* ]] && warn "Install with: brew install coreutils"
|
|
exit 1
|
|
fi
|
|
echo "done."
|
|
}
|
|
|
|
install_binary() {
|
|
[[ -f "${SCRIPT_DIR}/himi" ]] || { err "himi binary not found in installer directory"; return 1; }
|
|
|
|
mkdir -p "$BIN_DIR"
|
|
if cp "${SCRIPT_DIR}/himi" "$BIN_DIR/"; then
|
|
chmod +x "$BIN_DIR/himi"
|
|
ok "Installed himi to $BIN_DIR"
|
|
else
|
|
err "Failed to copy himi to $BIN_DIR"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_config() {
|
|
mkdir -p "${CONFIG_DIR}/lists" "${CONFIG_DIR}/pool"
|
|
|
|
if [[ -f "${SCRIPT_DIR}/config.sh" ]]; then
|
|
if [[ ! -f "${CONFIG_DIR}/config.sh" ]]; then
|
|
cp "${SCRIPT_DIR}/config.sh" "${CONFIG_DIR}/"
|
|
ok "Installed default config to $CONFIG_DIR"
|
|
elif ! cmp -s "${SCRIPT_DIR}/config.sh" "${CONFIG_DIR}/config.sh"; then
|
|
cp "${SCRIPT_DIR}/config.sh" "${CONFIG_DIR}/config.sh.new"
|
|
warn "Kept your existing config; new default saved as config.sh.new"
|
|
warn "Merge what you want, then set CONFIG_VERSION to match (himi will nag until you do)"
|
|
else
|
|
dim "Config already up to date, skipping.\n"
|
|
fi
|
|
else
|
|
err "config.sh missing from installer directory"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_lists() {
|
|
local lists_src="${SCRIPT_DIR}/lists"
|
|
local count=0
|
|
|
|
if [[ -d "$lists_src" ]]; then
|
|
for f in "${lists_src}"/*.txt; do
|
|
[[ -f "$f" ]] || continue
|
|
# Use count+=1 to avoid the exit code 1 issue with ((count++)) when count is 0
|
|
cp "$f" "${CONFIG_DIR}/lists/" && ((count+=1)) || true
|
|
done
|
|
ok "Installed $count wordlists"
|
|
fi
|
|
|
|
# Enable defaults
|
|
local enabled=0
|
|
for name in eff bips; do
|
|
if [[ -f "${CONFIG_DIR}/lists/${name}.txt" ]]; then
|
|
ln -sf "../lists/${name}.txt" "${CONFIG_DIR}/pool/${name}.txt" && ((enabled+=1)) || true
|
|
fi
|
|
done
|
|
[[ $enabled -gt 0 ]] && ok "Enabled default lists (eff, bips)"
|
|
}
|
|
|
|
install_completions() {
|
|
local shell_type=$(detect_shell)
|
|
local profile=""
|
|
local comp_src=""
|
|
local comp_dest=""
|
|
local installed=""
|
|
|
|
case "$shell_type" in
|
|
bash)
|
|
profile="${HOME}/.bashrc"
|
|
comp_src="${SCRIPT_DIR}/himi.bash"
|
|
comp_dest="${BASH_COMPLETION_DIR}/himi"
|
|
mkdir -p "$BASH_COMPLETION_DIR"
|
|
;;
|
|
zsh)
|
|
profile="${HOME}/.zshrc"
|
|
comp_src="${SCRIPT_DIR}/_himi"
|
|
comp_dest="${ZSH_COMPLETION_DIR}/_himi"
|
|
mkdir -p "$ZSH_COMPLETION_DIR"
|
|
;;
|
|
esac
|
|
|
|
if [[ -n "$comp_src" && -f "$comp_src" ]]; then
|
|
if cp "$comp_src" "$comp_dest"; then
|
|
installed="$shell_type"
|
|
fi
|
|
fi
|
|
|
|
[[ -z "$installed" ]] && return
|
|
|
|
# Shell Environment Hook
|
|
touch "$profile"
|
|
if ! grep -q "$MARKER_START" "$profile"; then
|
|
{
|
|
printf "\n%s\n" "$MARKER_START"
|
|
printf 'export PATH="${HOME}/.local/bin:${PATH}"\n'
|
|
if [[ "$installed" == "bash" ]]; then
|
|
printf '[[ -f "%s" ]] && source "%s"\n' "$comp_dest" "$comp_dest"
|
|
else
|
|
printf 'fpath=(%s $fpath)\nautoload -Uz compinit && compinit\n' "$ZSH_COMPLETION_DIR"
|
|
fi
|
|
printf "%s\n" "$MARKER_END"
|
|
} >> "$profile"
|
|
ok "Updated $profile with PATH and completions"
|
|
else
|
|
ok "Completions already configured in $profile"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
show_splash
|
|
printf "Installing himi...\n\n"
|
|
|
|
check_deps
|
|
install_binary
|
|
install_config
|
|
install_lists
|
|
install_completions
|
|
|
|
# Initialize the pool so the tool works immediately
|
|
dim "Initializing wordlist pool... "
|
|
if "${BIN_DIR}/himi" rebuild >/dev/null 2>&1; then
|
|
echo "done."
|
|
else
|
|
echo "failed."
|
|
warn "Initial pool rebuild failed. You may need to run 'himi rebuild' manually."
|
|
fi
|
|
|
|
printf "\n${GREEN}Done!${NC} Restart your terminal or run: source %s\n" "$( [[ "$(detect_shell)" == "zsh" ]] && echo '~/.zshrc' || echo '~/.bashrc' )"
|
|
}
|
|
|
|
main "$@"
|