Patch 8.1.1291

7 views
Skip to first unread message

Bram Moolenaar

unread,
May 7, 2019, 4:07:34 PM5/7/19
to vim...@googlegroups.com

Patch 8.1.1291
Problem: Not easy to change directory and restore.
Solution: Add the chdir() function. (Yegappan Lakshmanan, closes #4358)
Files: runtime/doc/eval.txt, runtime/doc/todo.txt,
runtime/doc/usr_41.txt, src/evalfunc.c, src/ex_docmd.c,
src/if_py_both.h, src/proto/ex_docmd.pro, src/structs.h,
src/testdir/test_cd.vim


*** ../vim-8.1.1290/runtime/doc/eval.txt 2019-05-05 18:11:46.312590682 +0200
--- runtime/doc/eval.txt 2019-05-07 22:00:01.205510545 +0200
***************
*** 2273,2278 ****
--- 2273,2279 ----
String status of channel {handle}
changenr() Number current change number
char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
+ chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
***************
*** 3469,3474 ****
--- 3470,3496 ----
let list = map(split(str, '\zs'), {_, val -> char2nr(val)})
< Result: [65, 66, 67]

+ chdir({dir}) *chdir()*
+ Change the current working directory to {dir}. The scope of
+ the directory change depends on the directory of the current
+ window:
+ - If the current window has a window-local directory
+ (|:lcd|), then changes the window local directory.
+ - Otherwise, if the current tabpage has a local
+ directory (|:tcd|) then changes the tabpage local
+ directory.
+ - Otherwise, changes the global directory.
+ If successful, returns the previous working directory. Pass
+ this to another chdir() to restore the directory.
+ On failure, returns an empty string.
+
+ Example: >
+ let save_dir = chdir(newdir)
+ if save_dir
+ " ... do some work
+ call chdir(save_dir)
+ endif
+ <
cindent({lnum}) *cindent()*
Get the amount of indent for line {lnum} according the C
indenting rules, as with 'cindent'.
*** ../vim-8.1.1290/runtime/doc/usr_41.txt 2019-05-05 18:11:46.328590595 +0200
--- runtime/doc/usr_41.txt 2019-05-07 21:56:20.282699661 +0200
***************
*** 769,774 ****
--- 769,775 ----
haslocaldir() check if current window used |:lcd| or |:tcd|
tempname() get the name of a temporary file
mkdir() create a new directory
+ chdir() change current working directory
delete() delete a file
rename() rename a file
system() get the result of a shell command as a string
*** ../vim-8.1.1290/src/evalfunc.c 2019-05-07 16:28:08.177289442 +0200
--- src/evalfunc.c 2019-05-07 21:56:20.286699640 +0200
***************
*** 107,112 ****
--- 107,113 ----
#endif
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
+ static void f_chdir(typval_T *argvars, typval_T *rettv);
static void f_cindent(typval_T *argvars, typval_T *rettv);
static void f_clearmatches(typval_T *argvars, typval_T *rettv);
static void f_col(typval_T *argvars, typval_T *rettv);
***************
*** 597,602 ****
--- 598,604 ----
#endif
{"changenr", 0, 0, f_changenr},
{"char2nr", 1, 2, f_char2nr},
+ {"chdir", 1, 1, f_chdir},
{"cindent", 1, 1, f_cindent},
{"clearmatches", 0, 1, f_clearmatches},
{"col", 1, 1, f_col},
***************
*** 2491,2496 ****
--- 2493,2537 ----
}

/*
+ * "chdir(dir)" function
+ */
+ static void
+ f_chdir(typval_T *argvars, typval_T *rettv)
+ {
+ char_u *cwd;
+ cdscope_T scope = CDSCOPE_GLOBAL;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ if (argvars[0].v_type != VAR_STRING)
+ return;
+
+ // Return the current directory
+ cwd = alloc(MAXPATHL);
+ if (cwd != NULL)
+ {
+ if (mch_dirname(cwd, MAXPATHL) != FAIL)
+ {
+ #ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(cwd);
+ #endif
+ rettv->vval.v_string = vim_strsave(cwd);
+ }
+ vim_free(cwd);
+ }
+
+ if (curwin->w_localdir != NULL)
+ scope = CDSCOPE_WINDOW;
+ else if (curtab->tp_localdir != NULL)
+ scope = CDSCOPE_TABPAGE;
+
+ if (!changedir_func(argvars[0].vval.v_string, TRUE, scope))
+ // Directory change failed
+ VIM_CLEAR(rettv->vval.v_string);
+ }
+
+ /*
* "cindent(lnum)" function
*/
static void
*** ../vim-8.1.1290/src/ex_docmd.c 2019-05-04 15:05:24.927269310 +0200
--- src/ex_docmd.c 2019-05-07 21:56:20.286699640 +0200
***************
*** 7513,7529 ****

