1 This file is cd.def, from which is created cd.c. It implements the
2 builtins "cd" and "pwd" in Bash.
4 Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
6 This file is part of GNU Bash, the Bourne Again SHell.
8 Bash is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with Bash; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
25 #if defined (HAVE_UNISTD_H)
27 # include <sys/types.h>
32 #include "../bashtypes.h"
34 #include "posixstat.h"
36 #include <sys/param.h>
41 #include "../bashansi.h"
44 #include <tilde/tilde.h>
50 #include "bashgetopt.h"
56 extern int posixly_correct, interactive;
57 extern int array_needs_making;
58 extern char *bash_getcwd_errstr;
60 static int change_to_directory ();
62 static char *cdspell ();
63 static int spname (), mindist (), spdist ();
65 /* Change this to 1 to get cd spelling correction by default. */
72 $SHORT_DOC cd [-PL] [dir]
73 Change the current directory to DIR. The variable $HOME is the
74 default DIR. The variable CDPATH defines the search path for
75 the directory containing DIR. Alternative directory names in CDPATH
76 are separated by a colon (:). A null directory name is the same as
77 the current directory, i.e. `.'. If DIR begins with a slash (/),
78 then CDPATH is not used. If the directory is not found, and the
79 shell option `cdable_vars' is set, then try the word as a variable
80 name. If that variable has a value, then cd to the value of that
81 variable. The -P option says to use the physical directory structure
82 instead of following symbolic links; the -L option forces symbolic links
90 char *dirname, *pwdvar;
91 int old_symlinks, old_anm;
96 old_symlinks = no_symbolic_links;
97 no_symbolic_links = 1;
98 dirname = get_working_directory ("cd");
99 no_symbolic_links = old_symlinks;
102 dirname = get_working_directory ("cd");
104 old_anm = array_needs_making;
105 pwdvar = get_string_value ("PWD");
107 tvar = bind_variable ("OLDPWD", pwdvar);
108 if (old_anm == 0 && array_needs_making && exported_p (tvar))
110 update_export_env_inplace ("OLDPWD=", 7, pwdvar);
111 array_needs_making = 0;
114 tvar = bind_variable ("PWD", dirname);
115 if (old_anm == 0 && array_needs_making && exported_p (tvar))
117 update_export_env_inplace ("PWD=", 4, dirname);
118 array_needs_making = 0;
122 return (EXECUTION_SUCCESS);
125 /* This builtin is ultimately the way that all user-visible commands should
126 change the current working directory. It is called by cd_to_string (),
127 so the programming interface is simple, and it handles errors and
128 restrictions properly. */
133 char *dirname, *cdpath, *path, *temp;
134 int path_index, no_symlinks, opt;
137 #if defined (RESTRICTED_SHELL)
140 builtin_error ("restricted");
141 return (EXECUTION_FAILURE);
143 #endif /* RESTRICTED_SHELL */
145 no_symlinks = no_symbolic_links;
146 reset_internal_getopt ();
147 while ((opt = internal_getopt (list, "LP")) != -1)
159 return (EXECUTION_FAILURE);
166 /* `cd' without arguments is equivalent to `cd $HOME' */
167 dirname = get_string_value ("HOME");
171 builtin_error ("HOME not set");
172 return (EXECUTION_FAILURE);
175 if (change_to_directory (dirname, no_symlinks) == 0)
177 builtin_error ("%s: %s", dirname, strerror (errno));
178 return (EXECUTION_FAILURE);
181 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
183 /* This is `cd -', equivalent to `cd $OLDPWD' */
184 dirname = get_string_value ("OLDPWD");
186 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
189 builtin_error ("OLDPWD not set");
191 builtin_error ("%s: %s", dirname, strerror (errno));
192 return (EXECUTION_FAILURE);
195 printf ("%s\n", dirname);
199 dirname = list->word->word;
201 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
203 /* Find directory in $CDPATH. */
205 while (path = extract_colon_unit (cdpath, &path_index))
207 /* OPT is 1 if the path element is non-empty */
208 opt = path[0] != '\0';
209 temp = sh_makepath (path, dirname, MP_DOTILDE);
212 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
218 if (change_to_directory (temp, no_symlinks))
220 /* POSIX.2 says that if a nonempty directory from CDPATH
221 is used to find the directory to change to, the new
222 directory name is echoed to stdout, whether or not
223 the shell is interactive. */
225 printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
228 /* Posix.2 says that after using CDPATH, the resultant
229 value of $PWD will not contain symlinks. */
230 return (bindpwd (posixly_correct || no_symlinks));
236 /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
237 try the current directory, so we just punt now with an error
238 message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
239 is so we don't mistakenly treat a CDPATH value of "" as not
240 specifying the current directory. */
241 if (posixly_correct && cdpath[0])
243 builtin_error ("%s: %s", dirname, strerror (ENOENT));
244 return (EXECUTION_FAILURE);
248 if (change_to_directory (dirname, no_symlinks))
249 return (bindpwd (no_symlinks));
251 /* If the user requests it, then perhaps this is the name of
252 a shell variable, whose value contains the directory to
253 change to. If that is the case, then change to that
257 temp = get_string_value (dirname);
258 if (temp && change_to_directory (temp, no_symlinks))
260 printf ("%s\n", temp);
261 return (bindpwd (no_symlinks));
265 /* If the user requests it, try to find a directory name similar in
266 spelling to the one requested, in case the user made a simple
267 typo. This is similar to the UNIX 8th and 9th Edition shells. */
268 if (interactive && cdspelling)
270 temp = cdspell (dirname);
271 if (temp && change_to_directory (temp, no_symlinks))
273 printf ("%s\n", temp);
275 return (bindpwd (no_symlinks));
281 builtin_error ("%s: %s", dirname, strerror (errno));
282 return (EXECUTION_FAILURE);
285 return (bindpwd (no_symlinks));
289 $FUNCTION pwd_builtin
291 Print the current working directory. With the -P option, pwd prints
292 the physical directory, without any symbolic links; the -L option
293 makes pwd follow symbolic links.
296 /* Non-zero means that pwd always prints the physical directory, without
298 static int verbatim_pwd;
300 /* Print the name of the current working directory. */
305 char *directory, *buffer;
308 verbatim_pwd = no_symbolic_links;
309 reset_internal_getopt ();
310 while ((opt = internal_getopt (list, "LP")) != -1)
322 return (EXECUTION_FAILURE);
329 buffer = xmalloc (PATH_MAX);
330 directory = getcwd (buffer, PATH_MAX);
334 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
339 directory = get_working_directory ("pwd");
343 printf ("%s\n", directory);
346 return (EXECUTION_SUCCESS);
349 return (EXECUTION_FAILURE);
352 /* Do the work of changing to the directory NEWDIR. Handle symbolic
353 link following, etc. */
356 change_to_directory (newdir, nolinks)
364 int chdir_return = 0;
365 char *tdir = (char *)NULL;
367 if (the_current_working_directory == 0)
369 t = get_working_directory ("cd_links");
373 if (the_current_working_directory)
374 t = make_absolute (newdir, the_current_working_directory);
376 t = savestring (newdir);
378 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
379 tdir = canonicalize_pathname (t);
381 /* Use the canonicalized version of NEWDIR, or, if canonicalization
382 failed, use the non-canonical form. */
391 if (chdir (tdir) < 0)
400 /* We failed changing to the canonicalized directory name. Try
401 what the user passed verbatim. If we succeed, reinitialize
402 the_current_working_directory. */
403 if (chdir (newdir) == 0)
406 if (the_current_working_directory)
408 free (the_current_working_directory);
409 the_current_working_directory = (char *)NULL;
412 tdir = get_working_directory ("cd");
422 FREE (the_current_working_directory);
423 the_current_working_directory = tdir;
426 return (chdir_return);
429 return (chdir (newdir) == 0);
432 /* Code for cd spelling correction. Original patch submitted by
433 Neil Russel (caret@c-side.com). */
442 n = (strlen (dirname) * 3 + 1) / 2 + 1;
445 switch (spname (dirname, guess))
458 * `spname' and its helpers are inspired by the code in "The UNIX
459 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
464 * `spname' -- return a correctly spelled filename
466 * int spname(char * oldname, char * newname)
467 * Returns: -1 if no reasonable match found
468 * 0 if exact match found
470 * Stores corrected name in `newname'.
473 spname(oldname, newname)
478 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
484 while (*op == '/') /* Skip slashes */
488 if (*op == '\0') /* Exact or corrected */
490 /* `.' is rarely the right thing. */
491 if (oldname[1] == '\0' && newname[1] == '\0' &&
492 oldname[0] != '.' && newname[0] == '.')
494 return strcmp(oldname, newname) != 0;
497 /* Copy next component into guess */
498 for (p = guess; *op != '/' && *op != '\0'; op++)
499 if (p < guess + PATH_MAX)
503 if (mindist(newname, guess, best) >= 3)
504 return -1; /* Hopeless */
507 * Add to end of newname
509 for (p = best; *np = *p++; np++)
515 * Search directory for a guess
518 mindist(dir, guess, best)
527 dist = 3; /* Worst distance */
531 if ((fd = opendir(dir)) == NULL)
534 while ((dp = readdir(fd)) != NULL)
537 * Look for a better guess. If the new guess is as
538 * good as the current one, we take it. This way,
539 * any single character match will be a better match
542 x = spdist(dp->d_name, guess);
543 if (x <= dist && x != 3)
545 strcpy(best, dp->d_name);
547 if (dist == 0) /* Exact match */
553 /* Don't return `.' */
554 if (best[0] == '.' && best[1] == '\0')
560 * `spdist' -- return the "distance" between two names.
562 * int spname(char * oldname, char * newname)
563 * Returns: 0 if strings are identical
564 * 1 if two characters are transposed
565 * 2 if one character is wrong, added or deleted
575 return 0; /* Exact match */
584 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
585 return 1; /* Transposition */
587 if (strcmp (cur + 1, new + 1) == 0)
588 return 2; /* One character mismatch */
591 if (strcmp(&cur[1], &new[0]) == 0)
592 return 2; /* Extra character */
595 if (*new && strcmp(cur, new + 1) == 0)
596 return 2; /* Missing character */