1313# set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
1414# set $_Z_NO_PROMPT_COMMAND if you're handling PROMPT_COMMAND yourself.
1515# set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
16+ # set $_Z_OWNER to your username if you want use z while sudo with $HOME kept
1617#
1718# USE:
1819# * z foo # cd to most frecent dir matching foo
3031
3132 local datafile=" ${_Z_DATA:- $HOME / .z} "
3233
33- # bail if we don't own ~/.z (we're another user but our ENV is still set)
34- [ -f " $datafile " -a ! -O " $datafile " ] && return
34+ # bail if we don't own ~/.z and $_Z_OWNER not set
35+ [ -z " $_Z_OWNER " -a - f " $datafile " -a ! -O " $datafile " ] && return
3536
3637 # add entries
3738 if [ " $1 " = " --add" ]; then
@@ -40,10 +41,10 @@ _z() {
4041 # $HOME isn't worth matching
4142 [ " $* " = " $HOME " ] && return
4243
43- # don't track excluded dirs
44+ # don't track excluded directory trees
4445 local exclude
4546 for exclude in " ${_Z_EXCLUDE_DIRS[@]} " ; do
46- [ " $* " = " $exclude " ] && return
47+ case " $* " in " $exclude * " ) return ;; esac
4748 done
4849
4950 # maintain the data file
6869 count += $2
6970 }
7071 END {
71- if( count > 6000 ) {
72+ if( count > 9000 ) {
7273 # aging
7374 for( x in rank ) print x "|" 0.99*rank[x] "|" time[x]
7475 } else for( x in rank ) print x "|" rank[x] "|" time[x]
@@ -78,42 +79,41 @@ _z() {
7879 if [ $? -ne 0 -a -f " $datafile " ]; then
7980 env rm -f " $tempfile "
8081 else
81- env mv -f " $tempfile " " $datafile " || env rm -f " $tmpfile "
82+ [ " $_Z_OWNER " ] && chown $_Z_OWNER :$( id -ng $_Z_OWNER ) " $tempfile "
83+ env mv -f " $tempfile " " $datafile " || env rm -f " $tempfile "
8284 fi
8385
8486 # tab completion
85- elif [ " $1 " = " --complete" ]; then
87+ elif [ " $1 " = " --complete" -a -s " $datafile " ]; then
8688 while read line; do
8789 [ -d " ${line%% \| * } " ] && echo $line
8890 done < " $datafile " | awk -v q=" $2 " -F" |" '
8991 BEGIN {
9092 if( q == tolower(q) ) imatch = 1
91- split(substr(q, 3), fnd, " ")
93+ q = substr(q, 3)
94+ gsub(" ", ".*", q)
9295 }
9396 {
9497 if( imatch ) {
95- for( x in fnd ) tolower($1) !~ tolower(fnd[x]) && $1 = ""
96- } else {
97- for( x in fnd ) $1 !~ fnd[x] && $1 = ""
98- }
99- if( $1 ) print $1
98+ if( tolower($1) ~ tolower(q) ) print $1
99+ } else if( $1 ~ q ) print $1
100100 }
101101 ' 2> /dev/null
102102
103103 else
104104 # list/go
105105 while [ " $1 " ]; do case " $1 " in
106- --) while [ " $1 " ]; do shift ; local fnd=" $fnd $1 " ; done ;;
106+ --) while [ " $1 " ]; do shift ; local fnd=" $fnd ${fnd : + } $1 " ; done ;;
107107 -* ) local opt=${1: 1} ; while [ " $opt " ]; do case ${opt: 0: 1} in
108108 c) local fnd=" ^$PWD $fnd " ;;
109109 h) echo " ${_Z_CMD:- z} [-chlrtx] args" >&2 ; return ;;
110- x) sed -i " \:^${PWD} |.*:d" " $datafile " ;;
110+ x) sed -i -e " \:^${PWD} |.*:d" " $datafile " ;;
111111 l) local list=1;;
112112 r) local typ=" rank" ;;
113113 t) local typ=" recent" ;;
114114 esac ; opt=${opt: 1} ; done ;;
115- * ) local fnd=" $fnd $1 " ;;
116- esac ; local last=$1 ; shift ; done
115+ * ) local fnd=" $fnd ${fnd : + } $1 " ;;
116+ esac ; local last=$1 ; [ " $# " -gt 0 ] && shift ; done
117117 [ " $fnd " -a " $fnd " != " ^$PWD " ] || local list=1
118118
119119 # if we hit enter on a completion just go there
@@ -163,22 +163,23 @@ _z() {
163163 # use a copy to escape special characters, as we want to return
164164 # the original. yeah, this escaping is awful.
165165 clean_short = short
166- gsub(/[\(\)\[\]\|]/, "\\\\&", clean_short)
166+ gsub(/\ [\(\)\[\]\|\ ]/, "\\\\&", clean_short)
167167 for( x in matches ) if( matches[x] && x !~ clean_short ) return
168168 return short
169169 }
170- BEGIN { split(q, words, " "); hi_rank = ihi_rank = -9999999999 }
170+ BEGIN {
171+ gsub(" ", ".*", q)
172+ hi_rank = ihi_rank = -9999999999
173+ }
171174 {
172175 if( typ == "rank" ) {
173176 rank = $2
174177 } else if( typ == "recent" ) {
175178 rank = $3 - t
176179 } else rank = frecent($2, $3)
177- matches[$1] = imatches[$1] = rank
178- for( x in words ) {
179- if( $1 !~ words[x] ) delete matches[$1]
180- if( tolower($1) !~ tolower(words[x]) ) delete imatches[$1]
181- }
180+ if( $1 ~ q ) {
181+ matches[$1] = rank
182+ } else if( tolower($1) ~ tolower(q) ) imatches[$1] = rank
182183 if( matches[$1] && matches[$1] > hi_rank ) {
183184 best_match = $1
184185 hi_rank = matches[$1]
@@ -219,7 +220,7 @@ if compctl >/dev/null 2>&1; then
219220 }
220221 fi
221222 [[ -n " ${precmd_functions[(r)_z_precmd]} " ]] || {
222- precmd_functions+=( _z_precmd)
223+ precmd_functions[ $(( $# precmd_functions + 1 )) ]= _z_precmd
223224 }
224225 }
225226 _z_zsh_tab_completion () {
@@ -236,7 +237,7 @@ elif complete >/dev/null 2>&1; then
236237 [ " $_Z_NO_PROMPT_COMMAND " ] || {
237238 # populate directory list. avoid clobbering other PROMPT_COMMANDs.
238239 grep " _z --add" <<< " $PROMPT_COMMAND" > /dev/null || {
239- PROMPT_COMMAND=" $PROMPT_COMMAND " $' \n ' ' _z --add "$(pwd ' $_Z_RESOLVE_SYMLINKS ' 2>/dev/null)" 2>/dev/null;'
240+ PROMPT_COMMAND=" $PROMPT_COMMAND " $' \n ' ' _z --add "$(command pwd ' $_Z_RESOLVE_SYMLINKS ' 2>/dev/null)" 2>/dev/null;'
240241 }
241242 }
242- fi
243+ fi
0 commit comments