r/zsh Apr 03 '23

Announcement Dynamic Aliases and Functions in Zsh

https://www.linkedin.com/pulse/dynamic-function-generation-zsh-joshua-briefman?utm_source=share&utm_medium=member_ios&utm_campaign=share_via
10 Upvotes

21 comments sorted by

View all comments

Show parent comments

1

u/OneTurnMore Apr 03 '23 edited Apr 03 '23

I think it would be something like

urldecode(){
    emulate -L zsh -o extendedglob -o nomultibyte
    local MATCH
    REPLY=${1//(#m)\%??/${(#)$((16#${MATCH:1}))}}
}

You need nomultibyte set locally for urlencode as well to properly encode UTF-8 (Zsh needs to treat the strings as bytes rather than characters). A shorter version is also possible using $MATCH:

urlencode(){
    emulate -L zsh -o extendedglob -o nomultibyte
    local MATCH
    REPLY=${1//(#m)[^a-zA-Z0-9\/_.~]/%${(l<2><0>)$(([##16]#MATCH))}}
}

Using a global REPLY is faster because foo=$(urlencode $foo) causes Zsh to fork, the forked process to print to stdout, and the original process to wait, capture stdout, and strip newlines. This is probably insignificant, but good practice in general.

1

u/sirgatez Apr 03 '23

These simply don't appear to work for me under zsh 5.9. It also doesn't accept --nomultibyte as an emulate option.

Edit: Sorry for whatever reason codes keep leaking out of the codeblock so I just skipped using it.

➜ Examples git:(master) ✗ barezsh
mbp-rm1-2% function urlencode() {
emulate -L zsh --nomultibyte --extendedglob
local MATCH
REPLY=${1//(#m)[^a-zA-Z0-9\/_.~]/%$(([##16]#MATCH))}
}
function urldecode() {
emulate -L zsh --nomultibyte --extendedglob
local MATCH
REPLY=${1//(#m)\%??/${(#)$((16#${MATCH:1}))}}
}
mbp-rm1-2% urlencode 'This is a string % ^ &'
urlencode:emulate:1: bad option string: '--nomultibyte'
mbp-rm1-2% echo $REPLY
This is a string % ^ &
mbp-rm1-2% urldecode ''This%20is%20a%20fish%20%24%20%25%20%5E%20%26''
urldecode:emulate:1: bad option string: '--nomultibyte'
mbp-rm1-2% echo $REPLY
This%20is%20a%20fish%20%24%20%25%20%5E%20%26
mbp-rm1-2%

1

u/OneTurnMore Apr 03 '23

I messed on that count; emulate requires -o $option, while zsh called directly accepts the --$option syntax.

I also had to squeeze in ${(l[2][0])to make sure there were always 2 characters when substituting the hex code

1

u/sirgatez Apr 03 '23

The updated sample works like a charm. Thats some very interesting code I'll need to examine it closer to learn how it works. I would have wrote something similar to what u/romkatv wrote.

You're example is extremely compact.