aboutsummaryrefslogtreecommitdiff
path: root/common/.bashrc
diff options
context:
space:
mode:
authorsrdusr <trevorgray@srdusr.com>2025-10-03 16:27:13 +0200
committersrdusr <trevorgray@srdusr.com>2025-10-03 16:27:13 +0200
commiteb344f0b2330354f11101ad9dc0c808a15765667 (patch)
treed083fe50769bc1e575427ed62a3be426a73101e4 /common/.bashrc
parentafe53c73bfd21a2931afccae9ea0bcfbfd4a9405 (diff)
downloaddotfiles-eb344f0b2330354f11101ad9dc0c808a15765667.tar.gz
dotfiles-eb344f0b2330354f11101ad9dc0c808a15765667.zip
Various changes/updates
Diffstat (limited to 'common/.bashrc')
-rw-r--r--common/.bashrc1056
1 files changed, 906 insertions, 150 deletions
diff --git a/common/.bashrc b/common/.bashrc
index fbc86fe..38be903 100644
--- a/common/.bashrc
+++ b/common/.bashrc
@@ -15,40 +15,541 @@ if [[ $- != *i* ]]; then
return
fi
+# If not running interactively, dont do anything
+[[ $- != *i* ]] && return
+
# Get the current active terminal
term="$(cat /proc/"$PPID"/comm)"
-# Set a default prompt
-p='\[\033[01;37m\]┌─[\[\033[01;32m\]srdusr\[\033[01;37m\]]-[\[\033[01;36m\]archlinux\[\033[01;37m\]]-[\[\033[01;33m\]\W\]\[\033[00;37m\]\[\033
-\[\033[01;37m\]└─[\[\033[05;33m\]$\[\033[00;37m\]\[\033[01;37m\]]\[\033[00;37m\] '
+########## Prompt ##########
+
+# ------------------------
+# Prompt Colors & Variables
+# ------------------------
+RESET="\[\033[0m\]"
+BOLD="\[\033[1m\]"
+BLINK="\[\033[5m\]"
-# Set transparency and prompt while using st
-if [[ $term = "st" ]]; then
- transset-df "0.65" --id "$WINDOWID" >/dev/null
+FG_USER="\[\033[38;5;82m\]" # bright green
+FG_HOST="\[\033[38;5;45m\]" # cyan/blue
+FG_DIR="\[\033[38;5;214m\]" # orange
+FG_PROMPT="\[\033[38;5;220m\]" # yellow
+FG_BORDER="\[\033[37m\]" # light gray
- # [Your_Name]-----| |=======|------[Your_Distro]
- # [Color]--------| | [Color]------| |
- # [Style]------------| | | [Style]---------| | |
- # V V V V V V
- p='\[\033[01;37m\]┌─[\[\033[01;32m\]srdusr\[\033[01;37m\]]-[\[\033[01;36m\]archlinux\[\033[01;37m\]]-[\[\033[01;33m\]\W\[\033[00;37m\]\[\033[01;37m\]]
-\[\033[01;37m\]└─[\[\033[05;33m\]$\[\033[00;37m\]\[\033[01;37m\]]\[\033[00;37m\] '
-# A A A
-# [Style]----| | |-------- [Your_Choice]
-# [Color]------------|
+USER_NAME="${USER}"
+HOST_NAME="$(hostname -s)"
+DISTRO="$(. /etc/os-release && echo $ID)" 2>/dev/null || DISTRO="$HOST_NAME"
+# ------------------------
+# Git bare-dotfiles environment
+# ------------------------
+set_git_env_vars() {
+ [[ -d "$HOME/.cfg" ]] || return
+ git --git-dir="$HOME/.cfg" rev-parse --is-bare-repository &>/dev/null || return
+
+ # Only set env vars if not inside another Git repo
+ if ! git rev-parse --is-inside-work-tree &>/dev/null; then
+ export GIT_DIR="$HOME/.cfg"
+ export GIT_WORK_TREE="$HOME"
+ else
+ unset GIT_DIR
+ unset GIT_WORK_TREE
+ fi
+}
+
+# Update git env on directory change
+if [ -n "$BASH_VERSION" ]; then
+ PROMPT_COMMAND="set_git_env_vars; $PROMPT_COMMAND"
fi
+set_git_env_vars
-# If not running interactively, dont do anything
-[[ $- != *i* ]] && return
+# ------------------------
+# Git branch info
+# ------------------------
+git_branch() {
+ # Only inside a git repo
+ if git rev-parse --is-inside-work-tree &>/dev/null; then
+ local branch
+ branch=$(git symbolic-ref --short HEAD 2>/dev/null)
+ # Check for uncommitted changes
+ local status=""
+ if [[ -n $(git status --porcelain) ]]; then
+ status="*"
+ fi
+ echo "(${branch}${status})"
+ fi
+}
+
+# ------------------------
+# Prompt with git info
+# ------------------------
+PS1_FUNC() {
+ local git_info
+ git_info=$(git_branch)
+ # Blank line before prompt
+ echo
+
+ PS1="${FG_BORDER}${BOLD}┌─[${FG_USER}${USER_NAME}${FG_BORDER}]"
+ PS1+=" ${FG_HOST}${DISTRO}${FG_BORDER}"
+ PS1+=" ${FG_DIR}\W${FG_BORDER}"
+ # Git info right after path
+ [[ -n "$git_info" ]] && PS1+=" ${FG_PROMPT}${git_info}${FG_BORDER}"
+ PS1+="\n${FG_BORDER}└──[${FG_PROMPT}${BLINK}\$${RESET}${FG_BORDER}]${RESET}"
+ export PS1
+}
+
+# Make PS1 dynamic each time
+PROMPT_COMMAND="PS1_FUNC; $PROMPT_COMMAND"
+
+# ------------------------
+# Optional: git subtree helper
+# ------------------------
+gsp() {
+ local GIT_SUBTREE_FILE="$PWD/.gitsubtrees"
+ [[ -f "$GIT_SUBTREE_FILE" ]] || { echo "No .gitsubtrees file."; return; }
+
+ while IFS= read -r LINE; do
+ [[ $LINE =~ ^# ]] && continue
+ IFS=';' read -r PREFIX REMOTE BRANCH <<< "$LINE"
+ echo "Pulling subtree $PREFIX from $REMOTE $BRANCH..."
+ git subtree pull --prefix="$PREFIX" "$REMOTE" "$BRANCH"
+ done < "$GIT_SUBTREE_FILE"
+}
+
+########## Bindings ##########
+
+bind -m vi-command 'Control-l: clear-screen'
+bind -m vi-insert 'Control-l: clear-screen'
+
+
+########## Env/exports ##########
+
+export PROMPT_COMMAND="resize &>/dev/null ; $PROMPT_COMMAND"
+
+# XDG Base Directories
+#--------------------------------------
+export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
+export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
+export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
+
+export INPUTRC="$XDG_CONFIG_HOME/inputrc"
+
+# PATH Setup
+#--------------------------------------
+# Base user paths
+export PATH="$HOME/.bin:$HOME/.local/bin:$HOME/.scripts:/usr/local/bin:/sbin:/usr/sbin:$PATH"
+
+# Termux (Android)
+if [ -d "/data/data/com.termux/files/usr/local/bin" ]; then
+ export PATH="/data/data/com.termux/files/usr/local/bin:$PATH"
+fi
+
+# cmake
+if [ -x "/usr/bin/cmake" ]; then
+ export PATH="/usr/bin/cmake:$PATH"
+fi
+
+# Chrome
+if [ -d "/opt/google/chrome" ]; then
+ export PATH="$PATH:/opt/google/chrome"
+fi
+
+# Homebrew (macOS / Linux)
+if [ -d "/opt/homebrew/bin" ]; then
+ export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
+fi
+
+# Nix profile
+if [ -d "$HOME/.nix-profile/bin" ]; then
+ export PATH="$HOME/.nix-profile/bin:$PATH"
+fi
+
+# Add ~/.scripts (excluding some dirs)
+#--------------------------------------
+EXCLUDE_DIRS=(".git" "assets" "test")
+if [ -d "$HOME/.scripts" ]; then
+ while IFS= read -r -d '' dir; do
+ rel_path="${dir#$HOME/.scripts/}"
+ skip=false
+ for exclude in "${EXCLUDE_DIRS[@]}"; do
+ [[ "$rel_path" == "$exclude"* ]] && skip=true && break
+ done
+ [ "$skip" = false ] && PATH="$dir:$PATH"
+ done < <(find "$HOME/.scripts" -type d -print0)
+fi
+
+# Terminal
+#--------------------------------------
+export TERM="xterm-256color"
+export COLORTERM="truecolor"
+
+# Choose default terminal
+for term in wezterm kitty alacritty xterm; do
+ if command -v "$term" &>/dev/null; then
+ export TERMINAL="$term"
+ break
+ fi
+done
+
+# Default Programs
+#--------------------------------------
+export EDITOR="$(command -v nvim || command -v vim || echo nano)"
+export TEXEDIT="$EDITOR"
+export FCEDIT="$EDITOR"
+export VISUAL="$EDITOR"
+export GIT_EDITOR="$EDITOR"
+
+export READER="zathura"
+export BROWSER="firefox"
+export OPENER="xdg-open"
+export VIDEO="mpv"
+export IMAGE="phototonic"
+
+# Man pager
+if command -v nvim &>/dev/null; then
+ export MANPAGER="sh -c 'col -b | nvim -c \"set ft=man ts=8 nomod nolist nonu noma\" -c \"autocmd VimEnter * call feedkeys(\\\"\\<CR>q\\\")\" -'"
+else
+ export MANPAGER="bat"
+fi
+export MANROFFOPT="-c"
+export PAGER="less"
+
+# Shell History
+#--------------------------------------
+export HISTSIZE=1000000
+export SAVEHIST=1000000
+
+# Colors
+#--------------------------------------
+export LSCOLORS=ExGxBxDxCxEgEdxbxgxcxd
+
+# less colors
+export LESS_TERMCAP_mb=$(tput bold; tput setaf 2) # green
+export LESS_TERMCAP_md=$(tput bold; tput setaf 2) # green
+export LESS_TERMCAP_so=$(tput bold; tput setaf 3) # yellow
+export LESS_TERMCAP_se=$(tput rmso; tput sgr0)
+export LESS_TERMCAP_us=$(tput smul; tput bold; tput setaf 1) # red
+export LESS_TERMCAP_ue=$(tput sgr0)
+export LESS_TERMCAP_me=$(tput sgr0)
+
+# Miscellaneous XDG-aware configs
+#--------------------------------------
+export RIPGREP_CONFIG_PATH="$XDG_CONFIG_HOME/ripgrep/ripgreprc"
+export DOCKER_CONFIG="$XDG_CONFIG_HOME/docker"
+export VSCODE_PORTABLE="$XDG_DATA_HOME/vscode"
+export GTK2_RC_FILES="$XDG_CONFIG_HOME/gtk-2.0/gtkrc"
+export DISCORD_USER_DATA_DIR="$XDG_DATA_HOME"
+export LYNX_CFG="$XDG_CONFIG_HOME/.lynxrc"
+
+# Languages / SDKs
+#--------------------------------------
+# Rust
+export RUSTUP_HOME="$XDG_DATA_HOME/rustup"
+export CARGO_HOME="$XDG_DATA_HOME/cargo"
+export PATH="$CARGO_HOME/bin:$RUSTUP_HOME/bin:$PATH"
+command -v rustc &>/dev/null && export RUST_BACKTRACE=1
+
+# Dotnet
+export DOTNET_HOME="$XDG_DATA_HOME/dotnet"
+export DOTNET_CLI_HOME="$XDG_CONFIG_HOME/dotnet"
+export PATH="$DOTNET_HOME/tools:$PATH"
+export DOTNET_ROOT="/opt/dotnet"
+export DOTNET_CLI_TELEMETRY_OPTOUT=1
+
+# Java
+export JAVA_HOME="/usr/lib/jvm/java-20-openjdk"
+export _JAVA_OPTIONS="-Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java"
+
+# Dart / Flutter
+if [ -d "/opt/flutter/bin" ]; then
+ export PATH="/opt/flutter/bin:/usr/lib/dart/bin:$PATH"
+fi
+
+# Go
+export GOPATH="$XDG_DATA_HOME/go"
+
+# Node / NVM
+export NVM_DIR="$XDG_CONFIG_HOME/nvm"
+[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
+
+[[ -d "$XDG_DATA_HOME/node/bin" ]] && PATH="$XDG_DATA_HOME/node/bin:$PATH"
+export NODE_REPL_HISTORY="$XDG_DATA_HOME/node_repl_history"
+export NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc"
+
+# Bun
+export BUN_INSTALL="$HOME/.bun"
+export PATH="$BUN_INSTALL/bin:$PATH"
+
+# Ruby
+export GEM_HOME="$XDG_DATA_HOME/ruby/gems"
+export GEM_PATH="$GEM_HOME"
+export GEM_SPEC_CACHE="$XDG_DATA_HOME/ruby/specs"
+
+# Python
+if command -v virtualenvwrapper.sh >/dev/null 2>&1; then
+ export WORKON_HOME="$HOME/.virtualenvs"
+ export VIRTUALENVWRAPPER_PYTHON="$(command -v python3)"
+ export VIRTUALENVWRAPPER_VIRTUALENV="$(command -v virtualenv)"
+ source "$(command -v virtualenvwrapper.sh)"
+fi
+export VIRTUAL_ENV_DISABLE_PROMPT=false
+export JUPYTER_CONFIG_DIR="$XDG_CONFIG_HOME/jupyter"
+export IPYTHONDIR="$XDG_CONFIG_HOME/jupyter"
+
+[[ "$(uname)" == "Darwin" ]] && export PYTHON_CONFIGURE_OPTS="--enable-framework"
+[[ "$(uname)" == "Linux" ]] && export PYTHON_CONFIGURE_OPTS="--enable-shared"
+
+export PYENV_ROOT="$HOME/.pyenv"
+export PATH="$PYENV_ROOT/bin:$PATH"
+
+# PHP
+export PATH="$HOME/.config/composer/vendor/bin:$PATH"
+
+# Lua
+if [ -d "/usr/local/luarocks/bin" ]; then
+ export PATH="$PATH:/usr/local/luarocks/bin"
+fi
+
+# Android SDK
+#--------------------------------------
+if [ -d "/opt/android-sdk" ]; then
+ export ANDROID_HOME="/opt/android-sdk"
+ export ANDROID_SDK_ROOT="/opt/android-sdk"
+ export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
+ export PATH="$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH"
+ export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH"
+fi
+
+# Misc
+#--------------------------------------
+export NVIM_TUI_ENABLE_TRUE_COLOR=1
+export GPG_TTY="$(tty)"
+export XDG_MENU_PREFIX="gnome-"
+export DEVELOPMENT_DIRECTORY="$HOME/code"
+export PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig:$PKG_CONFIG_PATH"
+
+# FZF + rg
+if command -v rg &>/dev/null; then
+ export FZF_DEFAULT_COMMAND="rg --files --hidden --glob '!{node_modules/*,.git/*}'"
+ export FZF_DEFAULT_OPTS='-m --height 50% --border'
+ export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
+fi
+
+########## Aliases ##########
+
+# Define alias for nvim/vim (fallback to vim)
+if command -v nvim > /dev/null; then
+ alias vi='nvim'
+else
+ alias vi='vim'
+fi
+
+#alias vv='$(history -p !vim)'
+alias vv="vim -c 'norm! ^O'"
+
+# Confirmation #
+alias mv='mv -i'
+alias cp='cp -i'
+alias ln='ln -i'
+
+# Disable 'rm'
+#alias rm='function _rm() { echo -e "\033[0;31mrm\033[0m is disabled, use \033[0;32mtrash\033[0m or \033[0;32mdel \033[0m\033[0;33m$1\033[0m"; }; _rm'
+#alias del='/bin/rm'
+
+# Use lsd for ls if available
+if command -v lsd >/dev/null 2>&1; then
+ alias ls='lsd --color=auto --group-directories-first'
+fi
+
+# ls variants
+alias l='ls -FAh --group-directories-first'
+alias la='ls -lAFh --group-directories-first'
+alias lt='ls -lFAht --group-directories-first'
+alias lr='ls -RFAh --group-directories-first'
+
+# more ls variants
+alias ldot='ls -ld .* --group-directories-first'
+alias lS='ls -1FASsh --group-directories-first'
+alias lart='ls -1Fcart --group-directories-first'
+alias lrt='ls -1Fcrt --group-directories-first'
+
+# ls with different alphabethical sorting
+#unalias ll
+#ll() { LC_COLLATE=C ls "$@" }
+
+# suffix aliases | commented out invalid bash syntax
+#alias -g CP='| xclip -selection clipboard -rmlastnl'
+#alias -g LL="| less exit 2>1 /dev/null"
+#alias -g CA="| cat -A"
+#alias -g KE="2>&1"
+#alias -g NE="2>/dev/null"
+#alias -g NUL=">/dev/null 2>&1"
+
+alias grep='grep --color=auto --exclude-dir={.git,.svn,.hg}'
+alias egrep='egrep --color=auto --exclude-dir={.git,.svn,.hg}'
+alias egrep='fgrep --color=auto --exclude-dir={.git,.svn,.hg}'
+
+#alias hist="grep '$1' $HISTFILE"
+alias hist="history | grep $1"
+
+
+alias gdb='gdb -q'
+alias rust-gdb='rust-gdb -q'
+
+alias cd="cd-clear-ls"
+alias clear='newline_clear'
+
+# List upto last 10 visited directories using "d" and quickly cd into any specific one
+alias d="dirs -v | head -10"
+
+# Using just a number from "0" to "9"
+alias 0="cd +0"
+alias 1="cd +1"
+alias 2="cd +2"
+alias 3="cd +3"
+alias 4="cd +4"
+alias 5="cd +5"
+alias 6="cd +6"
+alias 7="cd +7"
+alias 8="cd +8"
+alias 9="cd +9"
+
+alias sudo='sudo ' # zsh: elligible for alias expansion/fix syntax highlight
+alias sedit='sudoedit'
+#alias se='sudoedit'
+alias se='sudo -e'
+alias :q='exit 2>1 /dev/null'
+alias disk-destroyer='$(command -v dd)'
+alias dd='echo "Warning use command: disk-destroyer"'
+alias sc="systemctl"
+alias jc="journalctl"
+alias jck="journalctl -k" # Kernel
+alias jce='sudo journalctl -b --priority 0..3' # error
+alias journalctl-error='sudo journalctl -b --priority 0..3'
+alias jcssh="sudo journalctl -u sshd"
+alias tunnel='ssh -fNTL'
+# tty aliases
+#if [[ "$TERM" == 'linux' ]]; then
+# alias tmux='/usr/bin/tmux -L linux'
+#fi
+#alias logout="loginctl kill-user $(whoami)"
+
+logout() {
+ local wm
+ wm="$(windowManagerName)"
+ if [[ -n "$wm" ]]; then
+ echo "Logging out by killing window manager: $wm"
+ pkill "$wm"
+ else
+ echo "No window manager detected!" >&2
+ fi
+}
+alias lg="logout"
+
+#alias suspend='systemctl suspend && betterlockscreen -l' # Suspend(sleep) and lock screen if using systemctl
+#alias suspend='systemctl suspend' # Suspend(sleep) and lock screen if using systemctl
+alias suspend='loginctl suspend' # Suspend(sleep) and lock screen if using systemctl
+#alias shutdown='loginctl poweroff' # Suspend(sleep) and lock screen if using systemctl
+#alias shutdown='sudo /sbin/shutdown -h'
+#alias poweroff='loginctl poweroff'
+#alias reboot='loginctl reboot'
+alias reboot='sudo reboot'
+#alias hibernate='systemctl hibernate' # Hibernate
+alias lock='DISPLAY=:0 xautolock -locknow' # Lock my workstation screen from my phone
+alias oports="sudo lsof -i -P -n | grep -i 'listen'" # List open ports
+alias keyname="xev | sed -n 's/[ ]*state.* \([^ ]*\)).*/\1/p'"
+alias wget=wget --hsts-file="$XDG_CACHE_HOME/wget-hsts" # wget does not support environment variables
+alias open="xdg-open"
+alias pp='getlast 2>&1 |&tee -a output.txt'
+#alias lg='la | grep'
+alias pg='ps aux | grep'
+alias py='python'
+alias py3='python3'
+alias activate='source ~/.local/share/venv/bin/activate'
+alias sha256='shasum -a 256'
+alias rgf='rg -F'
+alias weather='curl wttr.in/durban'
+alias diary='nvim "$HOME/documents/main/inbox/diary/$(date +'%Y-%m-%d').md"'
+alias wifi='nmcli dev wifi show-password'
+alias ddg='w3m lite.duckduckgo.com'
+alias rss='newsboat'
+alias vpn='protonvpn'
+alias yt-dl="yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4' --restrict-filename"
+#alias com.obsproject.Studio="obs"
+#alias obs="com.obsproject.Studio"
+#alias obs-stuido="obs"
+
+# Time aliases
+alias utc='TZ=Africa/Johannesburg date'
+alias ber='TZ=Europe/Berlin date'
+alias nyc='TZ=America/New_York date'
+alias sfo='TZ=America/Los_Angeles date'
+alias utc='TZ=Etc/UTC date'
+
+alias src='source $ZDOTDIR/.zshrc'
+alias p=proxy
+
+alias cheat='~/.scripts/cheat.sh ~/documents/notes/cheatsheets'
+alias crypto='curl -s rate.sx | head -n -2 | tail -n +10'
+#alias todo='glow "$HOME"/documents/main/notes/TODO.md'
+
+alias todo='$EDITOR "$(find "$HOME"/documents/main -type f -iname "todo.md" | head -n 1)"'
+alias android-studio='/opt/android-studio/bin/studio.sh' # android-studio
+alias nomachine='/usr/NX/bin/nxplayer' # nomachine
+alias firefox="firefox-bin"
+alias discord="vesktop-bin"
+alias fetch="fastfetch"
+alias batt='upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -E "state|to full|percentage"'
+alias emerge-fetch='sudo tail -f /var/log/emerge-fetch.log'
+alias spotify="env LD_PRELOAD=/usr/local/lib/spotify-adblock.so spotify %U"
+
+alias proofread='firejail --private --private-tmp --net=none --seccomp --caps.drop=all zathura'
+
+# NVM
+if [ -s "$NVM_DIR/nvm.sh" ]; then
+ nvm_cmds=(nvm node npm yarn)
+ for cmd in "${nvm_cmds[@]}"; do
+ alias "$cmd"="unalias ${nvm_cmds[*]} && unset nvm_cmds && . $NVM_DIR/nvm.sh && $cmd"
+ done
+fi
+
+# Kubernetes
+if command -v kubectl > /dev/null; then
+ replaceNS() { kubectl config view --minify --flatten --context=$(kubectl config current-context) | yq ".contexts[0].context.namespace=\"$1\"" ; }
+ alias kks='KUBECONFIG=<(replaceNS "kube-system") kubectl'
+ alias kam='KUBECONFIG=<(replaceNS "authzed-monitoring") kubectl'
+ alias kas='KUBECONFIG=<(replaceNS "authzed-system") kubectl'
+ alias kar='KUBECONFIG=<(replaceNS "authzed-region") kubectl'
+ alias kt='KUBECONFIG=<(replaceNS "tenant") kubectl'
+
+ if command -v kubectl-krew > /dev/null; then
+ path=($XDG_CONFIG_HOME/krew/bin $path)
+ fi
+
+ rmfinalizers() {
+ kubectl get deployment "$1" -o json | jq '.metadata.finalizers = null' | kubectl apply -f -
+ }
+fi
+
+# Castero
+castero() {
+ if [[ -f ~/.local/share/venv/bin/activate ]]; then
+ . ~/.local/share/venv/bin/activate
+ fi
+ command castero "$@"
+}
+
+# Zoxide (cd alternative)
+if command -v zoxide >/dev/null 2>&1; then
+ eval "$(zoxide init bash)"
+fi
-# My alias commands
-alias ls='ls --color=auto -1'
-alias shred='shred -uzvn3'
-alias wallset='feh --bg-fill'
+
+########## Functions ##########
# Dotfiles Management System
if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
- # Core git wrapper with repository as work-tree
+ # Core git wrapper - .cfg is bare repo, work-tree points to .cfg itself
_config() {
git --git-dir="$HOME/.cfg" --work-tree="$HOME/.cfg" "$@"
}
@@ -61,86 +562,127 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
*) CFG_OS="other" ;;
esac
- # Map system path to repository path
_repo_path() {
local f="$1"
- # If it's an absolute path that's not in HOME, handle it specially
- if [[ "$f" == /* && "$f" != "$HOME/"* ]]; then
- echo "$CFG_OS/${f#/}"
- return
+ # Normalize absolute or relative
+ if [[ "$f" == "$HOME/"* ]]; then
+ f="${f#$HOME/}"
+ elif [[ "$f" == "./"* ]]; then
+ f="${f#./}"
fi
- # Check for paths that should go to the repository root
+ # Already tracked? Use that
+ local dirs=("common/" "$CFG_OS/home/" "$CFG_OS/Users/")
+ for d in "${dirs[@]}"; do
+ local match="$(_config ls-files --full-name | grep -F "/$f" | grep -F "$d" || true)"
+ if [[ -n "$match" ]]; then
+ echo "$match"
+ return
+ fi
+ done
+
+ # Already a special repo path
case "$f" in
- common/*|linux/*|macos/*|windows/*|profile/*|README.md)
+ common/*|"$CFG_OS/home/"*|"$CFG_OS/Users/"*|profile/*|README.md)
echo "$f"
return
;;
- "$HOME/"*)
- f="${f#$HOME/}"
- ;;
esac
- # Default: put under OS-specific home
- echo "$CFG_OS/home/$f"
+ # Map everything else dynamically
+ case "$f" in
+ *)
+ case "$CFG_OS" in
+ linux) echo "linux/home/$f" ;;
+ macos) echo "macos/Users/$f" ;;
+ windows) echo "windows/Users/$f" ;;
+ *) echo "$CFG_OS/home/$f" ;;
+ esac
+ ;;
+ esac
}
_sys_path() {
local repo_path="$1"
- local os_path_pattern="$CFG_OS/"
- # Handle OS-specific files that are not in the home subdirectory
- if [[ "$repo_path" == "$os_path_pattern"* && "$repo_path" != */home/* ]]; then
- echo "/${repo_path#$os_path_pattern}"
- return
- fi
+ # System HOME
+ local sys_home
+ case "$CFG_OS" in
+ linux|macos) sys_home="$HOME" ;;
+ windows) sys_home="$USERPROFILE" ;;
+ esac
+
+ # Repo HOME roots
+ local repo_home
+ case "$CFG_OS" in
+ linux) repo_home="linux/home" ;;
+ macos) repo_home="macos/Users" ;;
+ windows) repo_home="windows/Users" ;;
+ esac
case "$repo_path" in
- # Common configs → OS-specific config dirs
- common/config/*)
- case "$CFG_OS" in
- linux)
- local base="${XDG_CONFIG_HOME:-$HOME/.config}"
- echo "$base/${repo_path#common/config/}"
+ # Common files → $HOME/… but normalize well-known dirs
+ common/*)
+ local rel="${repo_path#common/}"
+
+ case "$rel" in
+ # XDG config
+ .config/*|config/*)
+ local sub="${rel#*.config/}"
+ sub="${sub#config/}"
+ echo "${XDG_CONFIG_HOME:-$sys_home/.config}/$sub"
;;
- macos)
- echo "$HOME/Library/Application Support/${repo_path#common/config/}"
+
+ # XDG data (assets, wallpapers, icons, fonts…)
+ assets/*|.local/share/*)
+ local sub="${rel#assets/}"
+ sub="${sub#.local/share/}"
+ echo "${XDG_DATA_HOME:-$sys_home/.local/share}/$sub"
;;
- windows)
- echo "$LOCALAPPDATA\\${repo_path#common/config/}"
+
+ # XDG cache (if you ever store cached scripts/config)
+ .cache/*)
+ local sub="${rel#.cache/}"
+ echo "${XDG_CACHE_HOME:-$sys_home/.cache}/$sub"
;;
+
+ # Scripts
+ .scripts/*|scripts/*)
+ local sub="${rel#*.scripts/}"
+ sub="${sub#scripts/}"
+ echo "$sys_home/.scripts/$sub"
+ ;;
+
+ # Default: dump directly under $HOME
*)
- echo "$HOME/.config/${repo_path#common/config/}"
+ echo "$sys_home/$rel"
;;
esac
;;
- # Common assets → stay in repo
- common/assets/*)
- echo "$HOME/.cfg/$repo_path"
+ # Profile files → $HOME/…
+ profile/*)
+ local rel="${repo_path#profile/}"
+ echo "$sys_home/$rel"
;;
- # Other common files (dotfiles like .bashrc, .gitconfig, etc.) → $HOME
- common/*)
- echo "$HOME/${repo_path#common/}"
+ # OS-specific home paths → $HOME or $USERPROFILE
+ "$repo_home"/*)
+ local rel="${repo_path#$repo_home/}"
+ echo "$sys_home/$rel"
;;
- # OS-specific home
- */home/*)
- echo "$HOME/${repo_path#*/home/}"
+ # OS-specific system paths outside home/Users → absolute
+ "$CFG_OS/"*)
+ local rel="${repo_path#$CFG_OS/}"
+ echo "/$rel"
;;
- # Profile configs and README → stay in repo
- profile/*|README.md)
+ # Fallback: treat as repo-only
+ *)
echo "$HOME/.cfg/$repo_path"
;;
-
- # Default fallback
- *)
- echo "$HOME/.cfg/$repo_path"
- ;;
-
esac
}
@@ -165,63 +707,120 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
# Main config command
config() {
local cmd="$1"; shift
- local target_dir=""
- # Parse optional --target flag for add
- if [[ "$cmd" == "add" ]]; then
- while [[ "$1" == --* ]]; do
- case "$1" in
- --target|-t)
- target_dir="$2"
- shift 2
- ;;
- *)
- echo "Unknown option: $1"
- return 1
- ;;
- esac
- done
- fi
case "$cmd" in
add)
local file_path
- for file_path in "$@"; do
+ local git_opts=()
+ local files=()
+ local target_dir=""
+
+ # Parse optional --target flag before files
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --target|-t)
+ target_dir="$2"
+ shift 2
+ ;;
+ -*) # any other git flags
+ git_opts+=("$1")
+ shift
+ ;;
+ *) # files
+ files+=("$1")
+ shift
+ ;;
+ esac
+ done
+
+ for file_path in "${files[@]}"; do
+ # Store original for rel_path calculation
+ local original_path="$file_path"
+
+ # Make path absolute first
+ if [[ "$file_path" != /* && "$file_path" != "$HOME/"* ]]; then
+ file_path="$(pwd)/$file_path"
+ fi
+
+ # Check if file exists
+ if [[ ! -e "$file_path" ]]; then
+ echo "Error: File not found: $file_path"
+ continue
+ fi
+
+ # Calculate relative path from original input
+ local rel_path
+ if [[ "$original_path" == "$HOME/"* ]]; then
+ rel_path="${original_path#$HOME/}"
+ elif [[ "$original_path" == "./"* ]]; then
+ rel_path="${original_path#./}"
+ else
+ rel_path="$original_path"
+ fi
+
+ # Check if file is already tracked
+ local existing_path="$(_config ls-files --full-name | grep -Fx "$(_repo_path "$file_path")" || true)"
local repo_path
- if [[ -n "$target_dir" ]]; then
- local rel_path
- if [[ "$file_path" == /* ]]; then
- rel_path="$(basename "$file_path")"
- else
- rel_path="$file_path"
- fi
+ if [[ -n "$existing_path" ]]; then
+ repo_path="$existing_path"
+ elif [[ -n "$target_dir" ]]; then
repo_path="$target_dir/$rel_path"
else
repo_path="$(_repo_path "$file_path")"
fi
+ # Copy file into bare repo
local full_repo_path="$HOME/.cfg/$repo_path"
mkdir -p "$(dirname "$full_repo_path")"
cp -a "$file_path" "$full_repo_path"
- git --git-dir="$HOME/.cfg" --work-tree="$HOME/.cfg" add "$repo_path"
+ # Add to git
+ _config add "${git_opts[@]}" "$repo_path"
echo "Added: $file_path -> $repo_path"
done
;;
+
rm)
local rm_opts=""
local file_path_list=()
+ local target_dir=""
- for arg in "$@"; do
- if [[ "$arg" == "-"* ]]; then
- rm_opts+=" $arg"
- else
- file_path_list+=("$arg")
- fi
+ # Parse options
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --target|-t)
+ target_dir="$2"
+ shift 2
+ ;;
+ -*)
+ rm_opts+=" $1"
+ shift
+ ;;
+ *)
+ file_path_list+=("$1")
+ shift
+ ;;
+ esac
done
for file_path in "${file_path_list[@]}"; do
- local repo_path="$(_repo_path "$file_path")"
+ local repo_path
+ # Check if already a repo path (exists in git index) - exact match
+ if _config ls-files --full-name | grep -qFx "$file_path"; then
+ repo_path="$file_path"
+ elif [[ -n "$target_dir" ]]; then
+ # Use target directory if specified
+ local rel_path
+ if [[ "$file_path" == "$HOME/"* ]]; then
+ rel_path="${file_path#$HOME/}"
+ else
+ rel_path="${file_path#./}"
+ fi
+ repo_path="$target_dir/$rel_path"
+ else
+ repo_path="$(_repo_path "$file_path")"
+ fi
if [[ "$rm_opts" == *"-r"* ]]; then
_config rm --cached -r "$repo_path"
@@ -229,15 +828,21 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
_config rm --cached "$repo_path"
fi
- eval "rm $rm_opts \"$file_path\""
- echo "Removed: $file_path"
+ # Compute system path for actual file removal
+ local sys_file="$(_sys_path "$repo_path")"
+ if [[ -e "$sys_file" ]]; then
+ eval "rm $rm_opts \"$sys_file\""
+ fi
+ echo "Removed: $repo_path"
done
;;
+
sync)
local direction="${1:-to-repo}"; shift
_config ls-files | while read -r repo_file; do
local sys_file="$(_sys_path "$repo_file")"
local full_repo_path="$HOME/.cfg/$repo_file"
+
if [[ "$direction" == "to-repo" ]]; then
if [[ -e "$sys_file" && -n "$(diff "$full_repo_path" "$sys_file" 2>/dev/null || echo "diff")" ]]; then
cp -a "$sys_file" "$full_repo_path"
@@ -258,32 +863,59 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
fi
done
;;
+
status)
- local auto_synced=()
+ local modified_files=()
+ local missing_files=()
+
+ # Colors like git
+ local RED="\033[31m"
+ local GREEN="\033[32m"
+ local YELLOW="\033[33m"
+ local BLUE="\033[34m"
+ local BOLD="\033[1m"
+ local RESET="\033[0m"
+
while read -r repo_file; do
local sys_file="$(_sys_path "$repo_file")"
local full_repo_path="$HOME/.cfg/$repo_file"
- if [[ -e "$sys_file" && -e "$full_repo_path" ]]; then
+
+ if [[ ! -e "$full_repo_path" ]]; then
+ missing_files+=("$repo_file")
+ elif [[ -e "$sys_file" ]]; then
if ! diff -q "$full_repo_path" "$sys_file" >/dev/null 2>&1; then
- cp -fa "$sys_file" "$full_repo_path"
- auto_synced+=("$repo_file")
+ modified_files+=("$repo_file")
fi
fi
done < <(_config ls-files)
- if [[ ${#auto_synced[@]} -gt 0 ]]; then
- echo "=== Auto-synced Files ==="
- for repo_file in "${auto_synced[@]}"; do
- echo "synced: $(_sys_path "$repo_file") -> $repo_file"
+
+ # Report missing files
+ if [[ ${#missing_files[@]} -gt 0 ]]; then
+ echo -e "${BOLD}${RED}=== Missing Files (consider removing from git) ===${RESET}"
+ for repo_file in "${missing_files[@]}"; do
+ echo -e " ${RED}deleted:${RESET} $(_sys_path "$repo_file") -> $repo_file"
done
echo
fi
- _config status
- echo
+
+ # Report modified files
+ if [[ ${#modified_files[@]} -gt 0 ]]; then
+ echo -e "${BOLD}${YELLOW}=== Modified Files (different from system) ===${RESET}"
+ for repo_file in "${modified_files[@]}"; do
+ echo -e " ${YELLOW}modified:${RESET} $(_sys_path "$repo_file") -> $repo_file"
+ done
+ echo
+ fi
+
+ # Finally, show underlying git status (with colors)
+ _config -c color.status=always status
;;
- deploy)
+
+ deploy|checkout)
+ echo "Deploying dotfiles from .cfg..."
_config ls-files | while read -r repo_file; do
local full_repo_path="$HOME/.cfg/$repo_file"
- local sys_file="$(_sys_path "$repo_file")" # destination only
+ local sys_file="$(_sys_path "$repo_file")"
# Only continue if the source exists
if [[ -e "$full_repo_path" && -n "$sys_file" ]]; then
@@ -303,29 +935,7 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
fi
done
;;
- checkout)
- echo "Checking out dotfiles from .cfg..."
- _config ls-files | while read -r repo_file; do
- local full_repo_path="$HOME/.cfg/$repo_file"
- local sys_file="$(_sys_path "$repo_file")"
- if [[ -e "$full_repo_path" && -n "$sys_file" ]]; then
- local dest_dir
- dest_dir="$(dirname "$sys_file")"
-
- # Create destination if it doesn't exist
- if [[ "$sys_file" == /* && "$sys_file" != "$HOME/"* ]]; then
- _sudo_prompt mkdir -p "$dest_dir"
- _sudo_prompt cp -a "$full_repo_path" "$sys_file"
- else
- mkdir -p "$dest_dir"
- cp -a "$full_repo_path" "$sys_file"
- fi
-
- echo "Checked out: $repo_file -> $sys_file"
- fi
- done
- ;;
backup)
local timestamp=$(date +%Y%m%d%H%M%S)
local backup_dir="$HOME/.dotfiles_backup/$timestamp"
@@ -341,6 +951,7 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
done
echo "Backup complete. To restore, copy files from $backup_dir to their original locations."
;;
+
*)
_config "$cmd" "$@"
;;
@@ -348,24 +959,169 @@ if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
}
fi
-PS1=$p
+# Make SUDO_ASKPASS agnostic: pick the first available askpass binary.
+# You can predefine SUDO_ASKPASS env var to force a particular path.
+: "${SUDO_ASKPASS:=""}"
-bind -m vi-command 'Control-l: clear-screen'
-bind -m vi-insert 'Control-l: clear-screen'
+# list of common askpass binaries (order: preferred -> fallback)
+_askpass_candidates=(
+ "$SUDO_ASKPASS" # user-specified (if absolute path)
+ "/usr/lib/ssh/x11-ssh-askpass"
+ "/usr/libexec/openssh/ssh-askpass"
+ "/usr/lib/ssh/ssh-askpass"
+ "/usr/bin/ssh-askpass"
+ "/usr/bin/ssh-askpass-gtk"
+ "/usr/bin/ssh-askpass-gnome"
+ "/usr/bin/ssh-askpass-qt"
+ "/usr/bin/ksshaskpass"
+ "/usr/bin/zenity" # use zenity --entry as wrapper (see below)
+ "/usr/bin/mate-ssh-askpass"
+ "/usr/bin/xdg-open" # last-resort GUI helper (not ideal)
+)
-export EDITOR="nvim"
+find_askpass() {
+ for p in "${_askpass_candidates[@]}"; do
+ [ -z "$p" ] && continue
+ # if user gave a path in SUDO_ASKPASS we accept it only if it's executable
+ if [ -n "$SUDO_ASKPASS" ] && [ "$p" = "$SUDO_ASKPASS" ]; then
+ [ -x "$p" ] && { printf '%s\n' "$p"; return 0; }
+ continue
+ fi
-#export NVM_DIR="$HOME/.local/share/nvm"
-#[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
-#[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
+ # if candidate is an absolute path, test directly
+ if [ "${p#/}" != "$p" ]; then
+ [ -x "$p" ] && { printf '%s\n' "$p"; return 0; }
+ continue
+ fi
-export PROMPT_COMMAND="resize &>/dev/null ; $PROMPT_COMMAND"
+ # otherwise try to resolve via PATH
+ if command -v "$p" >/dev/null 2>&1; then
+ # For zenity, we will use a small wrapper (see below)
+ printf '%s\n' "$(command -v "$p")"
+ return 0
+ fi
+ done
-# Rust environment (silent if not installed)
-export RUSTUP_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/rustup"
-export CARGO_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/cargo"
-export PATH="$CARGO_HOME/bin:$RUSTUP_HOME/bin:$PATH"
+ return 1
+}
-if command -v rustc >/dev/null 2>&1; then
- export RUST_BACKTRACE=1
+# If zenity is chosen, use a thin wrapper script so sudo -A can call it like an askpass binary.
+# This wrapper will be created in $XDG_RUNTIME_DIR or /tmp (non-persistent).
+create_zenity_wrapper() {
+ local wrapper
+ wrapper="${XDG_RUNTIME_DIR:-/tmp}/.sudo_askpass_zenity.sh"
+ cat >"$wrapper" <<'EOF'
+#!/bin/sh
+# simple zenity askpass wrapper for sudo
+# prints password to stdout so sudo -A works
+zenity --entry --title="Authentication" --text="Elevated privileges are required" --hide-text 2>/dev/null || exit 1
+EOF
+ chmod 700 "$wrapper"
+ printf '%s\n' "$wrapper"
+}
+
+# Set askpass
+if [ -z "$SUDO_ASKPASS" ]; then
+ candidate="$(find_askpass || true)"
+ if [ -n "$candidate" ]; then
+ if command -v zenity >/dev/null 2>&1 && [ "$(command -v zenity)" = "$candidate" ]; then
+ # create the wrapper and export it
+ wrapper="$(create_zenity_wrapper)"
+ export SUDO_ASKPASS="$wrapper"
+ else
+ export SUDO_ASKPASS="$candidate"
+ fi
+ else
+ # optional: leave unset or set to empty to avoid mistakes
+ unset SUDO_ASKPASS
+ fi
fi
+# debug: (uncomment to print what was chosen)
+# printf 'SUDO_ASKPASS -> %s\n' "${SUDO_ASKPASS:-<none>}"
+
+
+# Git
+# No arguments: `git status`
+# With arguments: acts like `git`
+g() {
+ if [ $# -gt 0 ]; then
+ git "$@" # Pass arguments to git
+ else
+ git status # Default to `git status`
+ fi
+}
+
+# Optional: enable bash completion for `g` like git
+if type complete &>/dev/null; then
+ complete -o default -o nospace -F _git g
+fi
+
+ga() { g add "$@"; }
+gaw() { g add -A && g diff --cached -w | g apply --cached -R; }
+grm() { g rm "$@"; }
+gb() { g branch "$@"; }
+gbl() { g branch -l "$@"; }
+gbD() { g branch -D "$@"; }
+gbu() { g branch -u "$@"; }
+ge() { g clone "$@"; }
+gc() { g commit "$@"; }
+gcm() { g commit -m "$@"; }
+gca() { g commit -a "$@"; }
+gcaa() { g commit -a --amend "$@"; }
+gcam() { g commit -a -m "$@"; }
+gce() { g commit -e "$@"; }
+gcfu() { g commit --fixup "$@"; }
+gco() { g checkout "$@"; }
+gcob() { g checkout -b "$@"; }
+gcoB() { g checkout -B "$@"; }
+gcp() { g cherry-pick "$@"; }
+gcpc() { g cherry-pick --continue "$@"; }
+gd() { g diff "$@"; }
+gds() { g diff --staged "$@"; }
+gdc() { g diff --cached "$@"; }
+gl() { g lg "$@"; } # Assuming you have `lg` configured as alias/log format
+glg() { g log --graph --decorate --all "$@"; }
+gls() {
+ query="$1"
+ shift
+ glog --pickaxe-regex "-S$query" "$@"
+}
+gu() { g pull "$@"; }
+gp() { g push "$@"; }
+gpom() { g push origin main "$@"; }
+gr() { g remote "$@"; }
+gra() { g rebase --abort "$@"; }
+grb() { g rebase --committer-date-is-author-date "$@"; }
+grbom() { grb --onto master "$@"; }
+grbasi() { g rebase --autosquash --interactive "$@"; }
+grc() { g rebase --continue "$@"; }
+grs() { g restore --staged "$@"; }
+grv() { g remote -v "$@"; }
+grh() { g reset --hard "$@"; }
+grH() { g reset HEAD "$@"; }
+gs() { g status -sb "$@"; }
+gsd() { g stash drop "$@"; }
+gsl() { g stash list --date=relative "$@"; }
+gsp() { g stash pop "$@"; }
+gss() { g stash show "$@"; }
+gst() { g status "$@"; }
+gsu() { g standup "$@"; } # Custom command
+gforgotrecursive() { g submodule update --init --recursive --remote "$@"; }
+gfp() { g commit --amend --no-edit && g push --force-with-lease "$@"; }
+
+# Enter directory and list contents
+function cd-clear-ls() {
+ if [ -n "$1" ]; then
+ builtin cd "$@" 2>/dev/null || { echo "cd: no such file or directory: $1"; return 1; }
+ else
+ builtin cd ~ || return 1
+ fi
+
+ echo -e "\033[H\033[J" # Clear screen but keep scroll buffer
+
+ if [ "$PWD" != "$HOME" ] && git rev-parse --is-inside-work-tree &>/dev/null; then
+ ls -a
+ else
+ ls
+ fi
+}