/*
* Deal with the side effects of changing the current directory.
! * When "tablocal" is TRUE then this was after an ":tcd" command.
! * When "winlocal" is TRUE then this was after an ":lcd" command.
*/
void
! post_chdir(int tablocal, int winlocal)
{
! if (!winlocal)
// Clear tab local directory for both :cd and :tcd
VIM_CLEAR(curtab->tp_localdir);
VIM_CLEAR(curwin->w_localdir);
! if (winlocal || tablocal)
{
/* If still in global directory, need to remember current
* directory as global directory. */
--- 7513,7529 ----

/*
* Deal with the side effects of changing the current directory.
! * When 'scope' is CDSCOPE_TABPAGE then this was after an ":tcd" command.
! * When 'scope' is CDSCOPE_WINDOW then this was after an ":lcd" command.
*/
void
! post_chdir(cdscope_T scope)
{
! if (scope != CDSCOPE_WINDOW)
// Clear tab local directory for both :cd and :tcd
VIM_CLEAR(curtab->tp_localdir);
VIM_CLEAR(curwin->w_localdir);
! if (scope != CDSCOPE_GLOBAL)
{
/* If still in global directory, need to remember current
* directory as global directory. */
***************
*** 7532,7538 ****
/* Remember this local directory for the window. */
if (mch_dirname(NameBuff, MAXPATHL) == OK)
{
! if (tablocal)
curtab->tp_localdir = vim_strsave(NameBuff);
else
curwin->w_localdir = vim_strsave(NameBuff);
--- 7532,7538 ----
/* Remember this local directory for the window. */
if (mch_dirname(NameBuff, MAXPATHL) == OK)
{
! if (scope == CDSCOPE_TABPAGE)
curtab->tp_localdir = vim_strsave(NameBuff);
else
curwin->w_localdir = vim_strsave(NameBuff);
***************
*** 7548,7649 ****
shorten_fnames(TRUE);
}

-
/*
! * ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
*/
! void
! ex_cd(exarg_T *eap)
{
- char_u *new_dir;
char_u *tofree;
int dir_differs;

! new_dir = eap->arg;
! #if !defined(UNIX) && !defined(VMS)
! /* for non-UNIX ":cd" means: print current directory */
! if (*new_dir == NUL)
! ex_pwd(NULL);
! else
! #endif
{
! if (allbuf_locked())
! return;
! if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged()
! && !eap->forceit)
! {
! emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
! return;
! }

! /* ":cd -": Change to previous directory */
! if (STRCMP(new_dir, "-") == 0)
{
! if (prev_dir == NULL)
! {
! emsg(_("E186: No previous directory"));
! return;
! }
! new_dir = prev_dir;
}

! /* Save current directory for next ":cd -" */
! tofree = prev_dir;
! if (mch_dirname(NameBuff, MAXPATHL) == OK)
! prev_dir = vim_strsave(NameBuff);
! else
! prev_dir = NULL;

#if defined(UNIX) || defined(VMS)
! /* for UNIX ":cd" means: go to home directory */
! if (*new_dir == NUL)
! {
! /* use NameBuff for home directory name */
# ifdef VMS
! char_u *p;

! p = mch_getenv((char_u *)"SYS$LOGIN");
! if (p == NULL || *p == NUL) /* empty is the same as not set */
! NameBuff[0] = NUL;
! else
! vim_strncpy(NameBuff, p, MAXPATHL - 1);
# else
! expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
# endif
! new_dir = NameBuff;
! }
#endif
! dir_differs = new_dir == NULL || prev_dir == NULL
! || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0;
! if (new_dir == NULL || (dir_differs && vim_chdir(new_dir)))
! emsg(_(e_failed));
! else
{
! char_u *acmd_fname;
! int is_winlocal_chdir = eap->cmdidx == CMD_lcd
! || eap->cmdidx == CMD_lchdir;
! int is_tablocal_chdir = eap->cmdidx == CMD_tcd
! || eap->cmdidx == CMD_tchdir;

! post_chdir(is_tablocal_chdir, is_winlocal_chdir);

! /* Echo the new current directory if the command was typed. */
if (KeyTyped || p_verbose >= 5)
ex_pwd(eap);
-
- if (dir_differs)
- {
- if (is_winlocal_chdir)
- acmd_fname = (char_u *)"window";
- else if (is_tablocal_chdir)
- acmd_fname = (char_u *)"tabpage";
- else
- acmd_fname = (char_u *)"global";
- apply_autocmds(EVENT_DIRCHANGED, acmd_fname,
- new_dir, FALSE, curbuf);
- }
}
- vim_free(tofree);
}
}

--- 7548,7673 ----
shorten_fnames(TRUE);
}

/*
! * Change directory function used by :cd/:tcd/:lcd Ex commands and the
! * chdir() function. If 'winlocaldir' is TRUE, then changes the window-local
! * directory. If 'tablocaldir' is TRUE, then changes the tab-local directory.
! * Otherwise changes the global directory.
! * Returns TRUE if the directory is successfully changed.
*/
! int
! changedir_func(
! char_u *new_dir,
! int forceit,
! cdscope_T scope)
{
char_u *tofree;
int dir_differs;
+ int retval = FALSE;

! if (allbuf_locked())
! return FALSE;
!
! if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() && !forceit)
{
! emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
! return FALSE;
! }

! // ":cd -": Change to previous directory
! if (STRCMP(new_dir, "-") == 0)
! {
! if (prev_dir == NULL)
{
! emsg(_("E186: No previous directory"));
! return FALSE;
}
+ new_dir = prev_dir;
+ }

! // Save current directory for next ":cd -"
! tofree = prev_dir;
! if (mch_dirname(NameBuff, MAXPATHL) == OK)
! prev_dir = vim_strsave(NameBuff);
! else
! prev_dir = NULL;

#if defined(UNIX) || defined(VMS)
! // for UNIX ":cd" means: go to home directory
! if (*new_dir == NUL)
! {
! // use NameBuff for home directory name
# ifdef VMS
! char_u *p;

! p = mch_getenv((char_u *)"SYS$LOGIN");
! if (p == NULL || *p == NUL) // empty is the same as not set
! NameBuff[0] = NUL;
! else
! vim_strncpy(NameBuff, p, MAXPATHL - 1);
# else
! expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
# endif
! new_dir = NameBuff;
! }
#endif
! dir_differs = new_dir == NULL || prev_dir == NULL
! || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0;
! if (new_dir == NULL || (dir_differs && vim_chdir(new_dir)))
! emsg(_(e_failed));
! else
! {
! char_u *acmd_fname;
!
! post_chdir(scope);
!
! if (dir_differs)
{
! if (scope == CDSCOPE_WINDOW)
! acmd_fname = (char_u *)"window";
! else if (scope == CDSCOPE_TABPAGE)
! acmd_fname = (char_u *)"tabpage";
! else
! acmd_fname = (char_u *)"global";
! apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE,
! curbuf);
! }
! retval = TRUE;
! }
! vim_free(tofree);

! return retval;
! }

! /*
! * ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
! */
! void
! ex_cd(exarg_T *eap)
! {
! char_u *new_dir;
!
! new_dir = eap->arg;
! #if !defined(UNIX) && !defined(VMS)
! // for non-UNIX ":cd" means: print current directory
! if (*new_dir == NUL)
! ex_pwd(NULL);
! else
! #endif
! {
! cdscope_T scope = CDSCOPE_GLOBAL;
!
! if (eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir)
! scope = CDSCOPE_WINDOW;
! else if (eap->cmdidx == CMD_tcd || eap->cmdidx == CMD_tchdir)
! scope = CDSCOPE_TABPAGE;
!
! if (changedir_func(new_dir, eap->forceit, scope))
! {
! // Echo the new current directory if the command was typed.
if (KeyTyped || p_verbose >= 5)
ex_pwd(eap);
}
}
}

*** ../vim-8.1.1290/src/if_py_both.h 2019-04-27 20:36:52.530303581 +0200
--- src/if_py_both.h 2019-05-07 21:56:20.286699640 +0200
***************
*** 1032,1038 ****
Py_DECREF(newwd);
Py_XDECREF(todecref);

! post_chdir(FALSE, FALSE);

if (VimTryEnd())
{
--- 1032,1038 ----
Py_DECREF(newwd);
Py_XDECREF(todecref);

! post_chdir(CDSCOPE_GLOBAL);

if (VimTryEnd())
{
*** ../vim-8.1.1290/src/proto/ex_docmd.pro 2019-04-27 20:36:52.534303564 +0200
--- src/proto/ex_docmd.pro 2019-05-07 21:56:20.286699640 +0200
***************
*** 37,43 ****
void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin);
void free_cd_dir(void);
! void post_chdir(int tablocal, int winlocal);
void ex_cd(exarg_T *eap);
void do_sleep(long msec);
void ex_may_print(exarg_T *eap);
--- 37,44 ----
void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin);
void free_cd_dir(void);
! void post_chdir(cdscope_T cdscope);
! int changedir_func(char_u *new_dir, int forceit, cdscope_T cdscope);
void ex_cd(exarg_T *eap);
void do_sleep(long msec);
void ex_may_print(exarg_T *eap);
*** ../vim-8.1.1290/src/structs.h 2019-05-05 15:47:37.825923529 +0200
--- src/structs.h 2019-05-07 22:01:54.424899979 +0200
***************
*** 3555,3557 ****
--- 3555,3564 ----
varnumber_T vv_count;
varnumber_T vv_count1;
} vimvars_save_T;
+
+ // Scope for changing directory
+ typedef enum {
+ CDSCOPE_GLOBAL, // :cd
+ CDSCOPE_TABPAGE, // :tcd
+ CDSCOPE_WINDOW // :lcd
+ } cdscope_T;
*** ../vim-8.1.1290/src/testdir/test_cd.vim 2018-07-03 18:36:23.037340552 +0200
--- src/testdir/test_cd.vim 2019-05-07 21:56:20.286699640 +0200
***************
*** 1,4 ****
! " Test for :cd

