__p_exit_codes_to_SIGs () { sigcolour="$1" failcolour="$2" successcolour="$3" ; shift 3 local -n exit_status=$1 ; shift local -n exit_colour=$1 ; shift local exit_codes=( "$@" ) local exit_code local i=0 for exit_code in "${exit_codes[@]}" ; do exit_status[$i]="${exit_code}" # FIXME: process signals instead of exit codes > 128 if [ "$exit_code" -gt 128 ] ; then local signal=$((exit_code-128)) signal="SIG$(kill -l "$signal" 2> /dev/null)" || signal="" if [ -n "$signal" ] ; then exit_status[$i]="${signal}" exit_colour[$i]="${sigcolour}" else exit_colour[$i]="${failcolour}" fi elif [ "$exit_code" -ne 0 ] ; then exit_colour[$i]="${failcolour}" else exit_colour[$i]="${successcolour}" fi i=$((i+1)) done } __prompt_command () { local exit_code="$?" pipe_exit_codes=( "${PIPESTATUS[@]}" ) local s='\[' local e='\]' local cr='\r' local lf='\n' local user='\u' local host='\h' local cwd='\w' local bel='\a' local reset="$(tput sgr0)" local reset_colour="$(tput op)$(tput oc)" local bold="$(tput bold)" local flash="$(tput flash)" local black="$(tput setaf 0)" local red="$(tput setaf 1)" local green="$(tput setaf 2)" local yellow="$(tput setaf 3)" local blue="$(tput setaf 4)" local magenta="$(tput setaf 5)" local cyan="$(tput setaf 6)" local white="$(tput setaf 7)" # Save the status colours without terminal start/end sequences # since the status display uses those start/end sequences already # since they have to be in PS1 rather than inserted at PS1 evaluation local sigcolour="$blue" local failcolour="$red" local successcolour="" # Save the reset sequences without terminal start/end sequences # since the status display uses those start/end sequences already # since they have to be in PS1 rather than inserted at PS1 evaluation __p_reset="$reset" # Add terminal start/end sequences to shorten colour declarations reset="${s}${reset}${e}" reset_colour="${s}${reset_colour}${e}" bold="${s}${bold}${e}" black="${s}${black}${e}" red="${s}${red}${e}" green="${s}${green}${e}" yellow="${s}${yellow}${e}" blue="${s}${blue}${e}" magenta="${s}${magenta}${e}" cyan="${s}${cyan}${e}" white="${s}${white}${e}" # Define colours for the chroot, user, host and cwd local chrootcolour="${red}${bold}" local usercolour="${green}${bold}" local hostcolour="${green}${bold}" # modulate colors based on host and user to differentiate shells #local usercolor="${s}$(tput setaf $(((1+$(cksum <<<"$USER"|cut -f 1 -d ' ')) %7 +1 )))${e}" #local hostcolour="${s}$(tput setaf $(((4+$(cksum <<<"$HOSTNAME"|cut -f 1 -d ' ')) %7 +1 )))${e}" local cwdcolour="${blue}${bold}" # Define various separators local status_indicator_primary_status_sep=' ' local primary_status_pipe_status_sep=' ' local pipe_status_start='(' local pipe_status_sep=' | ' local pipe_status_end=')' local status_chroot_sep="${lf}" local chroot_user_sep='' local user_host_sep='@' local host_cwd_sep=' ' local cwd_cmd_sep=' \$ ' # Define the status indicator string if [ "$TERM" = xterm-256color ] ; then local status_indicator='⭲' else local status_indicator='->|' fi # Define the printf used for the git prompt fragment # the empty value tells git to use the default local git_printf='' # Define if command success should be shown local show_success=0 # Used when showing/hiding status parts at PS1 evaluation time __p_print=9999 __p_pipe_print=9999 # Calculate the text and colour of the primary status __p_primary_status=( ) __p_primary_colour=( ) __p_exit_codes_to_SIGs "$sigcolour" "$failcolour" "$successcolour" __p_primary_status __p_primary_colour "$exit_code" # Calculate the colour of the status prefix __p_status_prefix_colour="${__p_primary_colour[0]}" # Calculate the text and colour of the pipe statuses __p_pipe_status=( ) __p_pipe_colour=( ) __p_exit_codes_to_SIGs "$sigcolour" "$failcolour" "$successcolour" __p_pipe_status __p_pipe_colour "${pipe_exit_codes[@]}" # Disable the status display when asked to not display success local pipe_exit_code_sum=0 local pipe_exit_code for pipe_exit_code in "${pipe_exit_codes[@]}"; do ((pipe_exit_code_sum+=pipe_exit_code)) done if [ "$show_success" -eq 0 -a "$exit_code" -eq 0 -a "$pipe_exit_code_sum" -eq 0 ] ; then __p_print=0 __p_pipe_print=0 fi # Pass the status prefix to PS1 evaluation time __p_status_prefix="${status_indicator}${status_indicator_primary_status_sep}" local status_prefix="${s}"'${__p_status_prefix_colour:0:((__p_print*__ps1_print))}'"${e}" status_prefix="${status_prefix}"'${__p_status_prefix:0:((__p_print*__ps1_print))}' status_prefix="${status_prefix}${s}"'${__p_reset:0:((__p_print*__ps1_print))}'"${e}" # Pass the primary status to PS1 evaluation time local primary_status="${s}"'${__p_primary_colour[0]:0:((__p_print*__ps1_print))}'"${e}" primary_status="${primary_status}"'${__p_primary_status[0]:0:((__p_print*__ps1_print))}' primary_status="${primary_status}${s}"'${__p_reset:0:((__p_print*__ps1_print))}'"${e}" # Disable the pipe statuses when identical to the primary status # FIXME: disable pipe statuses after Ctrl+Z if [ "${#pipe_exit_codes[@]}" -eq 1 -a "$exit_code" -eq "${pipe_exit_codes[0]}" ] ; then __p_pipe_print=0 primary_status_pipe_status_sep= pipe_status_start= __p_pipe_status=() __p_pipe_colour=() pipe_status_sep= pipe_status_end= fi # Pass the separator between the primary and pipe statuses to PS1 evaluation time __p_primary_status_pipe_status_sep="$primary_status_pipe_status_sep" primary_status_pipe_status_sep='${__p_primary_status_pipe_status_sep:0:((__p_pipe_print*__ps1_print))}' # Pass the pipe statuses start to PS1 evaluation time __p_pipe_status_start="$pipe_status_start" pipe_status_start='${__p_pipe_status_start:0:((__p_pipe_print*__ps1_print))}' # Pass the pipe statuses to PS1 evaluation time local pipe_statuses=( ) local pipe_status_count=${#__p_pipe_status[@]} __p_pipe_status_sep=( ) local i for (( i=0; i < pipe_status_count; i++ )) ; do pipe_statuses[$i]="${s}"'${__p_pipe_colour['"$i"']:0:((__p_pipe_print*__ps1_print))}'"${e}" pipe_statuses[$i]="${pipe_statuses[$i]}"'${__p_pipe_status['"$i"']:0:((__p_pipe_print*__ps1_print))}' pipe_statuses[$i]="${pipe_statuses[$i]}${s}"'${__p_reset:0:((__p_pipe_print*__ps1_print))}'"${e}" # Append the separator except for the last item if [ "$i" -lt "$((pipe_status_count-1))" ] ; then __p_pipe_status_sep[$i]="$pipe_status_sep" else __p_pipe_status_sep[$i]="" fi pipe_statuses[$i]="${pipe_statuses[$i]}"'${__p_pipe_status_sep['"$i"']:0:((__p_pipe_print*__ps1_print))}' done # Join the pipe statuses local ifs="$IFS" IFS="" local pipe_status="${pipe_statuses[*]}" IFS="$ifs" unset ifs # Pass the pipe statuses end to PS1 evaluation time __p_pipe_status_end="$pipe_status_end" pipe_status_end='${__p_pipe_status_end:0:((__p_pipe_print*__ps1_print))}' # Pass the separator between the status and chroot to PS1 evaluation time printf -v __p_status_chroot_sep "$status_chroot_sep" status_chroot_sep='${__p_status_chroot_sep:0:((__p_print*__ps1_print))}' # Detect when there was not a syntax error or Ctrl+C PS0='${IFS:0:0*((__p_is_cmd=1, 0))}' # Pass the status print decision code to PS1 evaluation time local init='${IFS:0:0*((' # Do not print the status at the start or after pressing enter # or when there are two syntax errors or Ctrl+C in a row # FIXME: differentiate between Ctrl+C Ctrl+C and Ctrl+C Enter # FIXME: disable the status after tab completion output # FIXME: disable the status on the first line (after terminal clearing) init="${init}"'__ps1_print=(__p_last_cmd_no!=0 && (__p_last_cmd_no!=\# || (__p_is_cmd!=1 && __p_last_status!=$?))), ' # Disable printing the pipe status for syntax error or Ctrl+C (doesn't update PIPESTATUS) init="${init}"'__p_pipe_print=(__p_is_cmd==1 ? __p_pipe_print : 0), ' # Reset the commandness as syntax errors and Ctrl+C don't do that init="${init}"'__p_is_cmd=0, ' # Store the last command number and last status for next evaluation init="${init}"'__p_last_cmd_no=\#, __p_last_status=$?, 0))}' # Prepare the window title, status, chroot for the prompt local title="${s}\\e]0;${chroot}${chroot_user_sep}${user}${user_host_sep}${host}${host_cwd_sep}${cwd}${bel}${e}" local status="${status_prefix}${primary_status}${primary_status_pipe_status_sep}${pipe_status_start}${pipe_status}${pipe_status_end}" local chroot="${debian_chroot:+($debian_chroot)}" # Colourise the chroot, user, host, cwd chroot="${chrootcolour}${chroot}${reset}" user="${usercolour}${user}${reset}" host="${hostcolour}${host}${reset}" cwd="${cwdcolour}${cwd}${reset}" # Prepare the pre/post text local pre="${status}${status_chroot_sep}${chroot}${chroot_user_sep}${user}${user_host_sep}${host}${host_cwd_sep}${cwd}${pre}" local post="$cwd_cmd_sep" # Only set the window title in supported terminals case "$TERM" in xterm*|rxvt*) pre="${title}${pre}" ;; *) ;; esac # Reset to avoid leaking any non-terminated colours or highlighting from the command pre="${reset}${reset_colour}${pre}" # Put the newness calculation at the very start of the prompt # so that it can be used to show/hide any info in the prompt pre="${init}${pre}" # Set PS1 to the git PS1 plus prepared pre/post text __git_ps1 "$pre" "$post" "$git_printf" } PROMPT_COMMAND='__prompt_command'