Imported from ../bash-3.1.tar.gz.
[platform/upstream/bash.git] / builtins / cd.def
index ef8d805..025e4f5 100644 (file)
@@ -1,13 +1,13 @@
 This file is cd.def, from which is created cd.c.  It implements the
 builtins "cd" and "pwd" in Bash.
 
 This file is cd.def, from which is created cd.c.  It implements the
 builtins "cd" and "pwd" in Bash.
 
-Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
 Bash is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
 
 This file is part of GNU Bash, the Bourne Again SHell.
 
 Bash is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 1, or (at your option) any later
+Software Foundation; either version 2, or (at your option) any later
 version.
 
 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
 version.
 
 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -17,7 +17,7 @@ for more details.
 
 You should have received a copy of the GNU General Public License along
 with Bash; see the file COPYING.  If not, write to the Free Software
 
 You should have received a copy of the GNU General Public License along
 with Bash; see the file COPYING.  If not, write to the Free Software
-Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
 
 $PRODUCES cd.c
 #include <config.h>
 
 $PRODUCES cd.c
 #include <config.h>
@@ -30,8 +30,8 @@ $PRODUCES cd.c
 #endif
 
 #include "../bashtypes.h"
 #endif
 
 #include "../bashtypes.h"
-#include "../posixdir.h"
-#include "../posixstat.h"
+#include "posixdir.h"
+#include "posixstat.h"
 #ifndef _MINIX
 #include <sys/param.h>
 #endif
 #ifndef _MINIX
 #include <sys/param.h>
 #endif
@@ -39,13 +39,14 @@ $PRODUCES cd.c
 #include <stdio.h>
 
 #include "../bashansi.h"
 #include <stdio.h>
 
 #include "../bashansi.h"
+#include "../bashintl.h"
 
 #include <errno.h>
 #include <tilde/tilde.h>
 
 #include "../shell.h"
 #include "../flags.h"
 
 #include <errno.h>
 #include <tilde/tilde.h>
 
 #include "../shell.h"
 #include "../flags.h"
-#include "../maxpath.h"
+#include "maxpath.h"
 #include "common.h"
 #include "bashgetopt.h"
 
 #include "common.h"
 #include "bashgetopt.h"
 
@@ -53,14 +54,15 @@ $PRODUCES cd.c
 extern int errno;
 #endif /* !errno */
 
 extern int errno;
 #endif /* !errno */
 
-extern int posixly_correct, interactive;
+extern int posixly_correct;
 extern int array_needs_making;
 extern char *bash_getcwd_errstr;
 
 extern int array_needs_making;
 extern char *bash_getcwd_errstr;
 
-static int change_to_directory ();
+static int bindpwd __P((int));
+static void setpwd __P((char *));
+static int change_to_directory __P((char *, int));
 
 
-static char *cdspell ();
-static int spname (), mindist (), spdist ();
+static char *cdspell __P((char *));
 
 /* Change this to 1 to get cd spelling correction by default. */
 int cdspelling = 0;
 
 /* Change this to 1 to get cd spelling correction by default. */
 int cdspelling = 0;
@@ -69,7 +71,7 @@ int cdable_vars;
 
 $BUILTIN cd
 $FUNCTION cd_builtin
 
 $BUILTIN cd
 $FUNCTION cd_builtin
-$SHORT_DOC cd [-PL] [dir]
+$SHORT_DOC cd [-L|-P] [dir]
 Change the current directory to DIR.  The variable $HOME is the
 default DIR.  The variable CDPATH defines the search path for
 the directory containing DIR.  Alternative directory names in CDPATH
 Change the current directory to DIR.  The variable $HOME is the
 default DIR.  The variable CDPATH defines the search path for
 the directory containing DIR.  Alternative directory names in CDPATH
@@ -83,43 +85,21 @@ instead of following symbolic links; the -L option forces symbolic links
 to be followed.
 $END
 
 to be followed.
 $END
 
