##### Common Yashrc ##### # This file is in the public domain. # enable bash-like extended expansion set --brace-expand # enable recursive pathname expansion set --extended-glob # prevent redirections from overwriting existing files set --no-clobber # don't implicitly expand non-existent variables to empty strings set --no-unset # if yash is built with command history enabled... if command --identify --builtin-command history >/dev/null; then # don't save commands starting with a space in history set --hist-space # prevent clearing history by accident history() if [ -t 0 ] && ( for arg do case "${arg}" in (-[drsw]?* | --*=*) ;; (-*c*) exit;; esac done false ) then printf 'history: seems you are trying to clear the whole history.\n' >&2 printf 'are you sure? (yes/no) ' >&2 case "$(head -n 1)" in ([Yy]*) command history "$@";; (*) printf 'history: cancelled.\n' >&2;; esac else command history "$@" fi fi # if yash is built with line-editing enabled... if command --identify --builtin-command bindkey >/dev/null; then # print job status update ASAP, but only while line-editing set --notify-le # some terminfo data are broken; meta flags have to be ignored for UTF-8 set --le-no-conv-meta # enable command line prediction set --le-predict # most users are more familiar with emacs mode than vi mode if [ -o vi ]; then set --emacs fi # some useful key bindings bindkey --emacs '\^N' beginning-search-forward bindkey --emacs '\^O' clear-candidates bindkey --emacs '\^P' beginning-search-backward bindkey --emacs '\N' complete-next-column bindkey --emacs '\P' complete-prev-column # key bindings for vi mode, some of which are from emacs mode bindkey --vi-insert '\^A' beginning-of-line bindkey --vi-insert '\^B' backward-char bindkey --vi-insert '\^D' eof-or-delete bindkey --vi-insert '\#' eof-or-delete bindkey --vi-insert '\^E' end-of-line bindkey --vi-insert '\^F' forward-char bindkey --vi-insert '\^K' forward-kill-line bindkey --vi-insert '\^N' beginning-search-forward bindkey --vi-insert '\^O' clear-candidates bindkey --vi-insert '\^P' beginning-search-backward bindkey --vi-insert '\^U' backward-kill-line bindkey --vi-insert '\$' backward-kill-line bindkey --vi-insert '\^W' backward-delete-viword bindkey --vi-insert '\^Y' put-left bindkey --vi-insert '\N' complete-next-column bindkey --vi-insert '\P' complete-prev-column bindkey --vi-command '\^N' beginning-search-forward bindkey --vi-command '\^P' beginning-search-backward fi # some useful shortcuts alias -- -='cd -' alias la='ls -a' ll='ls -l' lla='ll -a' alias h='fc -l' alias j='jobs' alias r='fc -s' # avoid removing/overwriting existing files by accident cp() if [ -t 0 ]; then command cp -i "$@"; else command cp "$@"; fi mv() if [ -t 0 ]; then command mv -i "$@"; else command mv "$@"; fi rm() if [ -t 0 ]; then command rm -i "$@"; else command rm "$@"; fi # normally yash is more POSIX-compliant than /bin/sh :-) sh() { yash --posix "$@"; } yash() { command yash "$@"; } # By re-defining 'yash' using the 'command' built-in, the 'jobs' built-in # prints a command name that exposes the arguments like # 'yash --posix -n foo.sh' rather than a command name that hides the # arguments like 'yash --posix "${@}"'. This applies to the 'yash' command # invoked via the 'sh' function. # ensure job control works as expected case $- in (*m*) trap - TSTP TTIN TTOU esac # if the terminal supports color... if [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then # make command output colorful if ls --color=auto -d / >/dev/null 2>&1; then ls() { command ls --color=auto "$@"; } fi if grep --color=auto -q X <</dev/null; then grep() { command grep --color=auto "$@"; } fi if ggrep --color=auto -q X <</dev/null; then ggrep() { command ggrep --color=auto "$@"; } fi fi # if vim is available... if command --identify vim >/dev/null 2>&1; then # prefer vim over vi vi() { vim "$@"; } view() { vim -R "$@"; } vim() { command vim "$@"; } # Re-definition hack. See above. fi # avoid removing existing crontab by accident crontab() if [ -t 0 ] && ( for arg do case "${arg}" in (-*r*) exit;; esac done false ) then printf 'crontab: seems you are trying to clear your crontab.\n' >&2 printf 'are you sure? (yes/no) ' >&2 case "$(head -n 1)" in ([Yy]*) command crontab "$@";; (*) printf 'crontab: cancelled.\n' >&2;; esac else command crontab "$@" fi # an alias that opens a file if command --identify xdg-open >/dev/null 2>&1; then alias o='xdg-open' elif command --identify cmd.exe wslpath >/dev/null 2>&1; then alias o='open' open() ( target="$(wslpath -w "$1")" cd /mnt/c # suppress UNC path warning cmd.exe /c start "$target" ) elif command --identify cygstart >/dev/null 2>&1; then alias o='cygstart' elif [ "$(uname)" = Darwin ] 2>/dev/null; then alias o='open' fi # define some basic variables if missing : ${PAGER:=less} ${EDITOR:=vi} ${FCEDIT:=$EDITOR} : ${LOGNAME:=$(logname)} ${HOSTNAME:=$(uname -n)} # disable confusing treatment of arguments in the echo command : ${ECHO_STYLE:=RAW} # variables needed for command history if ! [ "${HISTFILE-}" ]; then HISTFILE=${XDG_STATE_HOME:-~/.local/state}/yash/history # handle old default history path if [ -f ~/.yash_history ]; then printf 'Note: The default history file location has been changed to "%s".\n' "$HISTFILE" >&2 HISTFILE=~/.yash_history printf 'Please consider moving the existing file from the current location "%s".\n' "$HISTFILE" >&2 fi fi # create HISTFILE parent directory if missing ! [ -d "${HISTFILE%/*}" ] && mkdir -p "${HISTFILE%/*}" HISTSIZE=5000 # HISTRMDUP makes prediction less accurate # HISTRMDUP=500 # default mail check interval is too long MAILCHECK=0 # emulate bash's $SHLVL if [ "${_old_shlvl+set}" != set ]; then _old_shlvl=${SHLVL-} fi SHLVL=$((_old_shlvl+1)) 2>/dev/null || SHLVL=1 export SHLVL # initialize event handlers COMMAND_NOT_FOUND_HANDLER=() PROMPT_COMMAND=() POST_PROMPT_COMMAND=() YASH_AFTER_CD=() # define prompt if [ -n "${SSH_CONNECTION-}" ]; then _hc='\fy.' # yellow hostname for SSH remote else _hc='\fg.' # green hostname for local fi if [ "$(id -u)" -eq 0 ]; then _uc='\fr.' # red username for root _2c='\fr.' # red PS2 for root else _uc=$_hc _hc= # same username color as hostname for non-root user _2c= # PS2 in normal color for non-root user fi # The main prompt ($YASH_PS1) contains the username, hostname, working # directory, last exit status (only if non-zero), and $SHLVL (only if # non-one). YASH_PS1=$_uc'${LOGNAME}'$_hc'@${HOSTNAME%%.*}\fd. '\ '${${${PWD:/~/\~}##*/}:-$PWD} ${{?:/0/}:+\\fr.$?\\fd. }${{SHLVL-0}:/1}\$ ' YASH_PS1R='\fc.${_vcs_info}' YASH_PS1S='\fo.' YASH_PS2=$_2c'> ' #YASH_PS2R= YASH_PS2S=$YASH_PS1S YASH_PS4='\fm.+ ' YASH_PS4S='\fmo.' unset _hc _uc _2c # No escape sequences allowed in the POSIXly-correct mode. PS1='${LOGNAME}@${HOSTNAME%%.*} '$PS1 # find escape sequence to change terminal window title case "$TERM" in (xterm|xterm[+-]*|gnome|gnome[+-]*|putty|putty[+-]*|cygwin) _tsl='\033];' _fsl='\a' ;; (*) _tsl=$( (tput tsl 0; echo) 2>/dev/null | sed -e 's;\\;\\\\;g' -e 's;;\\033;g' -e 's;;\\a;g' -e 's;%;%%;g') _fsl=$( (tput fsl ; echo) 2>/dev/null | sed -e 's;\\;\\\\;g' -e 's;;\\033;g' -e 's;;\\a;g' -e 's;%;%%;g') ;; esac # if terminal window title can be changed... if [ "$_tsl" ] && [ "$_fsl" ]; then # set terminal window title on each prompt _set_term_title() if [ -t 2 ]; then printf "$_tsl"'%s@%s:%s'"$_fsl" "${LOGNAME}" "${HOSTNAME%%.*}" \ "${${PWD:/$HOME/\~}/#$HOME\//\~\/}" >&2 fi PROMPT_COMMAND=("$PROMPT_COMMAND" '_set_term_title') # reset window title when changing host or user ssh() { if [ -t 2 ]; then printf "$_tsl"'ssh %s'"$_fsl" "$*" >&2; fi command ssh "$@" } su() { if [ -t 2 ]; then printf "$_tsl"'su %s'"$_fsl" "$*" >&2; fi command su "$@" } sudo() { if [ -t 2 ]; then printf "$_tsl"'sudo %s'"$_fsl" "$*" >&2; fi command sudo "$@" } doas() { if [ -t 2 ]; then printf "$_tsl"'doas %s'"$_fsl" "$*" >&2; fi command doas "$@" } fi # support VS Code shell integration if [ "${TERM_PROGRAM-}" = vscode ]; then # wrap the prompt with markers YASH_PS1="\[\e]633;A\e\\\\\]$YASH_PS1\[\e]633;B\e\\\\\]" YASH_PS1R="\[\e]633;A\e\\\\\]$YASH_PS1R\[\e]633;B\e\\\\\]" YASH_PS2="\[\e]633;A\e\\\\\]$YASH_PS2\[\e]633;B\e\\\\\]" # notify the terminal before executing a command _send_cmdline() { # Following the pre-execution mark, we also print the command line content # so the terminal can receive the correct command especially in the presence # of the right prompt. Some characters need to be escaped, so we print them # one by one. printf '\033]633;C\033\\\033]633;E;' typeset cmd="$COMMAND" while [ "$cmd" ]; do case "${cmd[1]}" in (';'|[-' ']) printf '\\x%.2X' "'${cmd[1]}" ;; (\\) printf '\\\\' ;; (*) printf %c "${cmd[1]}" ;; esac cmd=${cmd[2,-1]} done printf '\033\\' } POST_PROMPT_COMMAND=("$POST_PROMPT_COMMAND" _send_cmdline) # notify the terminal after executing a command _send_exit_status() { printf '\033]633;D;%d\033\\' "$?" } PROMPT_COMMAND=("$PROMPT_COMMAND" _send_exit_status) fi # define function that updates $_vcs_info and $_vcs_root _update_vcs_info() { typeset type branch { read --raw-mode type read --raw-mode _vcs_root read --raw-mode branch } <( exec 2>/dev/null typeset COMMAND_NOT_FOUND_HANDLER= while true; do if [ -e .git ] || [ . -ef "${GIT_WORK_TREE-}" ]; then printf 'git\n%s\n' "${GIT_WORK_TREE:-$PWD}" git branch --no-color | sed -n '/^\*/s/^..//p' exit elif [ -d .hg ]; then printf 'hg\n%s\n' "$PWD" exec cat .hg/branch elif [ -d .svn ]; then printf 'svn\n' _vcs_root=$(svn info --show-item=wc-root) printf '%s\n' "$_vcs_root" path=$(svn info --show-item=relative-url) case $path in (*/branches/*) printf '%s\n' "${${path#*/branches/}%%/*}" esac exit fi if [ / -ef . ] || [ . -ef .. ]; then exit fi \command cd -P .. done ) case "$type#$branch" in (hg#default) _vcs_info='hg';; (git#master) _vcs_info='git';; (*# ) _vcs_info="$type";; (* ) _vcs_info="$type@$branch";; esac } # update $_vcs_info on each prompt PROMPT_COMMAND=("$PROMPT_COMMAND" '_update_vcs_info') # these aliases choose a version controlling program for the current directory alias _vcs='${${_vcs_info:?not in a version-controlled directory}%%@*}' alias ci='_vcs commit' alias co='_vcs checkout' alias di='_vcs diff' alias log='_vcs log' alias st='_vcs status' alias up='_vcs update' # when a directory name is entered as a command, treat as "cd" _autocd() if [ -d "$1" ]; then HANDLED=true cd -- "$@" break -i fi COMMAND_NOT_FOUND_HANDLER=("$COMMAND_NOT_FOUND_HANDLER" '_autocd "$@"') # treat command names starting with % as "fg" _autofg() if [ $# -eq 1 ]; then case $1 in (%*) HANDLED=true fg "$1" break -i esac fi COMMAND_NOT_FOUND_HANDLER=("$COMMAND_NOT_FOUND_HANDLER" '_autofg "$@"') # print file type when executing non-executable files _file_type() if [ -e "$1" ] && ! [ -d "$1" ]; then file -- "$1" fi COMMAND_NOT_FOUND_HANDLER=("$COMMAND_NOT_FOUND_HANDLER" '_file_type "$@"') # vim: set et sw=2 sts=2 tw=78 ft=sh: