Skip to content

Instantly share code, notes, and snippets.

@andurilan
Created January 22, 2019 10:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andurilan/fafab6a5771375d7eca2ad8fe5f681a5 to your computer and use it in GitHub Desktop.
Save andurilan/fafab6a5771375d7eca2ad8fe5f681a5 to your computer and use it in GitHub Desktop.
Korn Shell Environment
# -*-sh-*-
#
# .kshrc - Korn shell 93 startup file
umask 0077
set -o emacs
set +o multiline
# Taken from bash completion support core Git. | Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org> |
# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). | Distributed under the GNU General Public License, version 2.0.
# __gitdir accepts 0 or 1 arguments (i.e., location)
# returns location of .git repo
function __gitdir
{
if [ -z "${1-}" ]; then
if [ -n "${__git_dir-}" ]; then
echo "$__git_dir"
elif [ -d .git ]; then
echo .git
else
git rev-parse --git-dir 2>/dev/null
fi
elif [ -d "$1/.git" ]; then
echo "$1/.git"
else
echo "$1"
fi
}
#
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# returns text to add to bash PS1 prompt (includes branch name)
function __git_ps1
{
typeset g="$(__gitdir)"
if [ -n "$g" ]; then
typeset r
typeset b
if [ -f "$g/rebase-merge/interactive" ]; then
r="|REBASE-i"
b="$(cat "$g/rebase-merge/head-name")"
elif [ -d "$g/rebase-merge" ]; then
r="|REBASE-m"
b="$(cat "$g/rebase-merge/head-name")"
else
if [ -d "$g/rebase-apply" ]; then
if [ -f "$g/rebase-apply/rebasing" ]; then
r="|REBASE"
elif [ -f "$g/rebase-apply/applying" ]; then
r="|AM"
else
r="|AM/REBASE"
fi
elif [ -f "$g/MERGE_HEAD" ]; then
r="|MERGING"
elif [ -f "$g/BISECT_LOG" ]; then
r="|BISECTING"
fi
b="$(git symbolic-ref HEAD 2>/dev/null)" || {
b="$(
case "${GIT_PS1_DESCRIBE_STYLE-}" in
(contains)
git describe --contains HEAD ;;
(branch)
git describe --contains --all HEAD ;;
(describe)
git describe HEAD ;;
(* | default)
git describe --exact-match HEAD ;;
esac 2>/dev/null)" ||
b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
b="unknown"
b="($b)"
}
fi
typeset w
typeset i
typeset s
typeset u
typeset c
if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
c="BARE:"
else
b="GIT_DIR"
fi
elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
git diff --no-ext-diff --ignore-submodules \
--quiet --exit-code || w="*"
if git rev-parse --quiet --verify HEAD >/dev/null; then
git diff-index --cached --quiet \
--ignore-submodules HEAD -- || i="+"
else
i="#"
fi
fi
fi
if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
fi
if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
if [ -n "$(git ls-files --others --exclude-standard)" ]; then
u="%"
fi
fi
fi
if [ -n "${1-}" ]; then
printf "$1%s" "$c${b##refs/heads/}$w$i$s$u$r"
else
printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
fi
fi
}
#
# DIRECTORY MANIPULATION FUNCTION, REPLACES CD
# Uses global parameters _push_max _push_top _push_stack
#
# Change directory and put directory on front of stack
function _cd
{
typeset dir=
integer n=0 type=4
case $1 in
-|-1|2) # \cd -
n=_push_top type=1
;;
-[1-9]*([0-9])) # \cd -n
n=_push_top+${1#-}-1 type=2
;;
1) # keep present directory
print -r - "$PWD"
return
;;
[1-9]*([0-9])) # \cd n
n=_push_top+${1}-2 type=2
;;
*) if ((_push_top <= 0))
then type=3 n=_push_max
fi
esac
if ((type<3))
then if ((n >= _push_max+1))
then print -u2 cd: Directory stack not that deep.
return 1
else dir=${_push_stack[n]}
fi
fi
case $dir in
\~*) dir=$HOME${dir#\~}
esac
\cd "${dir:-$@}" >| /dev/null || return 1
dir=${OLDPWD#$HOME/}
case $TERM in
630)
print "\033[?${#PWD};2v$PWD\c"
;;
esac
case $dir in
$HOME)
dir=\~
;;
/*) ;;
*) dir=\~/$dir
esac
case $type in
1) # swap first two elements
_push_stack[_push_top]=$dir
;;
2|3) # put $dir on top and shift down by one until top
integer i=_push_top
for dir in "$dir" "${_push_stack[@]}"
do ((i > n)) && break
_push_stack[i]=$dir
i=i+1
done
;;
4) # push name
_push_stack[_push_top=_push_top-1]=$dir
;;
esac
# print -r - "$PWD"
}
# Uses global parameter _push_stack
# Display directory stack -- $HOME displayed as ~
function dirs
{
typeset dir="${PWD#$HOME/}"
case $dir in
$HOME)
dir=\~
;;
/*) ;;
*) dir=\~/$dir
esac
PS3=
select i in "$dir" "${_push_stack[@]}"
do :
done < /dev/null
}
# Uses global parameter _push_stack
# Menu driven change directory command
function mcd
{
typeset dir="${PWD#$HOME/}"
case $dir in
$HOME)
dir=\~
;;
/*) ;;
*) dir=\~/$dir
esac
PS3='Select by number or enter a name: '
select dir in "$dir" "${_push_stack[@]}"
do if _cd $REPLY
then return
fi
done
}
# Uses global parameters _push_max _push_top _push_stack
# Pops the top directory
function popd
{
typeset dir
if ((_push_top >= _push_max))
then print popd: Nothing to pop.
return 1
fi
case $1 in
"")
dir=${_push_stack[_push_top]}
case $dir in
\~*) dir=$HOME${dir#\~}
esac
\cd "$dir" || return 1
;;
+[1-9]|+[1-9][0-9])
typeset savedir
integer i=_push_top$1-1
if ((i >= _push_max))
then print pushd: Directory stack not that deep.
return 1
fi
while ((i > _push_top))
do _push_stack[i]=${_push_stack[i-1]}
i=i-1
done
;;
*) print pushd: Bad directory.
return 1
esac
unset '_push_stack[_push_top]'
_push_top=_push_top+1
dirs
}
# Uses global parameters _push_max _push_top _push_stack
# Change directory and put directory on front of stack
function pushd
{
typeset dir= type=0
integer i
case $1 in
"") # pushd
if ((_push_top >= _push_max))
then print pushd: No other directory.
return 1
fi
type=1 dir=${_push_stack[_push_top]}
;;
+[1-9]|+[1-9][0-9]) # pushd +n
integer i=_push_top$1-1
if ((i >= _push_max))
then print pushd: Directory stack not that deep.
return 1
fi
type=2 dir=${_push_stack[i]}
;;
*) if ((_push_top <= 0))
then print pushd: Directory stack overflow.
return 1
fi
esac
case $dir in
\~*) dir=$HOME${dir#\~}
esac
\cd "${dir:-$1}" > /dev/null || return 1
dir=${OLDPWD#$HOME/}
case $dir in
$HOME)
dir=\~
;;
/*) ;;
*) dir=\~/$dir
esac
case $type in
0) # pushd name
_push_stack[_push_top=_push_top-1]=$dir
;;
1) # pushd
_push_stack[_push_top]=$dir
;;
2) # push +n
type=${1#+} i=_push_top-1
set -- "${_push_stack[@]}" "$dir" "${_push_stack[@]}"
shift $type
for dir
do (((i=i+1) < _push_max)) || break
_push_stack[i]=$dir
done
esac
dirs
}
# History file.
export HISTFILE=~/.hist$$
trap 'rm -f $HISTFILE' EXIT
export CDSTACK=32
export FPATH=$HOME/.funcs
integer _push_max=${CDSTACK} _push_top=${CDSTACK}
# Directory manipulation functions.
unalias cd
alias cd=_cd
alias pu=pushd
alias po=popd
alias d=dirs
alias h=history
alias j=jobs
alias m=$PAGER
alias ll='ls -laFo'
alias l='ls -l'
alias g='fgrep -i'
alias c=clear
alias ec=emacsclient
alias mutt='TERM=rxvt-256color mutt'
# Don't get fancy if we have a dumb terminal. This happens for
# example if we're accessing files remotely through tramp in emacs.
[[ $TERM == 'dumb' ]] && return 0
# Generate an associative array containing the alternative characters
# set for the terminal. See termcap (5) for more details.
eval typeset -A altchar=\($(tput acsc | sed -E "s/(.)(.)/['\1']='\2' /g")\)
# Generate two associative arrays containing the background
# and foreground colors.
typeset -A fg bg
function cleanhist
{
maxtime=1
find -E ~ -maxdepth 1 -regex '.*/\.hist[0-9]+' -Btime +${maxtime} -delete
}
function load_colors
{
typeset color
integer i=0
for color in black red green brown blue magenta cyan white; do
fg+=([$color]=$(tput setaf $i))
bg+=([$color]=$(tput setab $i))
(( i++ ))
done
fg+=([reset]=$(tput setaf 9))
bg+=([reset]=$(tput setab 9))
}
function init_parms
{
_user=$(whoami)
_host=$(hostname -s)
_tty=$(tty | sed s@/dev/@@)
_rprompt=
_lpos=
_rpos=
_cont_prompt=
case $(id -u) in
0) _prompt=\#;;
*) _prompt=\$;;
esac
_prompt=$(tput md)${_prompt}$(tput me)
# Use alternative characters to draw lines if supported or degrade
# to normal characters if not.
alt_on=$(tput as)
alt_off=$(tput ae)
_hbar=${altchar[q]:--}
_vbar=${altchar[x]:-\|}
_ulcorner=${altchar[l]:--}
_llcorner=${altchar[m]:--}
_urcorner=${altchar[k]:--}
_lrcorner=${altchar[j]:--}
_lbracket=${altchar[u]:-\[}
_rbracket=${altchar[t]:-\]}
integer colormax=$(tput colors)
if (( ${colormax:-0} >= 8 )); then
load_colors
case $(id -u) in
0)
_bgcolor=${bg[red]}
_fgcolor=${fg[white]}
;;
*)
_bgcolor=${bg[white]}
_fgcolor=${fg[black]}
;;
esac
fi
# Enable alternate char set.
tput enacs
}
# Like pwd but display the $HOME directory as ~
function _pwd
{
typeset dir="${PWD:-$(pwd -L)}"
dir="${dir#$HOME/}"
case $dir in
"$HOME")
dir=\~ ;;
/*)
;;
*)
dir=\~/$dir ;;
esac
print $dir
}
#### Two lines prompt.
# This function is executed before PS1 is referenced. It sets _rpos to
# the position of the right prompt and _lpos to the position after the
# left prompt. See discipline function in the man page of ksh93.
function PS1.get
{
typeset rc=$? # save the return value of the last command
typeset dir="$(_pwd)" padline
typeset uprompt="--[${_user}@${_host}:${_tty}]--(${dir})--"
typeset rprompt="-(${_rstatue})--" lprompt="--(${_lstatue}|$)- "
integer termwidth=$(tput co)
integer offset=$(( ${#uprompt} - ${termwidth} ))
integer i
# Truncate the current directory if too long and define a line
# padding such that the upper prompt occupy the terminal width.
if (( $offset > 0 )) ; then
dir="...${dir:$(( $offset + 3 ))}"
padline=""
else
offset=$(( - $offset ))
padline=${alt_on}
for (( i=0; i<$offset; i++ )); do
padline=${padline}${_hbar}
done
padline=${padline}${alt_off}
fi
_rpos=$(( $termwidth - ${#rprompt} ))
_lpos=${#lprompt}
_cont_prompt=
# Upper prompt.
.sh.value="\
${alt_on}${_ulcorner}${_hbar}${_lbracket}${alt_off}\
${_bgcolor}${_fgcolor}\
${_user}@${_host}:${_tty}\
${fg[reset]}${bg[reset]}\
${alt_on}${_rbracket}${alt_off}\
${padline}\
${alt_on}${_hbar}${_hbar}${alt_off}\
$(tput md)(${dir})$(tput me)\
${alt_on}${_hbar}${_urcorner}${alt_off}"
# If the terminal doesn't ignore a newline after the last column
# and has automatic margin (e.g. cons25), a newline or carriage
# return if written will be on the next line. So don't add a
# newline and for good mesure, move the cursor to the left before
# writing cr at the end of a line.
if ! tput am || tput xn; then
.sh.value=${.sh.value}$'\n'
fi
# Lower prompt using carriage return to display the right prompt.
.sh.value="${.sh.value}\
$(tput RI $_rpos)\
${_rprompt}\
$(tput le)$(tput cr)\
${alt_on}${_llcorner}${_hbar}${alt_off}\
(${_lstatue}${alt_on}${_vbar}${alt_off}${_prompt})\
${alt_on}${_hbar}${alt_off} "
return $rc
}
# Statue in the left prompt
function _lstatue.get
{
.sh.value=$(date +%H:%M:%S)
}
export GIT_PS1_SHOWDIRTYSTATE=yes
export GIT_PS1_SHOWUNTRACKEDFILES=yes
# Statue in the right prompt
function _rstatue.get
{
# Use the current branch in a git repository or the current date.
typeset b=$(__git_ps1 git:)
.sh.value=${b:-$(date "+%a, %d %b")}
}
# Right prompt.
function _rprompt.get
{
.sh.value="\
${alt_on}${_hbar}${alt_off}\
(${_rstatue})\
${alt_on}${_hbar}${_lrcorner}${alt_off}"
}
# Continuation prompt
function PS2.get
{
_cont_prompt=yes
.sh.value="${alt_on}${_hbar}${_hbar}${alt_off} "
}
# Deletion characters in emacs editing mode and from stty.
typeset -A _delchars=(
[$'\ch']=DEL
[$'\177']=BS
[$'\E\177']=KILL-REGION
[$'\cw']=BACKWARD-KILL-WORD
[$'\cu']=KILL-LINE
)
# Erase the right prompt if the text reaches it and redraw it if the
# text fits in the region between the left prompt and the right one.
function _rpdisplay
{
integer width=$(( $_rpos - $_lpos - 1))
integer pos=${#.sh.edtext}
typeset -S has_rprompt=yes
typeset ch=${.sh.edchar}
if [[ -z $has_rprompt ]]; then
if (( $pos < $width )) ||
( (($pos == $width+1)) && [[ -n ${_delchars[$ch]} ]] ); then
tput sc; tput vi
tput cr; tput RI $_rpos
print -n -- "${_rprompt}"
tput rc; tput ve
has_rprompt=yes
fi
elif (( $pos >= $width )) && [[ -z ${_delchars[$ch]} ]]; then
tput ce
has_rprompt=
fi
}
# Set the line status to the command buffer and the window title
# to the command name.
function _setscreen
{
typeset hs=${.sh.edtext/#*(\s)/} # delete leading blanks
typeset cmd=${hs/%@(\s)*}
typeset args=${hs/#+(\S)/}
typeset sudopts=AbEHhKkLlnPSVvg:p:U:u:C:c:
typeset -S lastcmd
if [[ -n $cmd ]]; then
cmd=${cmd##*/}
if [[ $cmd == sudo || $cmd == *=* ]]; then
# Find the real command name
set -- $args
{
while getopts $sudopts c; do
; # skip options
done
} 2>/dev/null
shift $((OPTIND-1))
if [[ -n $1 ]]; then
cmd=${1##*/}
fi
fi
# Ignore variable assignment
if [[ $cmd != *=* ]]; then
lastcmd=$cmd
fi
fi
print -nR $'\E_'${hs}$'\E\\'
print -nR $'\Ek'${lastcmd}$'\E\\'
}
# Assoctiate a key with an action.
typeset -A Keytable
function keybind # key [action]
{
typeset key=$(print -f "%q" "$2")
case $# in
2) Keytable[$1]=' .sh.edchar=${.sh.edmode}'"$key"
;;
1) unset Keytable[$1]
;;
*) print -u2 "Usage: $0 key [action]"
return 2 # usage errors return 2 by default
;;
esac
}
function _keytrap
{
eval "${Keytable[${.sh.edchar}]}"
# Execute only if we're not on a continuation prompt
if [[ -z $_cont_prompt ]]; then
[[ $TERM == screen && ${.sh.edchar} == $'\r' ]] && _setscreen
_rpdisplay
fi
}
trap _keytrap KEYBD
# Swap ^W and M-baskspace in emacs editing mode.
keybind $'\cw' $'\E\177'
keybind $'\E\177' $'\cw'
init_parms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment