# Copyright (c) 2022 Igor Bogomazov <[email protected]>

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# Supported shells:
# 1. bash
# 2. mksh / lksh
# 3. zsh
# 4. yash
# 5. dash
# 6. ksh93
# 7. busybox sh

# Some notes for future editions:
# 1. echo works differently in Bash and mksh, avoided here in favor of printf
# 2. this file is sourced from the main shell RC file, any combination of
#    shell options is possible
# 3. ksh93 when sourcing a file reads it whole up, therefore no aliases
#    defined in the same file get replaced
# 4. assuming terminal supports \b (backspace) character
# 5. PS1 expansion is not mandated by POSIX, more than that only parameter
#    expansion is mentioned in the standard; this implementation relies on
#    command substitution

# display GIT-repo and branch names
shrc_git_ps1() {
       set -- "$(basename -- "$(
               LC_ALL=C git rev-parse --show-toplevel 2>>/dev/null
       )" )"
       [ -z "$1" ] &&
               return
       set -- "$1" "$(
               LC_ALL=C git branch 2>>/dev/null |
               sed -nr 's@^\*[[:space:]]*(.*)$@\1@p; t lbl; b; :lbl q'
       )"
       printf ' [%s:%s]' "$1" "$2"
}

# display SVN root URL basename
shrc_svn_ps1() {
       set -- "$( basename -- "$(
               LC_ALL=C svn info 2>>/dev/null |
               sed -nr '/Root:/ s@^.*[[:space:]]([^[:space:]]+)$@\1@p; t lbl; b; :lbl q'
       )" )"
       [ -z "$1" ] &&
               return
       printf ' [SVN:%s]' "$1"
}

# set red color if non-zero exit code
shrc_dollar_color() {
       [ $shrc_exit_code -eq 0 ] &&
               return
       printf '\033[01;31m'
}

# display exit code with dollar sign if the last command failed
shrc_dollar_ps1() {
       [ "$shrc_exit_code" -ne 0 ] &&
               printf '(%s)' "$shrc_exit_code"
       printf '%s' '$'
}

shrc_erase_text() {
       while [ ${#1} -gt 0 ] ; do
               printf '\b'
               set -- "${1%?}"
       done
}

# variable to keep the exit code of the last command
shrc_exit_code=
# newline character
# bash-5.0 (fixed in 5.1): \n behaved wrong with lines wider than a screen
shrc_newline="$(printf '\n.')"
shrc_newline="${shrc_newline%.}"

# assume it is Bash, that means \[ and \] are markers for zero width sequences
PS1=
# remember exit code of the previous command
PS1="$PS1"'\[$((shrc_exit_code=$?))$(shrc_erase_text "$shrc_exit_code")\]'
# print out date before each prompt
PS1="$PS1"'$(date)'"$shrc_newline"
# display username and hostname in green
PS1="$PS1"'\[$(printf "\033[01;32m")\]'
PS1="$PS1"'$(whoami)@$(hostname)'
PS1="$PS1"'\[$(printf "\033[00m")\]'
PS1="$PS1"':'
# working directory in blue
PS1="$PS1"'\[$(printf "\033[01;34m")\]'
PS1="$PS1"'$PWD'
PS1="$PS1"'\[$(printf "\033[00m")\]'
# display git info
PS1="$PS1"'\[$(printf "\033[01m")\]'
PS1="$PS1"'$(shrc_git_ps1)'
PS1="$PS1"'\[$(printf "\033[00m")\]'
# display SVN info
PS1="$PS1"'\[$(printf "\033[01m")\]'
PS1="$PS1"'$(shrc_svn_ps1)'
PS1="$PS1"'\[$(printf "\033[00m")\]'
# display colorful dollar sign
PS1="$PS1"'\[$(shrc_dollar_color)\]'
PS1="$PS1"'$(shrc_dollar_ps1)'
# reset any text attributes and append whitespace
PS1="$PS1"'\[$(printf "\033[00m")\] '

if printf '%s\n' "${KSH_VERSION-}" | grep -qiw ksh ; then
       # change markers for non-printing sequences if in MirBSD ksh
       PS1="$(printf '%s\n' "$PS1" | sed -r 's/\\[]\[]/'$'\001''/g')"
       PS1="$(printf '\001\r%s' "$PS1")"
elif [ -n "${ZSH_VERSION-}" ] ; then
       # change markers for non-printing sequences if in zsh
       PS1="$(printf '%s\n' "$PS1" | sed -r 's/\\\[/%{/g ; s/\\\]/%}/g')"
       # and enable PS1 expansion as in bash/ksh
       setopt PROMPT_SUBST
elif [ -n "${YASH_VERSION:-}" ] ; then
       # yash uses bash-style markers
       true
elif [ -z "${BASH_VERSION-}" ] ; then
       # for other shells just remove the markers
       PS1="$(printf '%s\n' "$PS1" | sed -r 's/\\[]\[]//g')"
fi