# All of this is only known to work on OpenBSD's fork of pdksh [[ $(uname -s) == OpenBSD ]] || return # Frontend to controlling prompt prompt() { # If no arguments, print the prompt strings as they are if ! (($#)) ; then printf '%s\n' PS1="$PS1" PS2="$PS2" PS3="$PS3" PS4="$PS4" return fi # What's done next depends on the first argument to the function case $1 in # Turn complex, colored PS1 and debugging PS4 prompts on on) # Basic prompt shape depends on whether we're in SSH or not PS1= if [[ -n $SSH_CLIENT ]] || [[ -n $SSH_CONNECTION ]] ; then PS1=$PS1'\u@\h:' fi PS1=$PS1'\w' # Add sub-commands; VCS, job, and return status checks PS1=$PS1'$(prompt vcs)$(prompt job)' # Add prefix and suffix PS1='${PROMPT_PREFIX}'$PS1'${PROMPT_SUFFIX}' # Add terminating "$" or "#" sign PS1=$PS1'\$' # Count available colors typeset -i colors colors=$( { tput Co || tput colors } 2>/dev/null ) # Prepare reset code typeset reset reset=$( { tput me || tput sgr0 } 2>/dev/null ) # Decide prompt color formatting based on color availability typeset format case $colors in # Check if we have non-bold bright green available 256) format=$( { : "${PROMPT_COLOR:=12}" tput AF "$PROMPT_COLOR" || tput setaf "$PROMPT_COLOR" || tput AF "$PROMPT_COLOR" 0 0 || tput setaf "$PROMPT_COLOR" 0 0 } 2>/dev/null ) ;; # If we have only eight colors, use bold green 8) format=$( { : "${PROMPT_COLOR:=4}" tput AF "$PROMPT_COLOR" || tput setaf "$PROMPT_COLOR" tput md || tput bold } 2>/dev/null ) ;; # For all other terminals, we assume non-color (!), and we just # use bold *) format=$( { tput md || tput bold } 2>/dev/null ) ;; esac # String it all together PS1='\['"$format"'\]'"$PS1"'\['"$reset"'\] ' PS2='> ' PS3='? ' PS4='+<$?> $LINENO:' ;; # Git prompt function git) # Bail if we're not in a work tree--or, implicitly, if we don't # have git(1). typeset iswt iswt=$(git rev-parse --is-inside-work-tree 2>/dev/null) [[ $iswt = true ]] || return # Refresh index so e.g. git-diff-files(1) is accurate git update-index --refresh >/dev/null 2>&1 # Find a local branch, remote branch, or tag (annotated or not), or # failing all of that just show the short commit ID, in that order # of preference; if none of that works, bail out typeset name name=$( { git symbolic-ref --quiet HEAD || git describe --tags --exact-match HEAD || git rev-parse --short HEAD } 2>/dev/null) || return name=${name##*/} [[ -n $name ]] || return # Check various files in .git to flag processes typeset proc [[ -d .git/rebase-merge || -d .git/rebase-apply ]] && proc=${proc:+$proc,}'REBASE' [[ -f .git/MERGE_HEAD ]] && proc=${proc:+$proc,}'MERGE' [[ -f .git/CHERRY_PICK_HEAD ]] && proc=${proc:+$proc,}'PICK' [[ -f .git/REVERT_HEAD ]] && proc=${proc:+$proc,}'REVERT' [[ -f .git/BISECT_LOG ]] && proc=${proc:+$proc,}'BISECT' # Collect symbols representing repository state typeset state # Upstream HEAD has commits after local HEAD; we're "behind" typeset -i behind behind=$(git rev-list --count 'HEAD..@{u}' 2>/dev/null) ((behind)) && state=${state}'<' # Local HEAD has commits after upstream HEAD; we're "ahead" typeset -i ahead ahead=$(git rev-list --count '@{u}..HEAD' 2>/dev/null) ((ahead)) && state=${state}'>' # Tracked files are modified; double exclamation mark because # that's how you get a literal one in pdksh PS1 git diff-files --no-ext-diff --quiet || state=${state}'!!' # Changes are staged git diff-index --cached --no-ext-diff --quiet HEAD 2>/dev/null || state=${state}'+' # There are some untracked and unignored files git ls-files --directory --error-unmatch --exclude-standard \ --no-empty-directory --others -- ':/*' >/dev/null 2>&1 && state=${state}'?' # There are stashed changes git rev-parse --quiet --verify refs/stash >/dev/null && state=${state}'^' # Print the status in brackets; add a git: prefix only if there # might be another VCS prompt (because PROMPT_VCS is set) printf '(%s%s%s%s)' \ "${PROMPT_VCS:+git:}" "$name" "${proc:+:$proc}" "$state" ;; # Revert to simple inexpensive prompts off) PS1='\$ ' PS2='> ' PS3='? ' PS4='+ ' ;; # VCS wrapper prompt function; print the first relevant prompt, if any vcs) typeset vcs for vcs in "${PROMPT_VCS[@]:-git}" ; do prompt "$vcs" && return done ;; # Show the count of background jobs in curly brackets, if not zero job) typeset -i jobc jobc=$(jobs -p | sed -n '$=') ((jobc)) && printf '{%u}' "$jobc" ;; # Print error *) printf 'prompt: Unknown command %s\n' "$1" >&2 return 2 ;; esac } # Start with full-fledged prompt prompt on