summaryrefslogtreecommitdiff
path: root/builtins/cd.def
diff options
context:
space:
mode:
Diffstat (limited to 'builtins/cd.def')
-rw-r--r--builtins/cd.def525
1 files changed, 525 insertions, 0 deletions
diff --git a/builtins/cd.def b/builtins/cd.def
new file mode 100644
index 0000000..025e4f5
--- /dev/null
+++ b/builtins/cd.def
@@ -0,0 +1,525 @@
+This file is cd.def, from which is created cd.c. It implements the
+builtins "cd" and "pwd" in Bash.
+
+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
+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
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+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
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+
+$PRODUCES cd.c
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include "posixdir.h"
+#include "posixstat.h"
+#ifndef _MINIX
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include <errno.h>
+#include <tilde/tilde.h>
+
+#include "../shell.h"
+#include "../flags.h"
+#include "maxpath.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int posixly_correct;
+extern int array_needs_making;
+extern char *bash_getcwd_errstr;
+
+static int bindpwd __P((int));
+static void setpwd __P((char *));
+static int change_to_directory __P((char *, int));
+
+static char *cdspell __P((char *));
+
+/* Change this to 1 to get cd spelling correction by default. */
+int cdspelling = 0;
+
+int cdable_vars;
+
+$BUILTIN cd
+$FUNCTION cd_builtin
+$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
+are separated by a colon (:). A null directory name is the same as
+the current directory, i.e. `.'. If DIR begins with a slash (/),
+then CDPATH is not used. If the directory is not found, and the
+shell option `cdable_vars' is set, then try the word as a variable
+name. If that variable has a value, then cd to the value of that
+variable. The -P option says to use the physical directory structure
+instead of following symbolic links; the -L option forces symbolic links
+to be followed.
+$END
+
+/* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */
+static void
+setpwd (dirname)
+ char *dirname;
+{
+ int old_anm;
+ SHELL_VAR *tvar;
+
+ old_anm = array_needs_making;
+ tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
+ if (old_anm == 0 && array_needs_making && exported_p (tvar))
+ {
+ update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
+ array_needs_making = 0;
+ }
+}
+
+static int
+bindpwd (no_symlinks)
+ int no_symlinks;
+{
+ char *dirname, *pwdvar;
+ int old_anm;
+ SHELL_VAR *tvar;
+
+#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");
+
+ tvar = bind_variable ("OLDPWD", pwdvar, 0);
+ if (old_anm == 0 && array_needs_making && exported_p (tvar))
+ {
+ update_export_env_inplace ("OLDPWD=", 7, pwdvar);
+ array_needs_making = 0;
+ }
+
+ setpwd (dirname);
+
+ if (dirname && dirname != the_current_working_directory)
+ free (dirname);
+
+ 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
+ restrictions properly. */
+int
+cd_builtin (list)
+ WORD_LIST *list;
+{
+ char *dirname, *cdpath, *path, *temp;
+ int path_index, no_symlinks, opt, lflag;
+
+#if defined (RESTRICTED_SHELL)
+ if (restricted)
+ {
+ sh_restricted ((char *)NULL);
+ return (EXECUTION_FAILURE);
+ }
+#endif /* RESTRICTED_SHELL */
+
+ no_symlinks = no_symbolic_links;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "LP")) != -1)
+ {
+ switch (opt)
+ {
+ case 'P':
+ no_symlinks = 1;
+ break;
+ case 'L':
+ no_symlinks = 0;
+ break;
+ default:
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+ }
+ list = loptend;
+
+ lflag = (cdable_vars ? LCD_DOVARS : 0) |
+ ((interactive && cdspelling) ? LCD_DOSPELL : 0);
+
+ if (list == 0)
+ {
+ /* `cd' without arguments is equivalent to `cd $HOME' */
+ dirname = get_string_value ("HOME");
+
+ if (dirname == 0)
+ {
+ builtin_error (_("HOME not set"));
+ 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");
+
+ if (dirname == 0)
+ {
+ builtin_error (_("OLDPWD not set"));
+ return (EXECUTION_FAILURE);
+ }
+#if 0
+ lflag = interactive ? LCD_PRINTPATH : 0;
+#else
+ lflag = LCD_PRINTPATH; /* According to SUSv3 */
+#endif
+ }
+ else if (absolute_pathname (list->word->word))
+ dirname = list->word->word;
+ else if (cdpath = get_string_value ("CDPATH"))
+ {
+ dirname = list->word->word;
+
+ /* 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 = sh_makepath (path, dirname, MP_DOTILDE);
+ free (path);
+
+ 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 && (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);
+ }
+
+ /* 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])
+ {
+ 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, 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))
+ {
+ printf ("%s\n", temp);
+ 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 (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);
+ }
+
+ builtin_error ("%s: %s", dirname, strerror (errno));
+ return (EXECUTION_FAILURE);
+}
+
+$BUILTIN pwd
+$FUNCTION pwd_builtin
+$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.
+$END
+
+/* Non-zero means that pwd always prints the physical directory, without
+ symbolic links. */
+static int verbatim_pwd;
+
+/* Print the name of the current working directory. */
+int
+pwd_builtin (list)
+ WORD_LIST *list;
+{
+ char *directory;
+ int opt, pflag;
+
+ verbatim_pwd = no_symbolic_links;
+ pflag = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "LP")) != -1)
+ {
+ switch (opt)
+ {
+ case 'P':
+ verbatim_pwd = pflag = 1;
+ break;
+ case 'L':
+ verbatim_pwd = 0;
+ break;
+ default:
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+ }
+ list = loptend;
+
+#define tcwd the_current_working_directory
+
+ 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);
+ /* This is dumb but posix-mandated. */
+ if (posixly_correct && pflag)
+ setpwd (directory);
+ if (directory != the_current_working_directory)
+ free (directory);
+ fflush (stdout);
+ if (ferror (stdout))
+ {
+ sh_wrerror ();
+ clearerr (stdout);
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+ }
+ else
+ return (EXECUTION_FAILURE);
+}
+
+/* Do the work of changing to the directory NEWDIR. Handle symbolic
+ 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;
+{
+ char *t, *tdir;
+ int err, canon_failed, r, ndlen, dlen;
+
+ tdir = (char *)NULL;
+
+ if (the_current_working_directory == 0)
+ {
+ t = get_working_directory ("chdir");
+ FREE (t);
+ }
+
+ t = make_absolute (newdir, the_current_working_directory);
+
+ /* 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);
+
+ ndlen = strlen (newdir);
+ dlen = strlen (t);
+
+ /* 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)
+ {
+ t = resetpwd ("cd");
+ if (t == 0)
+ set_working_directory (tdir);
+ }
+ else
+ set_working_directory (tdir);
+
+ free (tdir);
+ return (1);
+ }
+
+ /* 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;
+
+ /* 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
+ free (t);
+
+ r = 1;
+ }
+ else
+ {
+ errno = err;
+ r = 0;
+ }
+
+ free (tdir);
+ return r;
+}
+
+/* Code for cd spelling correction. Original patch submitted by
+ Neil Russel (caret@c-side.com). */
+
+static char *
+cdspell (dirname)
+ char *dirname;
+{
+ int n;
+ char *guess;
+
+ n = (strlen (dirname) * 3 + 1) / 2 + 1;
+ guess = (char *)xmalloc (n);
+
+ switch (spname (dirname, guess))
+ {
+ case -1:
+ default:
+ free (guess);
+ return (char *)NULL;
+ case 0:
+ case 1:
+ return guess;
+ }
+}