]> Sergey Matveev's repositories - zsh-autoenv.git/blob - autoenv.zsh
Usage examples
[zsh-autoenv.git] / autoenv.zsh
1 : ${AUTOENV_AUTH_FILE:=~/.env_auth}
2 : ${AUTOENV_FILE_ENTER:=.autoenv.zsh}
3 : ${AUTOENV_FILE_LEAVE:=.autoenv_leave.zsh}
4 : ${AUTOENV_LOOK_UPWARDS:=1}
5 : ${AUTOENV_HANDLE_LEAVE:=1}
6 : ${AUTOENV_DISABLED:=0}
7
8 typeset -a _autoenv_stack_entered
9 typeset -A _autoenv_stack_entered_ctime
10 typeset -a _autoenv_ctime
11
12 zmodload -F zsh/stat b:zstat
13 _autoenv_get_file_ctime() {
14     zstat -a _autoenv_ctime +ctime $1 2>/dev/null || _autoenv_ctime=(0)
15 }
16
17 _autoenv_stack_entered_remove() {
18     local env_file=$1
19     _autoenv_stack_entered[$_autoenv_stack_entered[(i)$env_file]]=()
20     _autoenv_stack_entered_ctime[$env_file]=
21 }
22
23 _autoenv_stack_entered_add() {
24     local env_file=$1
25     _autoenv_stack_entered_remove $env_file
26     _autoenv_stack_entered+=($env_file)
27     _autoenv_get_file_ctime $env_file
28     _autoenv_stack_entered_ctime[$env_file]=${_autoenv_ctime[1]}
29 }
30
31 _autoenv_stack_entered_contains() {
32     local env_file=$1
33     local f i
34     if (( ${+_autoenv_stack_entered[(r)${env_file}]} )); then
35         f=$env_file
36     else
37         local env_file_abs=${env_file:A}
38         for i ($_autoenv_stack_entered) {
39             [[ ${i:A} == ${env_file_abs} ]] && {
40                 f=$i
41                 break
42             }
43         }
44     fi
45     [[ -n $f ]] || return 1
46     _autoenv_get_file_ctime $f
47     [[ ${_autoenv_stack_entered_ctime[$f]} == ${_autoenv_ctime[1]} ]] || return 1
48 }
49
50 _autoenv_hash_pair() {
51     local env_file=${1:A}
52     env_cksum=${${:-$(cksum "$env_file")}[1]}
53     ret_pair=":${env_file}:${env_cksum}"
54 }
55
56 _autoenv_authorized_env_file() {
57     local env_file=$1
58     local env_file_abs=${env_file:A}
59     local ret_pair
60     local -a lines
61     [[ -s $AUTOENV_AUTH_FILE ]] &&
62         lines=( ${(M)"${(f@)"$(< $AUTOENV_AUTH_FILE)"}":#:$env_file_abs:*} )
63     [[ -z $lines ]] && return 1
64     line=${lines[-1]}
65     _autoenv_hash_pair $env_file
66     [[ $line == $ret_pair ]] || return 1
67 }
68
69 _autoenv_authorize() {
70     local env_file=${1:A}
71     _autoenv_deauthorize $env_file
72     [[ -d ${AUTOENV_AUTH_FILE:h} ]] || mkdir -p ${AUTOENV_AUTH_FILE:h}
73     local ret_pair
74     _autoenv_hash_pair $env_file && print "$ret_pair" >>| $AUTOENV_AUTH_FILE
75 }
76
77 _autoenv_deauthorize() {
78     local env_file=${1:A}
79     [[ -s $AUTOENV_AUTH_FILE ]] || return
80     perl -i -ne "print unless m[:${env_file}:]" $AUTOENV_AUTH_FILE
81 }
82
83 _autoenv_check_authorized_env_file() {
84     local env_file=$1
85     [[ -f $env_file ]] || return 1
86     _autoenv_authorized_env_file $1 && return
87     command ls -l $1 >&2
88     local answer
89     setopt localtraps
90     trap "return 1" INT
91     read -q answer\?"Authorize? "
92     print
93     [[ $answer == "y" ]] || return 1
94     _autoenv_authorize $1
95 }
96
97 _autoenv_source() {
98     local autoenv_env_file=$1
99     local autoenv_event=$2
100     print "autoenv: $autoenv_env_file" >&2
101     (( $+functions[autostash] )) || source ${${funcsourcetrace[1]%:*}:h}/lib/varstash
102     varstash_dir=${autoenv_env_file:h} source $autoenv_env_file
103     [[ $autoenv_event == enter ]] && _autoenv_stack_entered_add $autoenv_env_file
104 }
105
106 _autoenv_get_file_upwards() {
107     setopt EXTENDED_GLOB NO_NOMATCH
108     found=( (../)#$AUTOENV_FILE_ENTER(Y1:A) )
109     env_file=${found[1]}
110 }
111
112 _autoenv_chpwd_handler() {
113     emulate -L zsh
114     (( $AUTOENV_DISABLED )) && return
115     local env_file="$PWD/$AUTOENV_FILE_ENTER"
116     if [[ $AUTOENV_HANDLE_LEAVE == 1 ]] && (( $#_autoenv_stack_entered )); then
117         local prev_file prev_dir
118         for prev_file in ${_autoenv_stack_entered}; do
119             prev_dir=${prev_file:h}
120             if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then
121                 local env_file_leave=$prev_dir/$AUTOENV_FILE_LEAVE
122                 if _autoenv_check_authorized_env_file $env_file_leave; then
123                     varstash_dir=$prev_dir _autoenv_source $env_file_leave leave $prev_dir
124                 fi
125                 (( $+functions[autostash] )) && varstash_dir=$prev_dir autounstash
126                 _autoenv_stack_entered_remove $prev_file
127             fi
128         done
129     fi
130     if ! [[ -f $env_file ]] && [[ $AUTOENV_LOOK_UPWARDS == 1 ]]; then
131         _autoenv_get_file_upwards $PWD
132         [[ -f $env_file ]] || return
133     fi
134     _autoenv_stack_entered_contains $env_file && return
135     _autoenv_check_authorized_env_file $env_file || return
136     _autoenv_source $env_file enter
137 }
138
139 autoload -U add-zsh-hook
140 add-zsh-hook chpwd _autoenv_chpwd_handler
141 _autoenv_chpwd_handler