###################################################################
#       File:          zshenv/zshrc
#
#       Description:   Anssi's zsh configuration file
#
#       Revision:      1.78
#       Release date:  2025-11-26 16:28:56 +0200
###################################################################

# This file is sourced on all invocations of the shell.
# If the -f flag is present or if the NO_RCS option is
# set within this file, all other initialization files
# are skipped.
#
# This file should contain commands to set the command
# search path, plus other important environment variables.
# This file should not contain commands that produce
# output or assume the shell is attached to a tty.
#
# Global Order: zshenv, zprofile, zshrc, zlogin

# TODO: these should handle multiple parameters.
remove_from_path () {
    if [[ -n "$1" ]] ; then
        set -A path ${path:#"$1"}
    fi
}

append_path () {
    if [[ -n "$1"  && -d "$1" ]] ; then
        remove_from_path "$1"
        path+="$1"
    fi
}

prepend_path () {
    if [[ -n "$1"  && -d "$1" ]] ; then
        remove_from_path "$1"
        path[1,0]="$1"
    fi
}

# Only add these dirs to path if they exist.
if [[ -d ~/.local/bin ]]; then
        prepend_path ~/.local/bin
fi

# TODO: use prepend_path instead.
if [[ -d ~/bin ]]; then
        set -A path ${path:#/home/as/bin}
        PATH=~/bin:$PATH
fi

# shell opts
setopt auto_list
setopt correct
# Suspending prints pid and job numbers
setopt longlistjobs
setopt nopromptcr

# Tired of having to quote ^ or # or on the command line for grep or
# sed or git diff when excluding dirs. ~ is also affected but rarely
# an issue. Home directory expansion and directory stack expansion
# don't need escaping the ~ even if extended_glob is on.

# This shouldn't be enabled (setopt was removed) but just in case.
unsetopt extended_glob

bindkey -e
bindkey '\ep' history-beginning-search-backward
bindkey '\en' history-beginning-search-forward

# shell vars
export NOTIFY

# This just excludes / from WORDCHARS
#WORDCHARS='*?_-.[]~&;!#$%^(){}<>'

# This exludes / . ? from WORDCHARS and adds = for M-backspace while ^W works
# as before.
function backward-kill-partial-word {
    local WORDCHARS="${WORDCHARS//[\/.\?]/}="
    zle backward-kill-word "$@"
}
zle -N backward-kill-partial-word
for x in '^Xw' '^[^?' '^[^H'; do
    bindkey "$x" backward-kill-partial-word
done
unset x

# Automatically Report CPU usage for commands running longer than 10 seconds.
REPORTTIME=10

# history saving
export HISTSIZE=500
export HISTFILE=~/.zsh_history
export SAVEHIST=500

# environment vars
export ALTERNATE_EDITOR=emacs
# Color theme for bat
# Solarized for Debian 12 and MSYS, ansi-dark for Debian 11 and Ubuntu 20.04.
if [[ -n "$MSYSTEM" ]]
then
    export BAT_THEME='Solarized (dark)'
else
    lr_loc=`whence lsb_release`
    if [[ $? == 0 ]]
    then
        os_release=`lsb_release -r|awk '{print $2}'`
        os_id=`lsb_release -i|awk '{print $3}'`
        if [[ "$os_id" == Debian && "$os_release" -eq 11 ]]
        then
            export BAT_THEME=ansi-dark
        elif [[ "$os_id" == Ubuntu && "$os_release" == 20.04 ]]
        then
            export BAT_THEME=ansi-dark
        elif [[ "$os_id" == Debian && "$os_release" -eq 12 ]]
        then
            export BAT_THEME='Solarized (dark)'
        fi
    fi
fi

export EDITOR=vi
export LESS=-M
# With Git color coded output less -r is better than plain less.
export PAGER='less -r'
# Since Git supports it...
export GIT_PAGER='less -iX -E -R'
export VISUAL=vi

export euro=5.94573

# Why not... zsh/mathfunc includes no constants.
export e=2.7182818285
export pi=3.1415926536

# Remove wrong aliases...
unalias m >& /dev/null

# Aliases
# if ci and co exist in $PATH, define checkin and checkout aliases

tmpxyz=`whence ci`
if [[ -n "$tmpxyz" && -x "$tmpxyz" ]] ; then
        alias checkin='ci -u'
fi

tmpxyz=`whence co`
if [[ -n "$tmpxyz" && -x "$tmpxyz" ]] ; then
        alias checkout='co -l'
fi

unset tmpxyz

# Needed for msys2 in Windows so that zsh doesn't try to correct ping
# to PING etc.
if [[ $OS == Windows_NT ]]
then
    alias ping='nocorrect ping'
    alias route='nocorrect route'
    alias tracert='nocorrect tracert'
fi

# Debian and derivatives call bat batcat which is really inconvenient.
# Hence this alias.
if [[ -n $(whence batcat) ]]
then
    alias bat=batcat
fi

# functions

# alias list_mounts='mount | egrep -v "cgroup|rpc|ramfs|tmpfs|^sys|on /dev|on /proc|on /sys|on /var" | sort'
# works, needs a few more exclusions though, at least autofs.

# Turns out the pipeline can be a function without weirdness
# myfunc() { mount | fgrep -vf <(awk '/^nodev/{print $2}' /proc/filesystems); }
list_local_mounts() {
    mount | fgrep -vf <(awk '/^nodev/{print $2}' /proc/filesystems)
}

## Show a raw man page
## \todo Handle multiple files
function m {
        if [ $# -gt 0 ]
        then
                nroff -man $1 | less
        fi
}

## Create a directory and push into it
## \todo Handle multiple dirs
function mdcd { mkdir -p $1 ; pushd $1 }

lth() {
    if [[ -z "$*" ]] ; then
        mydirs=.
    else
        # This assignment means mydirs is an array...
        # Gotta love zsh syntax :)
        mydirs=($@)
    fi
    for i in $mydirs ; do
        ls --color=always -lt $i | head -20
    done
}

# Debian includes original-awk which is small and
# supports ** operator for power, whereas mawk does not.
if [[ -n "`whence original-awk`" ]] ; then
        awk=original-awk
elif [[ -n "`whence gawk`" ]] ; then
        awk=gawk
elif [[ -n "`whence nawk`" ]] ; then
        awk=nawk
elif [[ -n "`whence mawk`" ]] ; then
        awk=mawk
elif [[ -n "`whence awk`" ]] ; then
        awk=awk
fi
if [[ -n "$awk" ]] ; then
        calcfunc() { $awk -v OFMT=%f "BEGIN{ print $* }"; setopt glob; }
elif [[ `echo $ZSH_VERSION|cut -d. -f1` -eq 4 ]] ; then
        calcfunc() { echo $(( $* )); setopt glob; }
fi
# If the function was defined, define an alias too
# In theory, I might run this code on a machine without awk
# and zsh 3 or 2...
if [[ "`whence calcfunc`" == calcfunc ]] ; then
        alias calc="setopt noglob; calcfunc"
fi

# This only does decimal to hex. Awk thing would do octal too, but who needs it?
# Figuring out how to do it in zsh is not done... The tcsh alias is
# alias 2hex awk \'BEGIN\{printf \"%X\\n\", \!:1}\'
# Some weird stuff is printed on jolt with zsh 4.3.0-dev-2, seems to work fine on
# 4.2.0 on pepper at least. Doing printf "%X\n", $1; was the problem, removing those
# fixed things on jolt.
# This understands octal if OCTAL_ZEROS is set. Otherwise, any base can be given
# with base#number notation.
#2hex() { printf "%X\n" $1 }

# Awk version of 2hex, for reference purposes... Might be useful in bash.
#a2hex() { awk "BEGIN{printf \"%X\n\", $*}" }

# Real number conversion in the zsh way...
echo_in_other_base() { out=$(( $1[##$2] )) ; echo $out }

# So then we have 2hex like this:
2hex() { echo_in_other_base $1 16 }

# And 2bin like this:
# Doesn't convert negative numbers to 2's complement, though...
2bin() { echo_in_other_base $1 2 }

2oct() { echo_in_other_base $1 8 }

# Works for conversions to decimal as well...
# A leading zero means octal, if OCTAL_ZEROES is set,
# otherwise use 8#xx...
2dec() { echo_in_other_base $1 10 }

# Default prompt
export PROMPT=%m":"%4~"> "

# Enable some git support in the prompt
if [[ -r ~/.config/zsh/git-prompt.sh ]]
then
    . ~/.config/zsh/git-prompt.sh
    setopt PROMPT_SUBST
    #PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
    # Anssi modded prompt
    PS1='%m:%(5~|…/%4~|%~)$(__git_ps1 " (%s)")> '
    GIT_PS1_SHOWCOLORHINTS=true
fi

# Solarized dircolors for ls
dircolor_theme=~/.config/dircolors/dircolors.256dark
if [[ -r "$dircolor_theme" ]]; then
    eval $(dircolors "$dircolor_theme")
fi

do_compinit() {
    autoload -U compinit
    if [[ "$OSTYPE" == cygwin ]] ; then
        # Skip security check on Cygwin
        compinit -C
    else
        compinit
    fi
}

# completion control
major_version=`echo $ZSH_VERSION|cut -d. -f1`
if [[ "$major_version" -ge 4 ]]
then
    do_compinit
    # From zsh-lovers at https://grml.org/zsh/zsh-lovers.html
    zstyle ':completion:*' use-cache on
    # N.B.: cache-path a folder and zsh creates it if not present.
    anssi_cache_path=${HOME}/.cache/zsh
    zsh_cache_path=${anssi_cache_path}/completion_cache
    if [[ ! -d $anssi_cache_path ]]
    then
        mkdir -p $anssi_cache_path
    fi
    zstyle ':completion:*' cache-path $zsh_cache_path
fi

## precmd functions:
## 1. to update terminal window title
## 2. reset window name in screen / tmux back to zsh when a program exits
precmd () {
    [[ -t 1 ]] || return
    case $TERM in
        (*xterm* | rxvt | (dt|k|E)term | terminator) print -Pn "\e]0;%n@%m:%4/\a" ;;
    esac
    if [[ -n "$STY" || -n "$TMUX" ]]
    then
        print -n "\033kzsh\033\134"
    fi
}

## preexec, put running program name in screen / tmux window name
## \todo code formatting is weird, this is copy pasted from somewhere.
preexec () {
    if [[ -n "$WINDOW" || -n "$TMUX" ]]
    then
        setopt shwordsplit
        my_cmd_array=(${2})
        case $my_cmd_array[1] in
            (ssh) cmd="$my_cmd_array[1] $my_cmd_array[2]"  ;;
            (fg) cmd=`jobs|grep +|awk '{print $4}'`
                 if [[ -z "cmd" ]]
                 then
                     cmd=fg
                 fi ;;
            (*) cmd=$my_cmd_array[1]  ;;
        esac
        print -n "\033k$cmd\033\134"
        unsetopt shwordsplit
    fi
}

# A couple of handy functions to find out system version and Debian version
# From https://dataswamp.org/~incal/conf/.zsh/distro
debian-version () {
    lsb_release -d -s
}

version () {
    uname -a
    echo

    lsb_release -a
    echo

    grep Revision /proc/cpuinfo
    echo

    local os_file=/etc/os-release
    [[ -f $os_file ]] && cat $os_file

    echo $XDG_CURRENT_DESKTOP
}

# local stuff
if [[ -r ~/.zshenv.local ]]
then
        . ~/.zshenv.local
fi

# Automatic URL quoting
# Conditional just in case url-quote-magic isn't around.
autoload +X -Uz url-quote-magic >& /dev/null && zle -N self-insert url-quote-magic

# Bind insert-composed-char to F5
# Useful compositions are DG for °, My for µ
# Man zshcontrib for more.
autoload -Uz insert-composed-char
zle -N insert-composed-char
bindkey '\e[15~' insert-composed-char

# Aliases

# Putting the most common ones right here.
alias cp='cp -i'
alias dirdu='find -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 du -s'
alias hd='od -v -t x1z -A x'
alias gendate='date +%Y-%m-%d'
alias gendt="date +%Y-%m-%dT%H:%M:%S"
alias j=jobs
#alias l="ls -lF|more"
#Possible shorter alternative to restore old more behavior:
#if more -e /dev/null; then
#    export MORE=-e
#fi
# The alias is then just:
#alias l='ls -lF|more'

# more_version=$(more --version|awk '{print $4}'|cut -d. -f1-2)
# if [[ $more_version -ge 2.38 ]]
# then
#     alias l='ls -lF|more --exit-on-eof'
# else
#     alias l='ls -lF|more'
# fi

# l now supports multiple dirs and colors.
# -iX -E should make less behave like more.
# The "$*" was definitely wrong.
function l() {
    ls --color=always -lF "$@" |less -iX -E -R
}

alias laR="ls -alR"
alias ll="ls -alF |more"
# Trying without -L again to keep symlinks.
alias lsl="ls -lF"
alias man="LC_ALL=C man"
alias mv='mv -i'

# I've come to like this mycal alias quite a lot. Turns out Cygwin
# doesn't have ncal but it does have cal with almost the same output
# with almost the same options. ncal output is a tiny little bit
# fancier.
# 2023-08-29 - Added Msys too.
# 2025-07-08 - Removed check for OS. Use ncal if found, use cal if no
# ncal, do nothing if neither is found.
# To see week 53 displayed, try mycal 1 2021.
cal_path=`whence cal`
ncal_path=`whence ncal`

# Found ncal
if [[ -n "$ncal_path" && -x "$ncal_path" ]] ; then
    # -b for oldstyle format.
    # -w for week numbers.
    # -3 for three months side by side.
    alias mycal='ncal -b -w -3'
elif [[ -n "$cal_path" && -x "$cal_path" ]] ; then
    # -m for weeks start on Monday and week numbers are as ISO8601.
    # -w for week numbers.
    # -3 for three months side by side.
    alias mycal='cal -m -w -3'
fi
unset cal_path
unset ncal_path

alias q='quota -v'
alias rs='eval `resize`'
alias rm='rm -i'
alias sr='screen -daARR'
alias uzip='unzip -q'

for i in /etc/aliases.zsh ~/.aliases.zsh
do
        if [[ -r $i ]]
        then
                . $i
        fi
done

# Process waiting functions
# Wait for one function
function pwait() {
    if [[ $# -ge 1 ]]
    then
        while [[ -d /proc/$1 ]]
        do
            sleep 30
        done
    fi
}

# Wait for many functions.
# Someone else's code, not liking the quoting here.
# Removed quotes from around $pid, kinda sensible for $@?
# Or not really, pid's a single number, no point in "$@" here.
function anywait() {
    for pid in $@
    do
        while kill -0 $pid >&/dev/null
        do
            sleep 30
        done
    done
}

###################################################################
#       Change History, left here for historical reference:
#             $Log: .zshenv,v $
#             Revision 1.53  2021/10/06 06:04:01  as
#             Summary: Added code to set different mycal alias in Cygwin or none
#             if cal or ncal is not found.
#
#             Revision 1.52  2021/03/09 14:01:37  as
#             Added alias mycal for ncal -b -w -3.
#
#             Revision 1.51  2021/02/12 07:30:49  as
#             remove_from_path no longer checks that the path to be removed exists.
#             Copy-paste issue, it shouldn't check for that.
#
#             Revision 1.50  2020/06/22 13:01:35  as
#             Summary: Updated $PAGER to less -r
#
#             Revision 1.49  2018/04/18 14:34:43  as
#             Summary: Added autoloading of insert-composed-char and binding it to F5.
#
#             Revision 1.48  2018/02/22 10:12:57  as
#             Summary: Fixed new precmd and removed old definition.
#
#             Revision 1.47  2018/02/22 10:07:08  as
#             Summary: Added precmd and preexec functions.
#
#             Revision 1.46  2017/08/18 08:28:53  as
#             Added ~/.local/bin to path if it exists.
#             Moved remove_from_path, append_path, prepend_path to the beginning
#             so they can be used for path manipulation.
#
#             Revision 1.45  2017/03/27 07:28:53  as
#             Added alias gendate to generate iso8601 date.
#             For example, 2017-03-27.
#
#             Revision 1.44  2017/02/13 11:55:53  as
#             Removed modified WORDCHARS. Instead, M-backspace doesn't
#             consider / as a WORDCHAR but ^W does.
#
#             Revision 1.43  2016/04/01 07:18:26  as
#             Set REPORTTIME to 10 so that zsh will report time data for processes
#             that run for more than 10 secs.
#
#             Revision 1.42  2015/01/21 19:26:17  as
#             Changed the sr alias to screen -daARR from screen -daAr.
#
#             Revision 1.41  2015/01/21 08:37:49  as
#             Added remove_from_path. Also functions append_path and prepend_path
#             were updated. All of these handle one path only. append_path and
#             prepend_path remove any instance of the path in question first before
#             appending or prepending. Maybe this should be configurable?
#
#             Revision 1.40  2015/01/04 13:04:24  as
#             Added functions append_path and prepend_path.
#
#             Revision 1.39  2014/11/01 17:03:32  as
#             Version check fixed.
#             ALTERNATE_EDITOR environment variable added.
#
#             Revision 1.38  2013/04/08 10:41:57  as
#             Added setopt extendedglob for, well, extended globbing. Especially "^x
#             matches anything except the pattern x" kind.
#
#             Revision 1.37  2013/02/28 15:31:25  as
#             Made URL quoting conditional.
#
#             Revision 1.36  2013/02/28 15:00:07  as
#             Added URL quoting magic.
#
#             Revision 1.35  2013/02/08 08:27:37  as
#             Call compinit on Cygwin is specially handled now.
#
#             Revision 1.34  2013/02/07 12:31:23  as
#             Added my most common aliases right here.
#
#             Revision 1.33  2011/11/23 18:48:15  as
#             Removed LESSCHARSET environment variable. It's 2011...
#
#             Revision 1.32  2011/10/13 08:30:00  as
#             Added original-awk to the possible awks, since it has the much desired
#             ** operator for power.
#             Also added e and pi as environment variables.
#
#             Revision 1.31  2011/08/16 18:54:35  as
#             Changed alias reads and removed old environment vars h, s, f.
#
#             Revision 1.30  2011/04/14 11:29:24  as
#             Fixed precmd to include terminator terminal type as well.
#
#             Revision 1.29  2010/03/27 20:57:37  as
#             Fixed lth script, now handles the case with no argument properly.
#
#             Revision 1.28  2010/03/26 11:10:02  as
#             Changed lth function so that it handles all arguments given.
#
#             Revision 1.27  2008/05/26 15:37:39  as
#             Only add ~/bin to $PATH if it exists.
#
#             Revision 1.26  2008/03/23 18:19:10  as
#             Added setopt longlistjobs, so suspending a job prints
#             the pid and job number.
#
#             Revision 1.25  2007/10/14 16:45:20  as
#             Added some base conversions, like 2hex, 2bin, 2oct, 2dec.
#
#             Revision 1.24  2006/01/10 15:12:05  as
#             Added setopt nopromptcr to avoid clearing the line before prompt.
#
#             Revision 1.23  2005/05/22 15:27:18  as
#             Added euro as an enviroment variable.
#
#             Revision 1.22  2004/09/13 20:12:41  as
#             Added sourcing of ~/.aliases.zsh.
#
#             Revision 1.21  2003/11/24 07:32:07  as
#             Adding ~/bin to $PATH fixed so that it's not added multiple times.
#
#             Revision 1.20  2003/08/04 11:59:40  as
#             Updated single letter hostname variables.
#
#             Revision 1.19  2003/07/05 21:53:03  as
#             Added path to window title.
#
#             Revision 1.18  2003/03/09 09:03:19  as
#             Added -p to mkdir in mdcd function
#
#             Revision 1.17  2002/09/20 06:59:46  as
#             No compinit autoload if Zsh version is less than four.
#
#             Revision 1.16  2002/09/19 11:04:03  as
#             Added compinit
#
#             Revision 1.15  2002/09/17 10:15:45  as
#             Moved bindkey -e before other bindkeys as apparently
#             at least in 4.0.6 the -e resets other bindings.
#
#             Revision 1.14  2002/04/15 11:09:27  as
#             Added m function
#
#             Revision 1.13  2002/04/01 11:26:36  as
#             Updated calcfunc to use gawk/nawk/mawk/awk or $(( )) if
#             no awk can be found.
#
#             Revision 1.12  2002/03/22 14:43:26  as
#             Updated calc function to work at simpukka and
#             use awk with zsh 3 and built-in arithmetic
#             in zsh 4.
#
#             Revision 1.11  2002/03/19 14:32:15  as
#             Added calcfunc and calc alias. Try calc 2* ( 3+4 ), no quotes needed!
#
#             Revision 1.10  2002/03/10 10:26:20  as
#             Changed prompt, %~ to %4~, four path components are displayed.
#
#             Revision 1.9  2002/01/29 22:10:01  as
#             lth function added
#
#             Revision 1.8  2001/05/06 21:10:08  as
#             Changed mdcd function to use pushd instead of chdir.
#
#             Revision 1.7  2001/03/09 20:01:31  as
#             ~/.zshenv.local in now sourced only if it exists.
#
#             Revision 1.6  2001/02/26 09:18:33  as
#             Removed = from WORDCHARS.
#
#             Revision 1.5  2001/02/24 15:04:20  as
#             Added bindkey -e to ensure Emacs keybindings
#
#             Revision 1.4  2001/01/22 09:54:09  as
#             $s added.
#
#             Revision 1.3  2001/01/18 07:19:21  as
#             Added command line history saving
#
#             Revision 1.2  2001/01/17 14:21:04  as
#             Prompt setting added.
#
#             Revision 1.1  2001/01/17 14:15:45  as
#             Initial revision
#
###################################################################
