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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201905072007.x47K7QLN012131%40masaka.moolenaar.net.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui