patch 8.1.1291: not easy to change directory and restore
Problem: Not easy to change directory and restore.
Solution: Add the chdir() function. (Yegappan Lakshmanan, closes #4358)
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 1393d0e..a2b302d 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -7513,17 +7513,17 @@
/*
* 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.
+ * 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(int tablocal, int winlocal)
+post_chdir(cdscope_T scope)
{
- if (!winlocal)
+ if (scope != CDSCOPE_WINDOW)
// Clear tab local directory for both :cd and :tcd
VIM_CLEAR(curtab->tp_localdir);
VIM_CLEAR(curwin->w_localdir);
- if (winlocal || tablocal)
+ if (scope != CDSCOPE_GLOBAL)
{
/* If still in global directory, need to remember current
* directory as global directory. */
@@ -7532,7 +7532,7 @@
/* Remember this local directory for the window. */
if (mch_dirname(NameBuff, MAXPATHL) == OK)
{
- if (tablocal)
+ if (scope == CDSCOPE_TABPAGE)
curtab->tp_localdir = vim_strsave(NameBuff);
else
curwin->w_localdir = vim_strsave(NameBuff);
@@ -7548,6 +7548,96 @@
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".
@@ -7556,94 +7646,28 @@
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 */
+ // 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)
+ 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))
{
- 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. */
+ // 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);
}
}