-/* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste
-   them together into PATH/DIR.  Tilde expansion is performed on PATH if
-   DOTILDE is non-zero.  If PATH is the empty string, it is converted to
-   `./', since a null element in $CDPATH means the current directory. */
-static char *
-mkpath (path, dir, dotilde)
-     char *path, *dir;
-     int dotilde;
+/* Just set $PWD, don't change OLDPWD.  Used by `pwd -P' in posix mode. */
+static void
+setpwd (dirname)
+     char *dirname;
 {
 {
-  int dirlen, pathlen;
-  char *ret, *xpath;
-
-  if (*path == '\0')
-    {
-      xpath = xmalloc (2);
-      xpath[0] = '.';
-      xpath[1] = '\0';
-      pathlen = 1;
-    }
-  else
-    {
-      xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
-      pathlen = strlen (xpath);
-    }
+  int old_anm;
+  SHELL_VAR *tvar;
 
 
-  dirlen = strlen (dir);
-  ret = xmalloc (2 + dirlen + pathlen);
-  strcpy (ret, xpath);
-  if (xpath[pathlen - 1] != '/')
+  old_anm = array_needs_making;
+  tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
+  if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
     {
-      ret[pathlen++] = '/';
-      ret[pathlen] = '\0';
+      update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
+      array_needs_making = 0;
     }
     }
-  strcpy (ret + pathlen, dir);
-  if (xpath != path)
-    free (xpath);
-  return (ret);
 }
 
 static int
 }
 
 static int
@@ -127,42 +107,51 @@ bindpwd (no_symlinks)
      int no_symlinks;
 {
   char *dirname, *pwdvar;
      int no_symlinks;
 {
   char *dirname, *pwdvar;
-  int old_symlinks, old_anm;
+  int old_anm;
   SHELL_VAR *tvar;
 
   SHELL_VAR *tvar;
 
-  if (no_symlinks)
-    {
-      old_symlinks = no_symbolic_links;
-      no_symbolic_links = 1;
-      dirname = get_working_directory ("cd");
-      no_symbolic_links = old_symlinks;
-    }
-  else
-    dirname = get_working_directory ("cd");
-
-  bind_variable ("OLDPWD", get_string_value ("PWD"));
+#define tcwd the_current_working_directory
+  dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
+                : get_working_directory ("cd");
+#undef tcwd
 
   old_anm = array_needs_making;
 
   old_anm = array_needs_making;
-  tvar = bind_variable ("PWD", dirname);
-  /* This is an efficiency hack.  If PWD is exported, we will need to
-     remake the exported environment every time we change directories.
-     If there is no other reason to make the exported environment, just
-     update PWD in place and mark the exported environment as no longer
-     needing a remake. */
+  pwdvar = get_string_value ("PWD");
+
+  tvar = bind_variable ("OLDPWD", pwdvar, 0);
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
   if (old_anm == 0 && array_needs_making && exported_p (tvar))
     {
-      pwdvar = xmalloc (STRLEN (dirname) + 5); /* 5 = "PWD" + '=' + '\0' */
-      strcpy (pwdvar, "PWD=");
-      if (dirname)
-       strcpy (pwdvar + 4, dirname);
-      add_or_supercede_exported_var (pwdvar, 0);
+      update_export_env_inplace ("OLDPWD=", 7, pwdvar);
       array_needs_making = 0;
     }
 
       array_needs_making = 0;
     }
 
-  FREE (dirname);
+  setpwd (dirname);
+
+  if (dirname && dirname != the_current_working_directory)
+    free (dirname);
+
   return (EXECUTION_SUCCESS);
 }
 
   return (EXECUTION_SUCCESS);
 }
 
