#!/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" else dim "Config already exists, 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 "$@"