diff --git a/fn/-z4h-base64-decode b/fn/-z4h-base64-decode new file mode 100644 index 0000000..aa4d4bd --- /dev/null +++ b/fn/-z4h-base64-decode @@ -0,0 +1,38 @@ +#!/usr/bin/env zsh + +eval "$_z4h_opt" + +setopt cbases + +typeset -g REPLY= + +[[ -z $1 ]] && return +(( $#1 % 4 == 0 )) || return 1 + +local table='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +local c1 c2 c3 c4 res +local -i16 b1 b2 b3 b4 + +for c1 c2 c3 c4 in ${(s::)1//=/A}; do + (( + b1 = $table[(ie)$c1] - 1, + b2 = $table[(ie)$c2] - 1, + b3 = $table[(ie)$c3] - 1, + b4 = $table[(ie)$c4] - 1, + b1 = b1 << 2 | b2 >> 4, + b2 = (b2 << 4) & 255 | b3 >> 2, + b3 = (b3 << 6) & 255 | b4 + )) + res+=($b1 $b2 $b3) +done + +if [[ $1 == *= ]]; then + if [[ $1 == *== ]]; then + res[-2,-1]=() + else + res[-1]=() + fi +fi + +printf -v REPLY ${(j:\:)res#0} diff --git a/fn/-z4h-direnv-hook b/fn/-z4h-direnv-hook index cd4780a..518c263 100644 --- a/fn/-z4h-direnv-hook +++ b/fn/-z4h-direnv-hook @@ -1,5 +1,4 @@ #!/usr/bin/env zsh - eval "$_z4h_opt" unset _z4h_direnv_err @@ -14,33 +13,49 @@ else fi fi -# { print -n '\x1f\x8b\x08\x00\x00\x00\x00\x00'; base64 -d <<<${${DIRENV_WATCHES//-/+}//_//} } | zcat 2>/dev/null - -# zcat -# gzcat -# uncompress -c -# gunzip -c -# gzip -cd - -local sig +local -a deps local envrc=(./(../)#.envrc(NY1:a)) + if (( $#envrc )); then - local -a deps=( + deps+=( ${XDG_DATA_HOME:-~/.local/share}/direnv/allow - ${XDG_CONFIG_HOME:-~/.config}/{direnv.toml,config.toml}) - local -a stat - local files=($^deps(N)) - local non_files=(${deps:|files}) - if zstat -A stat +mtime -- $envrc $files 2>/dev/null; then - local sig=$envrc$'\0'${(pj:\0:)stat} - else - local sig=stat-error - fi -elif [[ ! -v DIRENV_WATCHES ]]; then + ${XDG_CONFIG_HOME:-~/.config}/{,direnv/}{direnv.toml,config.toml,direnvrc}) +fi + +if [[ -v DIRENV_WATCHES ]]; then + () { + local REPLY json + -z4h-base64-decode ${${DIRENV_WATCHES//-/+}//_//} || return + json=$(printf '\x1f\x8b\x08\x00\x00\x00\x00\x00%s' $REPLY | -z4h-zcat 2>/dev/null) #|| return + [[ $json == '['*']' ]] || return + # TODO: extract $(jq '.[] | .Path') from $json and append it to deps. + + local -i i=1; + local pattern=${:-\"Path\":\"([^\"\\\\]|\\\\?)#\"} + local pathmatch; + pathmatch=${(*MSI:$i:)json##${~pattern}} + while [[ -n $pathmatch ]]; do + deps+=${(Q)pathmatch##(*):} + pathmatch=${(*MSI:$((++i)):)json##${~pattern}} + done + + # TODO: cache those elements with $DIRENV_WATCHES as the key. + } + # TODO: handle errors from the above intelligently. +fi + +if (( $#envrc + $#deps == 0 )); then typeset -g _z4h_direnv_sig=none return +fi + +local -a stat +local files=($^deps(N)) +local non_files=(${deps:|files}) +if zstat -A stat +mtime -- $envrc $files 2>/dev/null; then + local sig=$envrc$'\0'${(pj:\0:)stat} else - local sig=none + local sig=stat-error fi [[ $sig == ${_z4h_direnv_sig-} ]] && return diff --git a/fn/-z4h-json-parse b/fn/-z4h-json-parse new file mode 100644 index 0000000..5569a42 --- /dev/null +++ b/fn/-z4h-json-parse @@ -0,0 +1,85 @@ +#!/usr/bin/env zsh +# Redneck json parsing. Yields correct results for any well-formed json document. +# Produces random garbage for invalid json. + +local data +local -a params + +data=$1; shift; params=( $@ ); + +data="${${data//$'\r'}##[[:space:]]#}" +[[ $data == ('{'|'[')* ]] || return +if [[ $data == '['*']' ]] +then + local arrayitem='' + local -a arraycontents + local -i depth=1 + data[1]= + while (( depth > 0 )) + do + data=${data##[[:space:]]#} + [[ -n $data ]] || return + case $data[1] in + '{'|'[') arrayitem+=$data[1]; data[1]=; (( ++depth )) ;; + '}'|']') arrayitem+=$data[1]; data[1]= + (( --depth == 1 )) && { arraycontents+=($arrayitem); arrayitem='' } ;; + ',') (( depth == 1 )) || arrayitem+=$data[1]; + data[1]= ;; + ':') arrayitem+=$data[1] data[1]= ;; + '"'|[[:alnum:].]) + if [[ $data[1] == '"' ]]; then + local tail=${data##\"([^\"\\]|\\?)#\"} + else + local tail=${data##[[:alnum:].]#} + fi + [[ $tail == [[:space:][:punct:]]* ]] || return + arrayitem+=${data::-$#tail}; data=${tail} + ;; + *) return 1 ;; + esac + done + (( ${#data##[[:space:]]#} == 0 )) || return + echo $arraycontents + +elif [[ $data == '{'*'}' ]] +then + local field + local -A found + local -i depth=1 + data[1]= + while (( depth > 0 )) + do + data=${data##[[:space:]]#} + [[ -n $data ]] || return + case $data[1] in + '{'|'[') data[1]=; (( ++depth )) ;; + '}'|']') data[1]=; (( --depth > 0 )) || return ;; + ':') data[1]= ;; + ',') data[1]=; field= ;; + '"'|[[:alnum:].]) + if [[ $data[1] == '"' ]]; then + local tail=${data##\"([^\"\\]|\\?)#\"} + else + local tail=${data##[[:alnum:].]#} + fi + [[ $tail == [[:space:][:punct:]]* ]] || return + local s=${data::-$#tail} + data=${tail} + (( depth == 1 )) || continue + if [[ -z $field ]]; then + field=${s:-x} + elif [[ $field:*params ]] + then + (( ! $+found[$field] )) || return + [[ -n $s ]] || return + [[ $s != *($'\n'|'\')* ]] || return + found[$field]=$s + (( $#found == $#params )) && break + fi + ;; + *) return 1 ;; + esac + done + echo ${(Q)found} +fi + diff --git a/fn/-z4h-zcat b/fn/-z4h-zcat new file mode 100644 index 0000000..106bd5e --- /dev/null +++ b/fn/-z4h-zcat @@ -0,0 +1,11 @@ +#!/usr/bin/env zsh + +eval "$_z4h_opt" + +local cmd +for cmd in zcat gzcat 'uncompress -c' 'gunzip -c' 'gzip -cd'; do + local words=($=cmd) + [[ -v commands[$words[1]] ]] || continue + $words || return + break +done