Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / builtins / cd.def
index b6bf692..57306a5 100644 (file)
@@ -7,7 +7,7 @@ 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
 
 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
@@ -45,7 +45,7 @@ $PRODUCES cd.c
 
 #include "../shell.h"
 #include "../flags.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"
 
@@ -60,7 +60,6 @@ extern char *bash_getcwd_errstr;
 static int change_to_directory ();
 
 static char *cdspell ();
 static int change_to_directory ();
 
 static char *cdspell ();
-static int spname (), mindist (), spdist ();
 
 /* 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;
@@ -83,45 +82,6 @@ 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;
-{
-  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);
-    }
-
-  dirlen = strlen (dir);
-  ret = xmalloc (2 + dirlen + pathlen);
-  strcpy (ret, xpath);
-  if (xpath[pathlen - 1] != '/')
-    {
-      ret[pathlen++] = '/';
-      ret[pathlen] = '\0';
-    }
-  strcpy (ret + pathlen, dir);
-  if (xpath != path)
-    free (xpath);
-  return (ret);
-}
-
 static int
 bindpwd (no_symlinks)
      int no_symlinks;
 static int
 bindpwd (no_symlinks)
      int no_symlinks;
@@ -130,15 +90,10 @@ bindpwd (no_symlinks)
   int old_symlinks, old_anm;
   SHELL_VAR *tvar;
 
   int old_symlinks, old_anm;
   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");
+#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;
   pwdvar = get_string_value ("PWD");
 
   old_anm = array_needs_making;
   pwdvar = get_string_value ("PWD");
@@ -157,10 +112,16 @@ bindpwd (no_symlinks)
       array_needs_making = 0;
     }
 
       array_needs_making = 0;
     }
 
-  FREE (dirname);
+  if (dirname && dirname != the_current_working_directory)
+    free (dirname);
   return (EXECUTION_SUCCESS);
 }
 
   return (EXECUTION_SUCCESS);
 }
 
+#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
@@ -170,8 +131,7 @@ 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)
@@ -200,6 +160,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' */
@@ -210,118 +173,106 @@ cd_builtin (list)
          builtin_error ("HOME not set");
          return (EXECUTION_FAILURE);
        }
          builtin_error ("HOME not set");
          return (EXECUTION_FAILURE);
        }
-
-      if (change_to_directory (dirname, no_symlinks) == 0)
-       {
-         builtin_error ("%s: %s", dirname, strerror (errno));
-         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);
+      lflag = interactive ? LCD_PRINTPATH : 0;
     }
     }
-  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", no_symlinks ? temp : 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.  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])
+         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)
+               printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
+
+             free (temp);
+             /* Posix.2 says that after using CDPATH, the resultant
+                value of $PWD will not contain `.' or `..'. */
+             return (bindpwd (posixly_correct || no_symlinks));
            }
            }
+         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
 }
 
 $BUILTIN pwd
@@ -341,7 +292,7 @@ int
 pwd_builtin (list)
      WORD_LIST *list;
 {
 pwd_builtin (list)
      WORD_LIST *list;
 {
-  char *directory, *buffer;
+  char *directory;
   int opt;
 
   verbatim_pwd = no_symbolic_links;
   int opt;
 
   verbatim_pwd = no_symbolic_links;
@@ -363,25 +314,23 @@ 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");
+#undef tcwd
 
   if (directory)
     {
       printf ("%s\n", directory);
 
   if (directory)
     {
       printf ("%s\n", directory);
+      if (directory != the_current_working_directory)
+       free (directory);
       fflush (stdout);
       fflush (stdout);
-      free (directory);
+      if (ferror (stdout))
+       {
+         builtin_error ("write error: %s", strerror (errno));
+         return (EXECUTION_FAILURE);
+       }
       return (EXECUTION_SUCCESS);
     }
   else
       return (EXECUTION_SUCCESS);
     }
   else
@@ -389,83 +338,79 @@ 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;
 
 
-  if (nolinks == 0)
-    {
-      int chdir_return = 0;
-      char *tdir = (char *)NULL;
-
-      if (the_current_working_directory == 0)
-       {
-         t = get_working_directory ("cd_links");
-         FREE (t);
-       }
+  tdir = (char *)NULL;
 
 
-      if (the_current_working_directory)
-       t = make_absolute (newdir, the_current_working_directory);
-      else
-       t = savestring (newdir);
+  if (the_current_working_directory == 0)
+    {
+      t = get_working_directory ("chdir");
+      FREE (t);
+    }
 
 
-      /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
-      tdir = canonicalize_pathname (t);
+  t = make_absolute (newdir, the_current_working_directory);
 
 
-      /* Use the canonicalized version of NEWDIR, or, if canonicalization
-        failed, use the non-canonical form. */
-      if (tdir && *tdir)
-       free (t);
-      else
-       {
-         FREE (tdir);
-         tdir = t;
-       }
+  /* 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);
 
 
-      if (chdir (tdir) < 0)
-       {
-         int err;
+  /* Use the canonicalized version of NEWDIR, or, if canonicalization
+     failed, use the non-canonical form. */
+  if (tdir && *tdir)
+    free (t);
+  else
+    {
+      FREE (tdir);
+      tdir = t;
+    }
 
 
-         chdir_return = 0;
-         free (tdir);
+  /* If the chdir succeeds, update the_current_working_directory. */
+  if (chdir (nolinks ? newdir : tdir) == 0)
+    {
+      FREE (the_current_working_directory);
+      the_current_working_directory = tdir;
+      
+      return (1);
+    }
 
 
-         err = errno;
+  /* We failed to change to the appropriate directory name.  If we tried
+     what the user passed (nolinks != 0), punt now. */
+  if (nolinks)
+    return (0);
 
 
-         /* 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;
-       }
-      else
-       {
-         chdir_return = 1;
+  err = errno;
+  free (tdir);
 
 
-         FREE (the_current_working_directory);
-         the_current_working_directory = tdir;
-       }
+  /* 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)
+    {
+      FREE (the_current_working_directory);
+      the_current_working_directory = (char *)NULL;
+      tdir = get_working_directory ("cd");
+      FREE (tdir);
 
 
-      return (chdir_return);
+      return (1);
     }
   else
     }
   else
-    return (chdir (newdir) == 0);
+    {
+      errno = err;
+      return (0);
+    }
 }
 
 /* Code for cd spelling correction.  Original patch submitted by
 }
 
 /* Code for cd spelling correction.  Original patch submitted by
@@ -492,147 +437,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;
-}