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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
25 #if defined (HAVE_UNISTD_H)
27 # include <sys/types.h>
32 #include "../bashtypes.h"
33 #include "../posixdir.h"
34 #include "../posixstat.h"
36 #include <sys/param.h>
41 #include "../bashansi.h"
44 #include <tilde/tilde.h>
48 #include "../maxpath.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
86 /* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste
87 them together into PATH/DIR. Tilde expansion is performed on PATH if
88 DOTILDE is non-zero. If PATH is the empty string, it is converted to
89 `./', since a null element in $CDPATH means the current directory. */
91 mkpath (path, dir, dotilde)
107 xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
108 pathlen = strlen (xpath);
111 dirlen = strlen (dir);
112 ret = xmalloc (2 + dirlen + pathlen);
114 if (xpath[pathlen - 1] != '/')
116 ret[pathlen++] = '/';
119 strcpy (ret + pathlen, dir);
126 bindpwd (no_symlinks)
129 char *dirname, *pwdvar;
130 int old_symlinks, old_anm;
135 old_symlinks = no_symbolic_links;
136 no_symbolic_links = 1;
137 dirname = get_working_directory ("cd");
138 no_symbolic_links = old_symlinks;
141 dirname = get_working_directory ("cd");
143 bind_variable ("OLDPWD", get_string_value ("PWD"));
145 old_anm = array_needs_making;
146 tvar = bind_variable ("PWD", dirname);
147 /* This is an efficiency hack. If PWD is exported, we will need to
148 remake the exported environment every time we change directories.
149 If there is no other reason to make the exported environment, just
150 update PWD in place and mark the exported environment as no longer
152 if (old_anm == 0 && array_needs_making && exported_p (tvar))
154 pwdvar = xmalloc (STRLEN (dirname) + 5); /* 5 = "PWD" + '=' + '\0' */
155 strcpy (pwdvar, "PWD=");
157 strcpy (pwdvar + 4, dirname);
158 add_or_supercede_exported_var (pwdvar, 0);
159 array_needs_making = 0;
163 return (EXECUTION_SUCCESS);
166 /* This builtin is ultimately the way that all user-visible commands should
167 change the current working directory. It is called by cd_to_string (),
168 so the programming interface is simple, and it handles errors and
169 restrictions properly. */
174 char *dirname, *cdpath, *path, *temp;
175 int path_index, no_symlinks, opt;
178 #if defined (RESTRICTED_SHELL)
181 builtin_error ("restricted");
182 return (EXECUTION_FAILURE);
184 #endif /* RESTRICTED_SHELL */
186 no_symlinks = no_symbolic_links;
187 reset_internal_getopt ();
188 while ((opt = internal_getopt (list, "LP")) != -1)
200 return (EXECUTION_FAILURE);
207 /* `cd' without arguments is equivalent to `cd $HOME' */
208 dirname = get_string_value ("HOME");
212 builtin_error ("HOME not set");
213 return (EXECUTION_FAILURE);
216 if (change_to_directory (dirname, no_symlinks) == 0)
218 builtin_error ("%s: %s", dirname, strerror (errno));
219 return (EXECUTION_FAILURE);
222 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
224 /* This is `cd -', equivalent to `cd $OLDPWD' */
225 dirname = get_string_value ("OLDPWD");
227 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
230 builtin_error ("OLDPWD not set");
232 builtin_error ("%s: %s", dirname, strerror (errno));
233 return (EXECUTION_FAILURE);
236 printf ("%s\n", dirname);
240 dirname = list->word->word;
242 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
244 /* Find directory in $CDPATH. */
246 while ((path = extract_colon_unit (cdpath, &path_index)))
248 /* OPT is 1 if the path element is non-empty */
249 opt = path[0] != '\0';
250 temp = mkpath (path, dirname, 1);
253 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
259 if (change_to_directory (temp, no_symlinks))
261 /* POSIX.2 says that if a nonempty directory from CDPATH
262 is used to find the directory to change to, the new
263 directory name is echoed to stdout, whether or not
264 the shell is interactive. */
266 printf ("%s\n", the_current_working_directory);
269 /* Posix.2 says that after using CDPATH, the resultant
270 value of $PWD will not contain symlinks. */
271 return (bindpwd (posixly_correct || no_symlinks));
277 /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
278 try the current directory, so we just punt now with an error
279 message if POSIXLY_CORRECT is non-zero. */
282 builtin_error ("%s: %s", dirname, strerror (ENOENT));
283 return (EXECUTION_FAILURE);
287 if (change_to_directory (dirname, no_symlinks))
288 return (bindpwd (no_symlinks));
290 /* If the user requests it, then perhaps this is the name of
291 a shell variable, whose value contains the directory to
292 change to. If that is the case, then change to that
296 temp = get_string_value (dirname);
297 if (temp && change_to_directory (temp, no_symlinks))
299 printf ("%s\n", temp);
300 return (bindpwd (no_symlinks));
304 /* If the user requests it, try to find a directory name similar in
305 spelling to the one requested, in case the user made a simple
306 typo. This is similar to the UNIX 8th and 9th Edition shells. */
307 if (interactive && cdspelling)
309 temp = cdspell (dirname);
310 if (temp && change_to_directory (temp, no_symlinks))
312 printf ("%s\n", temp);
314 return (bindpwd (no_symlinks));
320 builtin_error ("%s: %s", dirname, strerror (errno));
321 return (EXECUTION_FAILURE);
324 return (bindpwd (no_symlinks));
328 $FUNCTION pwd_builtin
330 Print the current working directory. With the -P option, pwd prints
331 the physical directory, without any symbolic links; the -L option
332 makes pwd follow symbolic links.
335 /* Non-zero means that pwd always prints the physical directory, without
337 static int verbatim_pwd;
339 /* Print the name of the current working directory. */
344 char *directory, *buffer;
347 verbatim_pwd = no_symbolic_links;
348 reset_internal_getopt ();
349 while ((opt = internal_getopt (list, "LP")) != -1)
361 return (EXECUTION_FAILURE);
368 buffer = xmalloc (PATH_MAX);
369 directory = getcwd (buffer, PATH_MAX);
373 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
378 directory = get_working_directory ("pwd");
382 printf ("%s\n", directory);
385 return (EXECUTION_SUCCESS);
388 return (EXECUTION_FAILURE);
391 /* Do the work of changing to the directory NEWDIR. Handle symbolic
392 link following, etc. */
395 change_to_directory (newdir, nolinks)
403 int chdir_return = 0;
404 char *tdir = (char *)NULL;
406 if (the_current_working_directory == 0)
408 t = get_working_directory ("cd_links");
412 if (the_current_working_directory)
413 t = make_absolute (newdir, the_current_working_directory);
415 t = savestring (newdir);
417 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
418 tdir = canonicalize_pathname (t);
420 /* Use the canonicalized version of NEWDIR, or, if canonicalization
421 failed, use the non-canonical form. */
430 if (chdir (tdir) < 0)
439 /* We failed changing to the canonicalized directory name. Try
440 what the user passed verbatim. If we succeed, reinitialize
441 the_current_working_directory. */
442 if (chdir (newdir) == 0)
445 if (the_current_working_directory)
447 free (the_current_working_directory);
448 the_current_working_directory = (char *)NULL;
451 tdir = get_working_directory ("cd");
461 FREE (the_current_working_directory);
462 the_current_working_directory = tdir;
465 return (chdir_return);
468 return (chdir (newdir) == 0);
471 /* Code for cd spelling correction. Original patch submitted by
472 Neil Russel (caret@c-side.com). */
481 n = (strlen (dirname) * 3 + 1) / 2 + 1;
484 switch (spname (dirname, guess))
497 * `spname' and its helpers are inspired by the code in "The UNIX
498 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
503 * `spname' -- return a correctly spelled filename
505 * int spname(char * oldname, char * newname)
506 * Returns: -1 if no reasonable match found
507 * 0 if exact match found
509 * Stores corrected name in `newname'.
512 spname(oldname, newname)
517 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
523 while (*op == '/') /* Skip slashes */
527 if (*op == '\0') /* Exact or corrected */
529 /* `.' is rarely the right thing. */
530 if (oldname[1] == '\0' && newname[1] == '\0' &&
531 oldname[0] != '.' && newname[0] == '.')
533 return strcmp(oldname, newname) != 0;
536 /* Copy next component into guess */
537 for (p = guess; *op != '/' && *op != '\0'; op++)
538 if (p < guess + PATH_MAX)
542 if (mindist(newname, guess, best) >= 3)
543 return -1; /* Hopeless */
546 * Add to end of newname
548 for (p = best; *np = *p++; np++)
554 * Search directory for a guess
557 mindist(dir, guess, best)
566 dist = 3; /* Worst distance */
570 if ((fd = opendir(dir)) == NULL)
573 while ((dp = readdir(fd)) != NULL)
576 * Look for a better guess. If the new guess is as
577 * good as the current one, we take it. This way,
578 * any single character match will be a better match
581 x = spdist(dp->d_name, guess);
582 if (x <= dist && x != 3)
584 strcpy(best, dp->d_name);
586 if (dist == 0) /* Exact match */
592 /* Don't return `.' */
593 if (best[0] == '.' && best[1] == '\0')
599 * `spdist' -- return the "distance" between two names.
601 * int spname(char * oldname, char * newname)
602 * Returns: 0 if strings are identical
603 * 1 if two characters are transposed
604 * 2 if one character is wrong, added or deleted
614 return 0; /* Exact match */
623 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
624 return 1; /* Transposition */
626 if (strcmp (cur + 1, new + 1) == 0)
627 return 2; /* One character mismatch */
630 if (strcmp(&cur[1], &new[0]) == 0)
631 return 2; /* Extra character */
634 if (*new && strcmp(cur, new + 1) == 0)
635 return 2; /* Missing character */