+/* Call get_working_directory to reset the value of
+   the_current_working_directory () */
+static char *
+resetpwd (caller)
+     char *caller;
+{
+  char *tdir;
+      
+  FREE (the_current_working_directory);
+  the_current_working_directory = (char *)NULL;
+  tdir = get_working_directory (caller);
+  return (tdir);
+}
+
+#define LCD_DOVARS     0x001
+#define LCD_DOSPELL    0x002
+#define LCD_PRINTPATH  0x004
+#define LCD_FREEDIRNAME        0x010
+
 /* This builtin is ultimately the way that all user-visible commands should
    change the current working directory.  It is called by cd_to_string (),
    so the programming interface is simple, and it handles errors and
 /* This builtin is ultimately the way that all user-visible commands should
    change the current working directory.  It is called by cd_to_string (),
    so the programming interface is simple, and it handles errors and
@@ -172,13 +161,12 @@ cd_builtin (list)
      WORD_LIST *list;
 {
   char *dirname, *cdpath, *path, *temp;
      WORD_LIST *list;
 {
   char *dirname, *cdpath, *path, *temp;
-  int path_index, no_symlinks, opt;
-  struct stat sb;
+  int path_index, no_symlinks, opt, lflag;
 
 #if defined (RESTRICTED_SHELL)
   if (restricted)
     {
 
 #if defined (RESTRICTED_SHELL)
   if (restricted)
     {
-      builtin_error ("restricted");
+      sh_restricted ((char *)NULL);
       return (EXECUTION_FAILURE);
     }
 #endif /* RESTRICTED_SHELL */
       return (EXECUTION_FAILURE);
     }
 #endif /* RESTRICTED_SHELL */
@@ -202,6 +190,9 @@ cd_builtin (list)
     }
   list = loptend;
 
     }
   list = loptend;
 
+  lflag = (cdable_vars ? LCD_DOVARS : 0) |
+         ((interactive && cdspelling) ? LCD_DOSPELL : 0);
+
   if (list == 0)
     {
       /* `cd' without arguments is equivalent to `cd $HOME' */
   if (list == 0)
     {
       /* `cd' without arguments is equivalent to `cd $HOME' */
@@ -209,124 +200,122 @@ cd_builtin (list)
 
       if (dirname == 0)
        {
 
       if (dirname == 0)
        {
-         builtin_error ("HOME not set");
-         return (EXECUTION_FAILURE);
-       }
-
-      if (change_to_directory (dirname, no_symlinks) == 0)
-       {
-         builtin_error ("%s: %s", dirname, strerror (errno));
+         builtin_error (_("HOME not set"));
          return (EXECUTION_FAILURE);
        }
          return (EXECUTION_FAILURE);
        }
+      lflag = 0;
     }
   else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
     {
       /* This is `cd -', equivalent to `cd $OLDPWD' */
       dirname = get_string_value ("OLDPWD");
 
     }
   else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
     {
       /* This is `cd -', equivalent to `cd $OLDPWD' */
       dirname = get_string_value ("OLDPWD");
 
-      if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
+      if (dirname == 0)
        {
        {
-         if (dirname == 0)
-           builtin_error ("OLDPWD not set");
-         else
-           builtin_error ("%s: %s", dirname, strerror (errno));
+         builtin_error (_("OLDPWD not set"));
          return (EXECUTION_FAILURE);
        }
          return (EXECUTION_FAILURE);
        }
-      if (interactive)
-       printf ("%s\n", dirname);
+#if 0
+      lflag = interactive ? LCD_PRINTPATH : 0;
+#else
+      lflag = LCD_PRINTPATH;           /* According to SUSv3 */
+#endif
     }
     }
