dian@lamia ~/work/zsh cd ..2 dian@lamia ~ cd ..123 dian@lamia / cd ~/..2/home/dian/work/../work/zsh dian@lamia ~/work/zsh cd ..2/work/zsh dian@lamia ~/work/zsh I added a minimal test case and so far it seems to do what I expect, but I don't know if there are any side effects to worry about. diff --git a/Src/builtin.c b/Src/builtin.c index d5a874a95..3a13f19ee 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -1353,8 +1353,12 @@ fixdir(char *src) *dest = '\0'; return chasedots; } - if (src[0] == '.' && src[1] == '.' && - (src[2] == '\0' || src[2] == '/')) { + if (src[0] == '.' && src[1] == '.') { + int backtracks = src[2] == '\0' || src[2] == '/' ? 1 : atoi(src + 2); + + if (backtracks < 1) + backtracks = 1; + if (isset(CHASEDOTS) || chasedots > 1) { chasedots = 1; /* and treat as normal path segment */ @@ -1375,11 +1379,16 @@ fixdir(char *src) *ptrd = '\0'; return 1; } - for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); - if (dest[-1] != '/') - dest--; + for (int i = 0; i < backtracks; i++) { + for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); + if (dest[-1] != '/') + dest--; + if (dest - 1 == d0) + break; + } } - src++; + /* jump forward one space plus the length of any trailing digits */ + src += backtracks == 1 ? 1 : (int) trunc(log10(backtracks)) + 2; while (*++src == '/'); continue; } diff --git a/Test/B01cd.ztst b/Test/B01cd.ztst index 21e751dcb..f7f82147f 100644 --- a/Test/B01cd.ztst +++ b/Test/B01cd.ztst @@ -123,6 +123,14 @@ F:something is broken. But you already knew that. print $PWD 0q:Changing directory up through symbolic links without following them >$mydir +>$mydir + + cd $mydir/cdtst.tmp/sub/fake + cd ..3 && + pwd && + print $PWD +0q:Changing directory up without following symlinks, shorter form +>$mydir >$mydir setopt chaselinks
On Sun, May 10, 2020 at 6:36 AM Dian M Fay <dian.m.fay@gmail.com> wrote:
>
> dian@lamia ~/work/zsh cd ..2
> dian@lamia ~ cd ..123
> dian@lamia /
`..2` is a valid directory name. So it's a breaking change.
~% mkdir ..2
~% cd ..2
~/..2%
Roman.
Of course. Actually encountering a directory named like that does seem
unlikely, but unlikely probably doesn't cut it. It could stat beforehand
but then the ..n behavior becomes less consistent.
Would making this an option ('BACKTRACK_N' or something) be appropriate?
On Sun May 10, 2020 at 6:43 AM, Roman Perepelitsa wrote:
> On Sun, May 10, 2020 at 6:36 AM Dian M Fay <dian.m.fay@gmail.com> wrote:
> >
> > dian@lamia ~/work/zsh cd ..2
> > dian@lamia ~ cd ..123
> > dian@lamia /
>
>
> `..2` is a valid directory name. So it's a breaking change.
>
>
> ~% mkdir ..2
> ~% cd ..2
> ~/..2%
>
>
> Roman.
On Sat, May 9, 2020 at 9:44 PM Roman Perepelitsa
<roman.perepelitsa@gmail.com> wrote:
>
> On Sun, May 10, 2020 at 6:36 AM Dian M Fay <dian.m.fay@gmail.com> wrote:
> >
> > dian@lamia ~/work/zsh cd ..2
> > dian@lamia ~ cd ..123
> > dian@lamia /
>
> `..2` is a valid directory name. So it's a breaking change.
It can also be implemented as a shell function wrapper around "cd", so
it's not necessary to modify the C code.
Here's the basics, minus necessary handling for other cd options.
cd() {
emulate -L zsh -o extendedglob -o histsubstpattern
while [[ $1 = (#b)((#s)(|*/)..(<->)(/*|)(#e)) ]] do
local dots=${(j:/:)${:-{1..${match[3]}}}//<->/..}
set -- ${1:s,${~match[1]},${match[2]}${dots}${match[4]}}
done
echo builtin cd $*
}
On Sat, May 9, 2020 at 10:58 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> echo builtin cd $*
Oops, forgot to remove the "echo" from my test case. But it's not
really a finished function anyway.
On 5/10/20 1:28 AM, Dian M Fay wrote:
> Of course. Actually encountering a directory named like that does seem
> unlikely, but unlikely probably doesn't cut it. It could stat beforehand
> but then the ..n behavior becomes less consistent.
>
> Would making this an option ('BACKTRACK_N' or something) be appropriate?
>
> On Sun May 10, 2020 at 6:43 AM, Roman Perepelitsa wrote:
>> On Sun, May 10, 2020 at 6:36 AM Dian M Fay <dian.m.fay@gmail.com> wrote:
>>>
>>> dian@lamia ~/work/zsh cd ..2
>>> dian@lamia ~ cd ..123
>>> dian@lamia /
>>
>>
>> `..2` is a valid directory name. So it's a breaking change.
>>
>>
>> ~% mkdir ..2
>> ~% cd ..2
>> ~/..2%
>>
>>
>> Roman.
I personally think a wrapper function is more appropriate.
On 5/10/20 3:06 AM, Eric Cook wrote:
> I personally think a wrapper function is more appropriate.
One not distributed with zsh itself that is.
Eric Cook wrote on Sun, 10 May 2020 03:09 -0400:
> On 5/10/20 3:06 AM, Eric Cook wrote:
> > I personally think a wrapper function is more appropriate.
>
> One not distributed with zsh itself that is.
Or a shell widget, like Mikael's rationalise-dot widget that lets one
type «cd ...» to get «cd ../..» and so on. This also has the benefit
of not requiring completion functions to be taught about the new syntax.
I don't have his latest version handy, but I use a modified version:
_rationalise-dot2 ()
{
if [[ $LBUFFER == *( ) ]]
then
zle .self-insert
return
fi
local -a tokens; tokens=(${(z)LBUFFER})
if [[ $tokens[-1] == (../|*/../) ]]
then
LBUFFER+=../
elif [[ $tokens[-1] == '.' ]]
then
LBUFFER+=./
else
zle .self-insert
fi
}
zle -N _rationalise-dot2
bindkey . _rationalise-dot2
bindkey -M isearch . self-insert
The zle calls inside the function don't pass argv through, but that
doesn't matter in practice.
Tweak to taste.
Thanks all! I'll check out some of the alternatives.
On Sat May 9, 2020 at 11:28 PM, Bart Schaefer wrote:
> On Sat, May 9, 2020 at 10:58 PM Bart Schaefer
> <schaefer@brasslantern.com> wrote:
> >
> > echo builtin cd $*
>
>
> Oops, forgot to remove the "echo" from my test case. But it's not
> really a finished function anyway.