From abb4d740338e667991656e3ca575e623aba7bd2a Mon Sep 17 00:00:00 2001 From: Ingo Karkat Date: Sat, 7 Feb 2026 10:41:32 +0000 Subject: [PATCH] patch 9.1.2138: win_execute() and 'autochdir' can corrupt buffer name MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Problem: With 'autochdir' win_execute() can corrupt the buffer name, causing :write to use wrong path. Solution: Save and restore b_fname when 'autochdir' is active (Ingo Karkat). This is caused by a bad interaction of the 'autochdir' behavior, overriding of the current directory via :lchdir, and the temporary window switching done by win_execute(), manifesting when e.g. a custom completion inspects other buffers: 1. In the initial state after the :lcd .. we have curbuf->b_fname = "Xsubdir/file". 2. do_autochdir() is invoked, temporarily undoing the :lcd .., changing back into the Xsubdir/ subdirectory. 3. win_execute() switches windows, triggering win_enter_ext() → win_fix_current_dir() → shorten_fnames(TRUE) 4. shorten_fnames() processes *all* buffers 5. shorten_buf_fname() makes the filename relative to the current (wrong) directory; b_fname becomes "file" instead of "Xsubdir/file" 6. Directory restoration correctly restores working directory via mch_chdir() (skipping a second do_autochdir() invocation because apply_acd is FALSE), but b_fname remains corrupted, with the "Xsubdir/" part missing. 7. expand("%:p") (and commands like :write) continue to use the corrupted filename, resolving to a wrong path that's missing the "Xsubdir/" part. To fix the problem the short filename is saved if its in effect (i.e. pointed to by curbuf->b_fname) and 'autochdir' happened. It's then restored in case of a local cwd override. The conditions limit this workaround to when 'autochdir' is active *and* overridden by a :lchdir. closes: #19343 Co-authored-by: zeertzjq Signed-off-by: Ingo Karkat Signed-off-by: Christian Brabandt --- runtime/doc/version9.txt | 5 +++++ src/evalwindow.c | 16 ++++++++++++++++ src/testdir/test_cd.vim | 21 +++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 44 insertions(+) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 3b9ecffd77..843868c207 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52512,4 +52512,9 @@ Problem: Some tests are not valid on OpenBSD. Solution: Add CheckNotOpenBSD, use it to skip certain tests (Kevin Goodsell). +Patch 9.1.2138 +Problem: With 'autochdir' win_execute() can corrupt the buffer name, causing + :write to use wrong path. +Solution: Save and restore b_fname when 'autochdir' is active (Ingo Karkat). + vim:tw=78:ts=8:noet:ft=help:norl:fdm=manual:nofoldenable diff --git a/src/evalwindow.c b/src/evalwindow.c index 1bd970e16a..a08cbf717d 100644 --- a/src/evalwindow.c +++ b/src/evalwindow.c @@ -732,6 +732,7 @@ f_win_execute(typval_T *argvars, typval_T *rettv) # ifdef FEAT_AUTOCHDIR char_u autocwd[MAXPATHL]; int apply_acd = FALSE; + char_u *save_sfname = NULL; # endif // Getting and setting directory can be slow on some systems, only do @@ -754,6 +755,8 @@ f_win_execute(typval_T *argvars, typval_T *rettv) // apply 'acd' afterwards, otherwise restore the current directory. if (cwd_status == OK && p_acd) { + if (curbuf->b_sfname != NULL && curbuf->b_fname == curbuf->b_sfname) + save_sfname = vim_strsave(curbuf->b_sfname); do_autochdir(); apply_acd = mch_dirname(autocwd, MAXPATHL) == OK && STRCMP(cwd, autocwd) == 0; @@ -768,11 +771,24 @@ f_win_execute(typval_T *argvars, typval_T *rettv) restore_win_noblock(&switchwin, TRUE); # ifdef FEAT_AUTOCHDIR if (apply_acd) + { + vim_free(save_sfname); do_autochdir(); + } else # endif if (cwd_status == OK) + { mch_chdir((char *)cwd); +# ifdef FEAT_AUTOCHDIR + if (save_sfname != NULL) + { + vim_free(curbuf->b_sfname); + curbuf->b_sfname = save_sfname; + curbuf->b_fname = curbuf->b_sfname; + } +# endif + } // Update the status line if the cursor moved. if (win_valid(wp) && !EQUAL_POS(curpos, wp->w_cursor)) diff --git a/src/testdir/test_cd.vim b/src/testdir/test_cd.vim index 6248ee4844..9b74d8fdca 100644 --- a/src/testdir/test_cd.vim +++ b/src/testdir/test_cd.vim @@ -189,6 +189,27 @@ func Test_lcd_split() quit! endfunc +" Test that a temporary override of 'autochdir' via :lcd isn't clobbered by win_execute() in a split window. +func Test_lcd_win_execute() + CheckOption autochdir + + let startdir = getcwd() + call mkdir('Xsubdir', 'R') + call test_autochdir() + set autochdir + edit Xsubdir/file + call assert_match('testdir.Xsubdir.file$', expand('%:p')) + split + lcd .. + call assert_match('testdir.Xsubdir.file$', expand('%:p')) + call win_execute(win_getid(2), "") + call assert_match('testdir.Xsubdir.file$', expand('%:p')) + + set noautochdir + bwipe! + call chdir(startdir) +endfunc + func Test_cd_from_non_existing_dir() CheckNotMSWindows diff --git a/src/version.c b/src/version.c index d083cd8ffd..0a395c50ec 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2138, /**/ 2137, /**/ -- 2.47.3