-  else
+  else if (absolute_pathname (list->word->word))
+    dirname = list->word->word;
+  else if (cdpath = get_string_value ("CDPATH"))
     {
       dirname = list->word->word;
 
     {
       dirname = list->word->word;
 
-      if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
+      /* Find directory in $CDPATH. */
+      path_index = 0;
+      while (path = extract_colon_unit (cdpath, &path_index))
        {
        {
-         /* Find directory in $CDPATH. */
-         path_index = 0;
-         while ((path = extract_colon_unit (cdpath, &path_index)))
-           {
-             /* OPT is 1 if the path element is non-empty */
-             opt = path[0] != '\0';
-             temp = mkpath (path, dirname, 1);
-             free (path);
-
-             if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
-               {
-                 free (temp);
-                 continue;
-               }
-
-             if (change_to_directory (temp, no_symlinks))
-               {
-                 /* POSIX.2 says that if a nonempty directory from CDPATH
-                    is used to find the directory to change to, the new
-                    directory name is echoed to stdout, whether or not
-                    the shell is interactive. */
-                 if (opt)
-                   printf ("%s\n", the_current_working_directory);
-
-                 free (temp);
-                 /* Posix.2 says that after using CDPATH, the resultant
-                    value of $PWD will not contain symlinks. */
-                 return (bindpwd (posixly_correct || no_symlinks));
-               }
-             else
-               free (temp);
-           }
+         /* OPT is 1 if the path element is non-empty */
+         opt = path[0] != '\0';
+         temp = sh_makepath (path, dirname, MP_DOTILDE);
+         free (path);
 
 
-         /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
-            try the current directory, so we just punt now with an error
-            message if POSIXLY_CORRECT is non-zero. */
-         if (posixly_correct)
+         if (change_to_directory (temp, no_symlinks))
            {
            {
-             builtin_error ("%s: %s", dirname, strerror (ENOENT));
-             return (EXECUTION_FAILURE);
+             /* POSIX.2 says that if a nonempty directory from CDPATH
+                is used to find the directory to change to, the new
+                directory name is echoed to stdout, whether or not
+                the shell is interactive. */
+             if (opt && (path = no_symlinks ? temp : the_current_working_directory))
+               printf ("%s\n", path);
+
+             free (temp);
+#if 0
+             /* Posix.2 says that after using CDPATH, the resultant
+                value of $PWD will not contain `.' or `..'. */
+             return (bindpwd (posixly_correct || no_symlinks));
+#else
+             return (bindpwd (no_symlinks));
+#endif
            }
            }
+         else
+           free (temp);
        }
 
        }
 
-      if (change_to_directory (dirname, no_symlinks))
-       return (bindpwd (no_symlinks));
-
-      /* If the user requests it, then perhaps this is the name of
-        a shell variable, whose value contains the directory to
-        change to.  If that is the case, then change to that
-        directory. */
-      if (cdable_vars)
+      /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
+        try the current directory, so we just punt now with an error
+        message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
+        is so we don't mistakenly treat a CDPATH value of "" as not
+        specifying the current directory. */
+      if (posixly_correct && cdpath[0])
        {
        {
-         temp = get_string_value (dirname);
-         if (temp && change_to_directory (temp, no_symlinks))
-           {
-             printf ("%s\n", temp);
-             return (bindpwd (no_symlinks));
-           }
+         builtin_error ("%s: %s", dirname, strerror (ENOENT));
+         return (EXECUTION_FAILURE);
        }
        }
+    }
+  else
+    dirname = list->word->word;
+
+  /* When we get here, DIRNAME is the directory to change to.  If we
+     chdir successfully, just return. */
+  if (change_to_directory (dirname, no_symlinks))
+    {
+      if (lflag & LCD_PRINTPATH)
+       printf ("%s\n", dirname);
+      return (bindpwd (no_symlinks));
+    }
 
 
-      /* If the user requests it, try to find a directory name similar in
-        spelling to the one requested, in case the user made a simple
-        typo.  This is similar to the UNIX 8th and 9th Edition shells. */
-      if (interactive && cdspelling)
+  /* If the user requests it, then perhaps this is the name of
+     a shell variable, whose value contains the directory to
+     change to. */
+  if (lflag & LCD_DOVARS)
+    {
+      temp = get_string_value (dirname);
+      if (temp && change_to_directory (temp, no_symlinks))
        {
        {
-         temp = cdspell (dirname);
-         if (temp && change_to_directory (temp, no_symlinks))
-            {
-              printf ("%s\n", temp);
-              free (temp);
-              return (bindpwd (no_symlinks));
-            }
-          else
-           FREE (temp);
+         printf ("%s\n", temp);
+         return (bindpwd (no_symlinks));
        }
        }
+    }
 
 
-      builtin_error ("%s: %s", dirname, strerror (errno));
-      return (EXECUTION_FAILURE);
+  /* If the user requests it, try to find a directory name similar in
+     spelling to the one requested, in case the user made a simple
+     typo.  This is similar to the UNIX 8th and 9th Edition shells. */
+  if (lflag & LCD_DOSPELL)
+    {
+      temp = cdspell (dirname);
+      if (temp && change_to_directory (temp, no_symlinks))
+       {
+         printf ("%s\n", temp);
+         return (bindpwd (no_symlinks));
+       }
+      else
+       FREE (temp);
     }
 
     }
 
