######################################################################
# Program:      Project setup function and Korn shell prompt
# Purpose:      The project function prj sets up for a project by
#               locating it, then setting the CDPATH to allow easier
#               access to the project's directories.
#               Window titles are also set for projects, and updated
#               to reflect running executable.
#               This is meant to be dotted (. ./this_file) or integrated
#               into a .kshrc
#               Customizing this to your needs is expected.
# Author:       Perette Barella
#---------------------------------------------------------------------

# Global variables:
# UID		Numeric user ID; used to check for being root.
# HOST		The current hostname
# KSH_TITLE	Project & shell name
# PROJECT	Project name
# PROJECTPATH	Path to project's root
# PROJECTPLACES Space-separated list of places to look for projects
# CDPATH	see ksh man page
# TPUT_*	Used to colorize/format prompt.


################################
# Project function
################################
UID=$(id -u)
[ -z "$HOST" ] && HOST=$(uname -n)

# Given a project name, sets the web base directory and sets the CDPATH
# to find directories within that project.
function prj {
	KSH_TITLE="$(basename "$SHELL")"
	if [ $# -eq 0 ]
	then
		export PROJECT=""
		# Accommodate zsh
		[ -n "$PROMPT" ] && PROMPT="%n@%m %5~%# "
		unset CDPATH
		cd ~
		return 0
	fi
	typeset abbrev="$1" project place path
	# Fix case in the project name
	for place in $PROJECTPLACES
	do
		$(whence -p ls) -1 $place |
			grep -i "^$abbrev" |
			read project || continue
		[ -d "$place/$project" ] && break
	done
	if [ "$place" = "" -o "$project" = "" -o ! -d "$place/$project" ] 
	then
		print -- "$1: Can't find project." 1>&2
		return 2
	fi
	export PROJECTPATH="$place/$project"
	cd "$PROJECTPATH" || exit 1
	# Accommodate zsh
	[ -n "$PROMPT" ] && PROMPT="[$project] %5~%# "
	export PROJECT="$project"
	export CDPATH="."
	# CDPATH says what directories to look in to find directories.
	# So, Strip off lowest directory level, since they have no subdirectories.
	find -L "$PROJECTPATH" -type d \( -name '*.xcodeproj' -o -name '_build' -o -name 'build' -o -name RCS -o -name .svn -o -name "$project-*" \) -prune -false -o -print |
	sed 's&/[^/][^/]*$&&' | uniq | while read path
	do
		CDPATH="$CDPATH:$path"
	done
	# My custom additions
	export WEBBASE="$PROJECTPATH"
}




################################
# Customized dynamic ksh prompt
################################

# Ahead are "discipline functions" to dynamically create variable values in ksh.
# In other languages, there are typically "getters".

# Provide xtitle equivalent, or a no-op if not on a suitable terminal.
# References:
# - Control sequences: https://www.xfree86.org/4.8.0/ctlseqs.html
if [[ $TERM == xterm* ]]
then
	function xtitle {
		typeset title="${*/[^ -~]/}"
		printf $'\E]2;%s\a\E]1;%s\a' "$title" "$title"
	}
else
	function xtitle {
		:
	}
fi

if whence tput >/dev/null
then
	TPUT_RED=$(tput setaf 1)
	TPUT_GREEN=$(tput setaf 2)
	TPUT_YELLOW=$(tput setaf 3)
	TPUT_WHITE=$(tput setaf 7)
	TPUT_BOLD=$(tput bold)
	# TPUT_RESET=$(tput sgr0)
	TPUT_RESET=$'\E[m'
	TPUT_CR=$'\r'
	TPUT_CLEAR=$(tput clear)$(tput E3)
fi

# Return the escape sequence to change the window title back to a default
# It would be nice to put the xtitle output into a string and return that,
# but it causes the shell to flake on ^L, tab completion, and other
# intermittent events.  This is bad form, but works, and works around bugs.
function TITLE_RESET.get {
	if [ "$UID" = "0" ]
	then
		.sh.value="$(xtitle [ROOT] $KSH_TITLE)"
	elif [ -n "$PROJECT" ]
	then
		.sh.value="$(xtitle [$project] $KSH_TITLE)"
	else
		.sh.value="$(xtitle $KSH_TITLE)"
	fi
}

# Retrieve shortened form of $PWD
# - If there is a project selected and we're in a subdirectory,
#   yield a path relative within the project.
# - Otherwise, if we're within our $HOME, yield relative to ~.
# - Otherwise, yield full path.
function RELATIVE_PWD.get {
	if [ -n "$PROJECTPATH" -a "${PWD:1:${#PROJECTPATH}}" = "${PROJECTPATH:1}/" ]
	then
		.sh.value="...${PWD:${#PROJECTPATH}}"
	elif [ "${PWD:0:${#HOME}}" = "$HOME" ]
	then
		.sh.value="~${PWD:${#HOME}}"
	else
		.sh.value="$PWD"
	fi
}

# Get the project name for the prompt.  If no project, provide user/host.
function PROJECT_ID.get {
	if [ -n "$PROJECT" ]
	then
		.sh.value="[$PROJECT]"
	else
		.sh.value="${USER}@${HOST%%.*}"
	fi
}

# If effectively root, return 'root#' in a special color, otherwise '$'.
function PROMPT_CHARACTER.get {
	if [ "$UID" = "0" ]
	then
		.sh.value="${TPUT_BOLD}${TPUT_RED}root#${TPUT_RESET}"
	else
		typeset subshell=""
		[ "$SHLVL" != "1" ] && subshell="${TPUT_YELLOW}[subshell ${SHLVL}]"
		.sh.value="${TPUT_BOLD}${subshell}${TPUT_GREEN}\$${TPUT_RESET}"
	fi
}


# Keyboard input handler to clear terminal on a ^L
function keytrap_handler {
	[[ ${.sh.edchar} == "" && ${.sh.edtext} == "" ]] &&
		print -n -- "$TPUT_CLEAR" && .sh.edchar=""
}


# If we're interactive and not running a script, set title on command.
# Only do this if:
# - stdin, stdout, and stderr all point to a terminal
# - This is the "outermost" shell being run, i.e., not a child process
# - The function is the "outermost" function being called, not nested.
# - The command being executed is not coming from a file
# - The command is not being run as part of command substitution
# - The command isn't an invokation of the keyboard trap handler

if [[ $- == *i* && -t 0 && -t 1 && -t 2 && ${SHLVL:-1} -eq 1 ]] && eval '[ "${.sh.subshell:-0}" -eq 0 -a "${SHLVL:-0}" -eq 1 ]' >/dev/null 2>&1
then
	KSH_TITLE="$0"
	trap '[ -t 0 -a -t 1 -a -t 2 -a "${SHLVL:-1}" -eq 1 -a "${.sh.level}" = "0" -a -z "${.sh.file}" -a "${.sh.subshell:-0}" = "0" -a "${.sh.command}" != "keytrap_handler" ] && xtitle "[$KSH_TITLE] ${.sh.command//[^ -~]/}"; true'  DEBUG
	# Keyboard input handler to clear terminal on a ^L
	trap keytrap_handler KEYBD
fi

# The carriage return after the title is necessary to reset ksh's count of
# the prompt length.  It uses this to know where the cursor is and how to
# reposition it.  See also: https://www.youtube.com/watch?v=bZe5J8SVCYQ
[ -z "$PS1" ] && PS1="${TPUT_CR}\${TITLE_RESET}${TPUT_CR}${TPUT_BOLD}${TPUT_YELLOW}\${PROJECT_ID}${TPUT_WHITE} \${RELATIVE_PWD}${TPUT_RESET} \${PROMPT_CHARACTER} "

