: ${AUTOENV_AUTH_FILE:=~/.env_auth} : ${AUTOENV_FILE_ENTER:=.autoenv.zsh} : ${AUTOENV_FILE_LEAVE:=.autoenv_leave.zsh} : ${AUTOENV_LOOK_UPWARDS:=1} : ${AUTOENV_HANDLE_LEAVE:=1} : ${AUTOENV_DISABLED:=0} typeset -a _autoenv_stack_entered typeset -A _autoenv_stack_entered_ctime typeset -a _autoenv_ctime zmodload -F zsh/stat b:zstat _autoenv_get_file_ctime() { zstat -a _autoenv_ctime +ctime $1 2>/dev/null || _autoenv_ctime=(0) } _autoenv_stack_entered_remove() { local env_file=$1 _autoenv_stack_entered[$_autoenv_stack_entered[(i)$env_file]]=() _autoenv_stack_entered_ctime[$env_file]= } _autoenv_stack_entered_add() { local env_file=$1 _autoenv_stack_entered_remove $env_file _autoenv_stack_entered+=($env_file) _autoenv_get_file_ctime $env_file _autoenv_stack_entered_ctime[$env_file]=${_autoenv_ctime[1]} } _autoenv_stack_entered_contains() { local env_file=$1 local f i if (( ${+_autoenv_stack_entered[(r)${env_file}]} )); then f=$env_file else local env_file_abs=${env_file:A} for i ($_autoenv_stack_entered) { [[ ${i:A} == ${env_file_abs} ]] && { f=$i break } } fi [[ -n $f ]] || return 1 _autoenv_get_file_ctime $f [[ ${_autoenv_stack_entered_ctime[$f]} == ${_autoenv_ctime[1]} ]] || return 1 } _autoenv_hash_pair() { local env_file=${1:A} env_cksum=${${:-$(cksum "$env_file")}[1]} ret_pair=":${env_file}:${env_cksum}" } _autoenv_authorized_env_file() { local env_file=$1 local env_file_abs=${env_file:A} local ret_pair local -a lines [[ -s $AUTOENV_AUTH_FILE ]] && lines=( ${(M)"${(f@)"$(< $AUTOENV_AUTH_FILE)"}":#:$env_file_abs:*} ) [[ -z $lines ]] && return 1 line=${lines[-1]} _autoenv_hash_pair $env_file [[ $line == $ret_pair ]] || return 1 } _autoenv_authorize() { local env_file=${1:A} _autoenv_deauthorize $env_file [[ -d ${AUTOENV_AUTH_FILE:h} ]] || mkdir -p ${AUTOENV_AUTH_FILE:h} local ret_pair _autoenv_hash_pair $env_file && print "$ret_pair" >>| $AUTOENV_AUTH_FILE } _autoenv_deauthorize() { local env_file=${1:A} [[ -s $AUTOENV_AUTH_FILE ]] || return perl -i -ne "print unless m[:${env_file}:]" $AUTOENV_AUTH_FILE } _autoenv_check_authorized_env_file() { local env_file=$1 [[ -f $env_file ]] || return 1 _autoenv_authorized_env_file $1 && return command ls -l $1 >&2 local answer setopt localtraps trap "return 1" INT read -q answer\?"Authorize? " print [[ $answer == "y" ]] || return 1 _autoenv_authorize $1 } _autoenv_source() { local autoenv_env_file=$1 local autoenv_event=$2 print "autoenv: $autoenv_env_file" >&2 (( $+functions[autostash] )) || source ${${funcsourcetrace[1]%:*}:h}/lib/varstash varstash_dir=${autoenv_env_file:h} source $autoenv_env_file [[ $autoenv_event == enter ]] && _autoenv_stack_entered_add $autoenv_env_file } _autoenv_get_file_upwards() { setopt EXTENDED_GLOB NO_NOMATCH found=( (../)#$AUTOENV_FILE_ENTER(Y1:A) ) env_file=${found[1]} } _autoenv_chpwd_handler() { emulate -L zsh (( $AUTOENV_DISABLED )) && return local env_file="$PWD/$AUTOENV_FILE_ENTER" if [[ $AUTOENV_HANDLE_LEAVE == 1 ]] && (( $#_autoenv_stack_entered )); then local prev_file prev_dir for prev_file in ${_autoenv_stack_entered}; do prev_dir=${prev_file:h} if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then local env_file_leave=$prev_dir/$AUTOENV_FILE_LEAVE if _autoenv_check_authorized_env_file $env_file_leave; then varstash_dir=$prev_dir _autoenv_source $env_file_leave leave $prev_dir fi (( $+functions[autostash] )) && varstash_dir=$prev_dir autounstash _autoenv_stack_entered_remove $prev_file fi done fi if ! [[ -f $env_file ]] && [[ $AUTOENV_LOOK_UPWARDS == 1 ]]; then _autoenv_get_file_upwards $PWD [[ -f $env_file ]] || return fi _autoenv_stack_entered_contains $env_file && return _autoenv_check_authorized_env_file $env_file || return _autoenv_source $env_file enter } autoload -U add-zsh-hook add-zsh-hook chpwd _autoenv_chpwd_handler _autoenv_chpwd_handler