Files
neovim/src/nvim/eval/window.h
zeertzjq ed8fbd2e29 vim-patch:9.1.2138: win_execute() and 'autochdir' can corrupt buffer name (#37767)
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: vim/vim#19343

abb4d74033

Co-authored-by: Ingo Karkat <swdev@ingo-karkat.de>
2026-02-08 07:04:36 +08:00

31 lines
718 B
C

#pragma once
#include <stdbool.h>
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/os/os_defs.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
/// Structure used by switch_win() to pass values to restore_win()
typedef struct {
win_T *sw_curwin;
tabpage_T *sw_curtab;
bool sw_same_win; ///< VIsual_active was not reset
bool sw_visual_active;
} switchwin_T;
/// Structure used by win_execute_before() to pass values to win_execute_after()
typedef struct {
win_T *wp;
pos_T curpos;
char cwd[MAXPATHL];
int cwd_status;
bool apply_acd;
char *save_sfname;
switchwin_T switchwin;
} win_execute_T;
#include "eval/window.h.generated.h"