-  return (bindpwd (no_symlinks));
+  builtin_error ("%s: %s", dirname, strerror (errno));
+  return (EXECUTION_FAILURE);
 }
 
 $BUILTIN pwd
 $FUNCTION pwd_builtin
 }
 
 $BUILTIN pwd
 $FUNCTION pwd_builtin
-$SHORT_DOC pwd [-PL]
+$SHORT_DOC pwd [-LP]
 Print the current working directory.  With the -P option, pwd prints
 the physical directory, without any symbolic links; the -L option
 makes pwd follow symbolic links.
 Print the current working directory.  With the -P option, pwd prints
 the physical directory, without any symbolic links; the -L option
 makes pwd follow symbolic links.
@@ -341,17 +330,18 @@ int
 pwd_builtin (list)
      WORD_LIST *list;
 {
 pwd_builtin (list)
      WORD_LIST *list;
 {
-  char *directory, *buffer;
-  int opt;
+  char *directory;
+  int opt, pflag;
 
   verbatim_pwd = no_symbolic_links;
 
   verbatim_pwd = no_symbolic_links;
+  pflag = 0;
   reset_internal_getopt ();
   while ((opt = internal_getopt (list, "LP")) != -1)
     {
       switch (opt)
        {
        case 'P':
   reset_internal_getopt ();
   while ((opt = internal_getopt (list, "LP")) != -1)
     {
       switch (opt)
        {
        case 'P':
-         verbatim_pwd = 1;
+         verbatim_pwd = pflag = 1;
          break;
        case 'L':
          verbatim_pwd = 0;
          break;
        case 'L':
          verbatim_pwd = 0;
@@ -363,25 +353,35 @@ pwd_builtin (list)
     }
   list = loptend;
 
     }
   list = loptend;
 
-  if (verbatim_pwd)
-    {
-      buffer = xmalloc (PATH_MAX);
-      directory = getcwd (buffer, PATH_MAX);
+#define tcwd the_current_working_directory
 
 
-      if (directory == 0)
-       {
-         builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
-         free (buffer);
-       }
-    }
-  else
-    directory = get_working_directory ("pwd");
+  directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
+                  : get_working_directory ("pwd");
+
+  /* Try again using getcwd() if canonicalization fails (for instance, if
+     the file system has changed state underneath bash). */
+  if ((tcwd && directory == 0) ||
+      (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
+    directory = resetpwd ("pwd");
+
+#undef tcwd
 
   if (directory)
     {
       printf ("%s\n", directory);
 
   if (directory)
     {
       printf ("%s\n", directory);
+      /* This is dumb but posix-mandated. */
+      if (posixly_correct && pflag)
+       setpwd (directory);
+      if (directory != the_current_working_directory)
+       free (directory);
       fflush (stdout);
       fflush (stdout);
-      free (directory);
+      if (ferror (stdout))
+       {
+         sh_wrerror ();
+         clearerr (stdout);
+         return (EXECUTION_FAILURE);
+       }
+
       return (EXECUTION_SUCCESS);
     }
   else
       return (EXECUTION_SUCCESS);
     }
   else
@@ -389,83 +389,114 @@ pwd_builtin (list)
 }
 
 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
 }
 
 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
-   link following, etc. */
+   link following, etc.  This function *must* return with
+   the_current_working_directory either set to NULL (in which case
+   getcwd() will eventually be called), or set to a string corresponding
+   to the working directory.  Return 1 on success, 0 on failure. */
 
 static int
 change_to_directory (newdir, nolinks)
      char *newdir;
      int nolinks;
 {
 
 static int
 change_to_directory (newdir, nolinks)
      char *newdir;
      int nolinks;
 {
-  char *t;
+  char *t, *tdir;
+  int err, canon_failed, r, ndlen, dlen;
 
 
-  if (nolinks == 0)
+  tdir = (char *)NULL;
+
+  if (the_current_working_directory == 0)
     {
     {
-      int chdir_return = 0;
-      char *tdir = (char *)NULL;
+      t = get_working_directory ("chdir");
+      FREE (t);
+    }
 
 
-      if (the_current_working_directory == 0)
-       {
-         t = get_working_directory ("cd_links");
-         FREE (t);
-       }
+  t = make_absolute (newdir, the_current_working_directory);
 
 
-      if (the_current_working_directory)
-       t = make_absolute (newdir, the_current_working_directory);
-      else
-       t = savestring (newdir);
+  /* TDIR is either the canonicalized absolute pathname of NEWDIR
+     (nolinks == 0) or the absolute physical pathname of NEWDIR
+     (nolinks != 0). */
+  tdir = nolinks ? sh_physpath (t, 0)
+                : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
 
 
-      /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
-      tdir = canonicalize_pathname (t);
+  ndlen = strlen (newdir);
+  dlen = strlen (t);
 
 
-      /* Use the canonicalized version of NEWDIR, or, if canonicalization
-        failed, use the non-canonical form. */
-      if (tdir && *tdir)
-       free (t);
-      else
+  /* Use the canonicalized version of NEWDIR, or, if canonicalization
+     failed, use the non-canonical form. */
+  canon_failed = 0;
+  if (tdir && *tdir)
+    free (t);
+  else
+    {
+      FREE (tdir);
+      tdir = t;
+      canon_failed = 1;
+    }
+
+  /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
+     returns NULL (because it checks the path, it will return NULL if the
+     resolved path doesn't exist), fail immediately. */
+  if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
+    {
+#if defined ENAMETOOLONG
+      if (errno != ENOENT && errno != ENAMETOOLONG)
+#else
+      if (errno != ENOENT)
+#endif
+       errno = ENOTDIR;
+      free (tdir);
+      return (0);
+    }
+
+  /* If the chdir succeeds, update the_current_working_directory. */
+  if (chdir (nolinks ? newdir : tdir) == 0)
+    {
+      /* If canonicalization failed, but the chdir succeeded, reset the
+        shell's idea of the_current_working_directory. */
+      if (canon_failed)
        {
        {
-         FREE (tdir);
-         tdir = t;
+         t = resetpwd ("cd");
+         if (t == 0)
+           set_working_directory (tdir);
        }
        }
+      else
+       set_working_directory (tdir);
 
 
-      if (chdir (tdir) < 0)
-       {
-         int err;
+      free (tdir);
+      return (1);
+    }
 
 
-         chdir_return = 0;
-         free (tdir);
+  /* We failed to change to the appropriate directory name.  If we tried
+     what the user passed (nolinks != 0), punt now. */
+  if (nolinks)
+    {
+      free (tdir);
+      return (0);
+    }
 
 
-         err = errno;
+  err = errno;
 
 
-         /* We failed changing to the canonicalized directory name.  Try
-            what the user passed verbatim.  If we succeed, reinitialize
-            the_current_working_directory. */
-         if (chdir (newdir) == 0)
-           {
-             chdir_return = 1;
-             if (the_current_working_directory)
-               {
-                 free (the_current_working_directory);
-                 the_current_working_directory = (char *)NULL;
-               }
-
-             tdir = get_working_directory ("cd");
-             FREE (tdir);
-           }
-         else
-           errno = err;
-       }
+  /* We're not in physical mode (nolinks == 0), but we failed to change to
+     the canonicalized directory name (TDIR).  Try what the user passed
+     verbatim. If we succeed, reinitialize the_current_working_directory. */
+  if (chdir (newdir) == 0)
+    {
+      t = resetpwd ("cd");
+      if (t == 0)
+       set_working_directory (tdir);
       else
       else
-       {
-         chdir_return = 1;
-
-         FREE (the_current_working_directory);
-         the_current_working_directory = tdir;
-       }
+       free (t);
 
 
-      return (chdir_return);
+      r = 1;
     }
   else
     }
   else
-    return (chdir (newdir) == 0);
+    {
+      errno = err;
+      r = 0;
+    }
+
+  free (tdir);
+  return r;
 }
 
 /* Code for cd spelling correction.  Original patch submitted by
 }
 
 /* Code for cd spelling correction.  Original patch submitted by
@@ -479,7 +510,7 @@ cdspell (dirname)
   char *guess;
 
   n = (strlen (dirname) * 3 + 1) / 2 + 1;
   char *guess;
 
   n = (strlen (dirname) * 3 + 1) / 2 + 1;
-  guess = xmalloc (n);
+  guess = (char *)xmalloc (n);
 
   switch (spname (dirname, guess))
     {
 
   switch (spname (dirname, guess))
     {
@@ -492,147 +523,3 @@ cdspell (dirname)
       return guess;
     }
 }
       return guess;
     }
 }
-
-/*
- * `spname' and its helpers are inspired by the code in "The UNIX
- * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
- * pages 209 - 213.
- */
-
-/*
- *     `spname' -- return a correctly spelled filename
- *
- *     int spname(char * oldname, char * newname)
- *     Returns:  -1 if no reasonable match found
- *                0 if exact match found
- *                1 if corrected
- *     Stores corrected name in `newname'.
- */
-static int
-spname(oldname, newname)
-     char *oldname;
-     char *newname;
-{
-  char *op, *np, *p;
-  char guess[PATH_MAX + 1], best[PATH_MAX + 1];
-
-  op = oldname;
-  np = newname;
-  for (;;)
-    {
-      while (*op == '/')    /* Skip slashes */
-       *np++ = *op++;
-      *np = '\0';
-
-      if (*op == '\0')    /* Exact or corrected */
-        {
-         /* `.' is rarely the right thing. */
-         if (oldname[1] == '\0' && newname[1] == '\0' &&
-               oldname[0] != '.' && newname[0] == '.')
-           return -1;
-         return strcmp(oldname, newname) != 0;
-        }
-
-      /* Copy next component into guess */
-      for (p = guess; *op != '/' && *op != '\0'; op++)
-       if (p < guess + PATH_MAX)
-         *p++ = *op;
-      *p = '\0';
-
-      if (mindist(newname, guess, best) >= 3)
-       return -1;  /* Hopeless */
-
-      /*
-       *  Add to end of newname
-       */
-      for (p = best; *np = *p++; np++)
-       ;
-    }
-}
-
-/*
- *  Search directory for a guess
- */
-static int
-mindist(dir, guess, best)
-     char *dir;
-     char *guess;
-     char *best;
-{
-  DIR *fd;
-  struct dirent *dp;
-  int dist, x;
-
-  dist = 3;    /* Worst distance */
-  if (*dir == '\0')
-    dir = ".";
-
-  if ((fd = opendir(dir)) == NULL)
-    return dist;
-
-  while ((dp = readdir(fd)) != NULL)
-    {
-      /*
-       *  Look for a better guess.  If the new guess is as
-       *  good as the current one, we take it.  This way,
-       *  any single character match will be a better match
-       *  than ".".
-       */
-      x = spdist(dp->d_name, guess);
-      if (x <= dist && x != 3)
-       {
-         strcpy(best, dp->d_name);
-         dist = x;
-         if (dist == 0)    /* Exact match */
-           break;
-       }
-    }
-  (void)closedir(fd);
-
-  /* Don't return `.' */
-  if (best[0] == '.' && best[1] == '\0')
-    dist = 3;
-  return dist;
-}
-
-/*
- *  `spdist' -- return the "distance" between two names.
- *
- *  int spname(char * oldname, char * newname)
- *  Returns:  0 if strings are identical
- *      1 if two characters are transposed
- *      2 if one character is wrong, added or deleted
- *      3 otherwise
- */
-static int
-spdist(cur, new)
-     char *cur, *new;
-{
-  while (*cur == *new)
-    {
-      if (*cur == '\0')
-       return 0;    /* Exact match */
-      cur++;
-      new++;
-    }
-
-  if (*cur)
-    {
-      if (*new)
-       {
-         if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
-           return 1;  /* Transposition */
-
-         if (strcmp (cur + 1, new + 1) == 0)
-           return 2;  /* One character mismatch */
-       }
-
-      if (strcmp(&cur[1], &new[0]) == 0)
-       return 2;    /* Extra character */
-    }
-
-  if (*new && strcmp(cur, new + 1) == 0)
-    return 2;      /* Missing character */
-
-  return 3;
-}