func Test_cd_large_path()
" This used to crash with a heap write overflow.
--- 1,4 ----
! " Test for :cd and chdir()

func Test_cd_large_path()
" This used to crash with a heap write overflow.
***************
*** 65,67 ****
--- 65,108 ----
set cpo&
bw!
endfunc
+
+ " Test for chdir()
+ func Test_chdir_func()
+ let topdir = getcwd()
+ call mkdir('Xdir/y/z', 'p')
+
+ " Create a few tabpages and windows with different directories
+ new
+ cd Xdir
+ tabnew
+ tcd y
+ below new
+ below new
+ lcd z
+
+ tabfirst
+ call chdir('..')
+ call assert_equal('y', fnamemodify(getcwd(1, 2), ':t'))
+ call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
+ tabnext | wincmd t
+ call chdir('..')
+ call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
+ call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
+ call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
+ call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
+ 3wincmd w
+ call chdir('..')
+ call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
+ call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
+ call assert_equal('y', fnamemodify(getcwd(3, 2), ':t'))
+ call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
+
+ " Error case
+ call assert_fails("call chdir('dir-abcd')", 'E472:')
+ silent! let d = chdir("dir_abcd")
+ call assert_equal("", d)
+
+ only | tabonly
+ exe 'cd ' . topdir
+ call delete('Xdir', 'rf')
+ endfunc
*** ../vim-8.1.1290/src/version.c 2019-05-07 21:48:15.629291397 +0200
--- src/version.c 2019-05-07 22:06:10.403517496 +0200
***************
*** 769,770 ****
--- 769,772 ----
{ /* Add new patch number below this line */
+ /**/
+ 1291,
/**/

--
Violators can be fined, arrested or jailed for making ugly faces at a dog.
[real standing law in Oklahoma, United States of America]

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages