diff --git a/.bashrc b/.bashrc index ea9796f..2ff1c63 100644 --- a/.bashrc +++ b/.bashrc @@ -1,5 +1,153 @@ #!/bin/bash - not strictly necessary, but helps nano with syntax highlighting. +__prompt_git_status() { + # Generate a git branch/status prompt. + # Based on git-prompt.sh by Shawn O. Pearce . + # Environment variables: + # GIT_PROMPT_SHOW_TYPE=1 - Show type of repository (Bare, Shallow). + # GIT_PROMPT_SHOW_IGNORED=1 - Show a ! if the directory is ignored. + # GIT_PROMPT_SHOW_UNSTAGED=1 - Show a * if there are unstaged changes (superceeded by above). + # GIT_PROMPT_SHOW_UNTRACKED=1 - Show a ? if there are untracked files in the working directory (superceeded by above). + # GIT_PROMPT_SHOW_STASH=1 - Show a & if there is a stash in this repository (superceeded by above). + # GIT_PROMPT_SHOW_UPSTREAM=1 - Show status of this repository compaired to upstream: + # ?? - No upstream set. + # == - Working tree is equal to upstream. + # <> - Divergent from upstream. + # >> or >x - Working tree is ahead of upstream (x = commits ahead when used with next option). + # << or /dev/null) ) + ERR="$?" + + # Do nothing if there's an error. + (( ERR >= 1 )) || { (( ERR == 128 )) && (( ${#GIT_REPO_INFO[@]} != 3 )); } && return "$RET" + + # Generate the prompt. + if [[ "${GIT_REPO_INFO[2]}" == "true" ]]; then + # If in the git directory, use a special branch marker. + GIT_PROMPT+="!GIT_DIR!" + elif [[ "${GIT_REPO_INFO[3]}" == "true" ]]; then + # If in the working directory, generate the prompt. + # Add status markers. + [[ -n "$GIT_PROMPT_SHOW_TYPE" ]] && { + if [[ "${GIT_REPO_INFO[0]}" == "true" ]]; then + GIT_PROMPT+="B:" + elif [[ "${GIT_REPO_INFO[1]}" == "true" ]]; then + GIT_PROMPT+="S:" + fi + } + + # Add the branch. + GIT_PROMPT+="$(git describe --contains --all HEAD)" + + # Add a marker if directory is ignored, there's untracked files or a stash. + if [[ -n "$GIT_PROMPT_SHOW_IGNORED" ]] && git check-ignore -q "${PWD#"$(git rev-parse --show-toplevel)/"}"; then + GIT_PROMPT+=" !" + elif [[ -n "$GIT_PROMPT_SHOW_UNSTAGED" ]] && git ls-files --modified --exclude-standard --directory --error-unmatch -- ':/*' >/dev/null 2>&1; then + GIT_PROMPT+=" *" + elif [[ -n "$GIT_PROMPT_SHOW_UNTRACKED" ]] && git ls-files --others --exclude-standard --directory --error-unmatch -- ':/*' >/dev/null 2>&1; then + GIT_PROMPT+=" ?" + elif [[ -n "$GIT_PROMPT_SHOW_STASH" ]] && git rev-parse --verify --quiet refs/stash >/dev/null; then + GIT_PROMPT+=" &" + fi + + # Get upstream status. + [[ -n "$GIT_PROMPT_SHOW_UPSTREAM" ]] || [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]] && { + COUNT="$(git rev-list --count --left-right '@{upstream}'...HEAD 2>/dev/null | tr '[:blank:]' ' ')" + case "$COUNT" in + "") + # No upstream. + GIT_PROMPT+=" ??" + ;; + "0 0") + # Equal to upstream. + GIT_PROMPT+=" ==" + ;; + "0 "*) + # Ahead of upstream. + GIT_PROMPT+=" >" + if [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]]; then + # Show the number of the difference. + GIT_PROMPT+="${COUNT#0 }" + else + GIT_PROMPT+=">" + fi + ;; + *" 0") + # Behind upstream. + GIT_PROMPT+=" <" + if [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]]; then + # Show the number of the difference. + GIT_PROMPT+="${COUNT% 0}" + else + GIT_PROMPT+="<" + fi + ;; + *) + # Divergent from upstream. + GIT_PROMPT+=" <>" + ;; + esac + } + fi + + # Output the prompt. + # shellcheck disable=SC2059 + printf -- "$1" "$GIT_PROMPT" + + # Return the original error code. + return "$RET" +} + +__prompt_user_colour() { + # Determine the colour of the username in the prompt. + local RET="$?" + + if [[ "$LOGNAME" == "root" ]]; then + printf "%s" "1;31m" # Bright Red. + elif [[ "$LOGNAME" == "tadgy" ]]; then + printf "%s" "1;32m" # Bright Green. + else + printf "%s" "1;36m" # Bright Cyan. + fi + + return "$RET" +} + +__nanorc_prompt_command() { + # Dynamically handle .nanorc file versions. + hash nano >/dev/null 2>&1 && { + # shellcheck disable=SC2155 + local NANO_VER="$(command nano --version | awk '/version/ { print $4 }' | cut -d. -f1)" + + # Darwin specifc .nanorc version. + [[ "$PLATFORM" == "Darwin" ]] && NANO_VER="darwin" + + if [[ -f "$HOME/.nanorc-$NANO_VER" ]]; then + if (( NANO_VER <= 4 )); then + ( cd "$HOME" && ln -sf ".nanorc-$NANO_VER" ".nanorc" ) + else + # shellcheck disable=SC2139 + alias nano="nano -f \"$HOME/.nanorc-$NANO_VER\"" + fi + else + if (( NANO_VER <= 4 )); then + [[ -L "$HOME/.nanorc" ]] && rm -f "$HOME/.nanorc" + else + unalias nano + fi + printf "\\033[1;31m%s\\033[39m\\n" "No .nanorc for version '$NANO_VER'." >&2 + fi + } +} + __find_ssh_agent_sock() { # Find an *active* ssh agent socket. # Returns: 0 = Found an active socket. @@ -24,23 +172,20 @@ __read_ssh_agents() { # Read all the known ssh agent sockets into an array. # Returns: 0 = Processed and read the agents file without issue. # 1 = Error processing/reading the agents file. - local ERR FD I PLATFORM SOCK + local ERR FD I SOCK [[ ! -e "$HOME/.ssh/agents" ]] && touch "$HOME/.ssh/agents" - # Determine the platform being logged into. - PLATFORM="$(uname -s)" - # Lock the ~/.ssh/agents file. if [[ "$PLATFORM" == "Linux" ]]; then # Linux has 'flock', thankfully. exec {FD}<"$HOME/.ssh/agents" && flock -E 10 -e -w 0.5 "$FD" ERR=$? if (( ERR == 10 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 elif (( ERR > 0 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Flock usage error." >&2 return 1 fi @@ -61,11 +206,11 @@ __read_ssh_agents() { fi done (( ERR != 0 )) && { - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 } else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on '$PLATFORM'." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "File locking unsupported on '$PLATFORM'." >&2 return 1 fi @@ -86,7 +231,7 @@ __read_ssh_agents() { # Error out if the data couldn't be read. (( ERR != 0 )) && { - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to read ssh-agent socket list." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to read ssh-agent socket list." >&2 unset SSH_AUTH_SOCKS SSH_AGENTS_MTIME return 1 } @@ -114,9 +259,6 @@ __write_ssh_agents() { (( $? <= 1 )) && SOCKS+=("${SSH_AUTH_SOCKS[$I]}") done - # Determine the platform being logged into. - PLATFORM="$(uname -s)" - # Lock the ~/.ssh/agents file. if [[ "$PLATFORM" == "Linux" ]]; then # Make sure SSH_AUTH_SOCKS has the most up to date data. @@ -127,10 +269,10 @@ __write_ssh_agents() { exec {FD}<"$HOME/.ssh/agents" && flock -E 10 -e -w 0.5 "$FD" ERR=$? if (( ERR == 10 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 elif (( ERR > 0 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Flock usage error." >&2 return 1 fi elif [[ "$(uname -s)" == "Darwin" ]]; then @@ -150,11 +292,11 @@ __write_ssh_agents() { fi done (( ERR != 0 )) && { - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 } else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on '$PLATFORM'." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "File locking unsupported on '$PLATFORM'." >&2 return 1 fi @@ -171,7 +313,7 @@ __write_ssh_agents() { rm -f "$HOME/.ssh/agents" 2>/dev/null elif (( ERR >= 1 )); then rm -f "$HOME/.ssh/agents" 2>/dev/null - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to write ssh-agent socket list." >&2 + printf "\\033[1;31m%s\\033[39m\\n" "Failed to write ssh-agent socket list." >&2 return 1 fi return 0 @@ -198,17 +340,17 @@ __ssh_agent_prompt_command() { # Find a new socket to use. if __find_ssh_agent_sock; then - printf "\\033[1;33;40m%s\\033[0;39m\\n" "Connected to existing ssh-agent socket." + printf "\\033[1;33m%s\\033[39m\\n" "Connected to existing ssh-agent socket." sleep 0.5 else # Start a new agent. eval "$(ssh-agent -s 2>/dev/null | grep -v 'echo'; printf "%s" "ERR=${PIPESTATUS[0]}")" (( ERR > 0 )) && { - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to start new ssh-agent - continuing with no agent." + printf "\\033[1;31m%s\\033[39m\\n" "Failed to start new ssh-agent - continuing with no agent." sleep 0.5 return 1 } - printf "\\033[1;32;40m%s\\033[0;39m\\n" "Started new ssh-agent." + printf "\\033[1;32m%s\\033[39m\\n" "Started new ssh-agent." __write_ssh_agents sleep 0.5 fi @@ -239,14 +381,17 @@ HISTSIZE=-1 HISTTIMEFORMAT="%d/%m/%y %H:%M:%S " history -r -# Determine the colour of the username in the prompt. -if [[ "$LOGNAME" == "root" ]]; then - COLOUR=1 # Red -elif [[ "$LOGNAME" == "tadgy" ]]; then - COLOUR=2 # Green -else - COLOUR=5 # Purple -fi +# The commands to execute before the prompt is displayed. +PROMPT_COMMAND="__nanorc_prompt_command; __ssh_agent_prompt_command" + +# Git prompt options. +GIT_PROMPT_SHOW_TYPE=1 +GIT_PROMPT_SHOW_IGNORED=1 +GIT_PROMPT_SHOW_UNSTAGED=1 +GIT_PROMPT_SHOW_UNTRACKED=1 +GIT_PROMPT_SHOW_STASH=1 +GIT_PROMPT_SHOW_UPSTREAM=1 +GIT_PROMPT_SHOW_UPSTREAM_EXTENDED=1 # Version specific set up. if (( BASH_VERSINFO[0] >= 4 )); then @@ -256,21 +401,17 @@ if (( BASH_VERSINFO[0] >= 4 )); then # Trim the path in the prompt. PROMPT_DIRTRIM=2 # Coloured username + host + directory: - PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\w\[$(tput sgr0)\] ->" + PS1='[\[\033[$(__prompt_user_colour)\]\u\[\033[39m\]@\[\033[1;33m\]\h\[\033[39m\]] \[\033[1;34m\]\w\[\033[39m\]$(__prompt_git_status "\[\\033[1;35m\] (%s)\[\\033[39m\]") ->' else # Set the prompts. # Coloured username + host + directory: # shellcheck disable=SC2154 - PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}')\[$(tput sgr0)\] ->" + PS1='[\[\033[$(__prompt_user_colour)\]\u\[\033[39m\]@\[\033[1;33m\]\h\[\033[39m\]] \[\033[1;34m\]\$(printf \"%s\" \"\${PWD/#\$HOME/~}\" | awk -F/ '\''{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}'\'')\[\033[39m\]$(__prompt_git_status "\[\\033[1;35m\] (%s)\[\\033[39m\]") ->' fi -unset COLOUR # Set the debugger prompt. # shellcheck disable=SC2155 -export PS4="+(\[\e[1;33;40m\]\$?\[$(tput sgr0)\]) \[$(tput bold)$(tput setaf 4)\]\${BASH_SOURCE##*/}\[$(tput sgr0)\]\${FUNCNAME[0]:+(\[$(tput bold)$(tput setaf 2)\]\${FUNCNAME[0]}\[$(tput sgr0)\])}:\[$(tput bold)$(tput setaf 1)\]\$LINENO\[$(tput sgr0)\]: " - -# The commands to execute before the prompt is displayed. -PROMPT_COMMAND="__ssh_agent_prompt_command" +export PS4='+(\[\033[1;33m\]$?\[\033[39m\]) \[\033[1;34m\]${BASH_SOURCE##*/}\[\033[39m\]${FUNCNAME[0]:+(\[\033[1;32m\]${FUNCNAME[0]}\[\033[39m\])}:\[\033[1;31m\]$LINENO\[\033[39m\]: ' # Common aliases. hash bc >/dev/null 2>&1 && alias bc='bc -lq' @@ -314,22 +455,6 @@ PLATFORM="$(uname -s)" # Platform specific set up. if [[ "$PLATFORM" = "Linux" ]]; then # Linux specific functions. - hash nano >/dev/null 2>&1 && nano() { - # shellcheck disable=SC2155 - local NANO_VER="$(command nano --version | awk '/version/ { print $4 }' | cut -d. -f1)" - if [[ -f "$HOME/.nanorc-$NANO_VER" ]]; then - if (( NANO_VER <= 4 )); then - ( cd "$HOME" && ln -sf ".nanorc-$NANO_VER" ".nanorc" ) - command nano "$@" - rm -f "$HOME/.nanorc" - else - command nano -f "$HOME/.nanorc-$NANO_VER" "$@" - fi - else - printf "%s: %s\\n" "${FUNCNAME[0]}" "no .nanorc for version '$NANO_VER'" >&2 - return 1 - fi - } hash ps grep >/dev/null 2>&1 && psgrep() { if [[ -n "$1" ]]; then # shellcheck disable=SC2009 @@ -353,23 +478,10 @@ if [[ "$PLATFORM" = "Linux" ]]; then hash pinfo >/dev/null 2>&1 && alias info='pinfo' hash ping >/dev/null 2>&1 && alias ping='ping -b' elif [[ "$PLATFORM" = "Darwin" ]]; then - # Darwin specific functions. - hash nano >/dev/null 2>&1 && nano() { - if [[ -f "$HOME/.nanorc-darwin" ]]; then - ( cd "$HOME" && ln -sf ".nanorc-darwin" ".nanorc" ) - command nano "$@" - rm -f "$HOME/.nanorc" - else - printf "%s: %s\\n" "${FUNCNAME[0]}" "no .nanorc-darwin found" >&2 - return 1 - fi - } - # Darwin specific aliases. hash df >/dev/null 2>&1 && alias df='df -P' hash ls >/dev/null 2>&1 && alias ls='ls -bFG' hash top >/dev/null 2>&1 && alias top='top -o cpu -S' else - echo "${BASH_SOURCE##*/}: unsupported platform: $PLATFORM" >&2 + printf "%s: %s\\n" "${BASH_SOURCE##*/}" "unsupported platform: $PLATFORM" >&2 fi -unset PLATFORM diff --git a/.gitignore b/.gitignore index 8b7c9e7..6cfb9ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,31 @@ -* -!.* -!.*/** +/* +!/.* +!/.*/ +!/.*/** *~ .nfs* *.save .*.swp -.ICEauthority -.Xauthority -.Xauthority.orig -.ab* -.alpine-smime/ -.aspell.* -.bash_history -.bash_history-* -.cache/ -.dbus/ -.fltk/ -.lesshst -.lynx.cookies -.mozilla/ -.nanorc -.pki/ -.rnd -.screen-*/ -.sqlite_history -.thumbnails/ -.vmware -.vnc -.wget-hsts -.xca -.xsession-errors* +/.ICEauthority +/.Xauthority* +/.ab* +/.alpine-smime/ +/.aspell.* +/.bash_history* +/.cache/ +/.dbus/ +/.fltk/ +/.lesshst +/.lynx.cookies +/.mozilla/ +/.nanorc +/.pki/ +/.rnd +/.screen-*/ +/.sqlite_history +/.thumbnails/ +/.vmware/ +/.vnc/ +/.wget-hsts +/.xca/ +/.xsession-errors*