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)
29 #include "../bashtypes.h"
30 #include "../posixdir.h"
31 #include "../posixstat.h"
32 #include <sys/param.h>
36 #include "../bashansi.h"
39 #include <tilde/tilde.h>
43 #include "../maxpath.h"
45 #include "bashgetopt.h"
51 extern int posixly_correct, interactive;
52 extern int array_needs_making;
53 extern char *bash_getcwd_errstr;
55 static int change_to_directory ();
57 static char *cdspell ();
58 static int spname (), mindist (), spdist ();
60 /* Change this to 1 to get cd spelling correction by default. */
67 $SHORT_DOC cd [-PL] [dir]
68 Change the current directory to DIR. The variable $HOME is the
69 default DIR. The variable $CDPATH defines the search path for
70 the directory containing DIR. Alternative directory names in CDPATH
71 are separated by a colon (:). A null directory name is the same as
72 the current directory, i.e. `.'. If DIR begins with a slash (/),
73 then $CDPATH is not used. If the directory is not found, and the
74 shell option `cdable_vars' is set, then try the word as a variable
75 name. If that variable has a value, then cd to the value of that
76 variable. The -P option says to use the physical directory structure
77 instead of following symbolic links; the -L option forces symbolic links
81 /* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste
82 them together into PATH/DIR. Tilde expansion is performed on PATH if
83 DOTILDE is non-zero. If PATH is the empty string, it is converted to
84 `./', since a null element in $CDPATH means the current directory. */
86 mkpath (path, dir, dotilde)
102 xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
103 pathlen = strlen (xpath);
106 dirlen = strlen (dir);
107 ret = xmalloc (2 + dirlen + pathlen);
109 if (xpath[pathlen - 1] != '/')
111 ret[pathlen++] = '/';
114 strcpy (ret + pathlen, dir);
121 bindpwd (no_symlinks)
124 char *dirname, *pwdvar;
125 int old_symlinks, old_anm;
130 old_symlinks = no_symbolic_links;
131 no_symbolic_links = 1;
132 dirname = get_working_directory ("cd");
133 no_symbolic_links = old_symlinks;
136 dirname = get_working_directory ("cd");
138 bind_variable ("OLDPWD", get_string_value ("PWD"));
140 old_anm = array_needs_making;
141 tvar = bind_variable ("PWD", dirname);
142 /* This is an efficiency hack. If PWD is exported, we will need to
143 remake the exported environment every time we change directories.
144 If there is no other reason to make the exported environment, just
145 update PWD in place and mark the exported environment as no longer
147 if (old_anm == 0 && array_needs_making && exported_p (tvar))
149 pwdvar = xmalloc (strlen (dirname) + 5); /* 5 = "PWD" + '=' + '\0' */
150 strcpy (pwdvar, "PWD=");
151 strcpy (pwdvar + 4, dirname);
152 add_or_supercede_exported_var (pwdvar, 0);
153 array_needs_making = 0;
157 return (EXECUTION_SUCCESS);
160 /* This builtin is ultimately the way that all user-visible commands should
161 change the current working directory. It is called by cd_to_string (),
162 so the programming interface is simple, and it handles errors and
163 restrictions properly. */
168 char *dirname, *cdpath, *path, *temp;
169 int path_index, no_symlinks, opt;
172 #if defined (RESTRICTED_SHELL)
175 builtin_error ("restricted");
176 return (EXECUTION_FAILURE);
178 #endif /* RESTRICTED_SHELL */
180 no_symlinks = no_symbolic_links;
181 reset_internal_getopt ();
182 while ((opt = internal_getopt (list, "LP")) != -1)
194 return (EXECUTION_FAILURE);
201 /* `cd' without arguments is equivalent to `cd $HOME' */
202 dirname = get_string_value ("HOME");
206 builtin_error ("HOME not set");
207 return (EXECUTION_FAILURE);
210 if (change_to_directory (dirname, no_symlinks) == 0)
212 builtin_error ("%s: %s", dirname, strerror (errno));
213 return (EXECUTION_FAILURE);
216 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
218 /* This is `cd -', equivalent to `cd $OLDPWD' */
219 dirname = get_string_value ("OLDPWD");
221 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
224 builtin_error ("OLDPWD not set");
226 builtin_error ("%s: %s", dirname, strerror (errno));
227 return (EXECUTION_FAILURE);
232 dirname = list->word->word;
234 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
236 /* Find directory in $CDPATH. */
238 while ((path = extract_colon_unit (cdpath, &path_index)))
240 temp = mkpath (path, dirname, 1);
243 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
249 if (change_to_directory (temp, no_symlinks))
251 if (temp[0] != '.' || temp[1] != '/')
252 printf ("%s\n", temp);
255 /* Posix.2 says that after using CDPATH, the resultant
256 value of $PWD will not contain symlinks. */
257 return (bindpwd (posixly_correct));
264 if (change_to_directory (dirname, no_symlinks))
265 return (bindpwd (no_symlinks));
267 /* If the user requests it, then perhaps this is the name of
268 a shell variable, whose value contains the directory to
269 change to. If that is the case, then change to that
273 temp = get_string_value (dirname);
274 if (temp && change_to_directory (temp, no_symlinks))
276 printf ("%s\n", temp);
277 return (bindpwd (no_symlinks));
281 /* If the user requests it, try to find a directory name similar in
282 spelling to the one requested, in case the user made a simple
283 typo. This is similar to the UNIX 8th and 9th Edition shells. */
284 if (interactive && cdspelling)
286 temp = cdspell (dirname);
287 if (temp && change_to_directory (temp, no_symlinks))
289 printf ("%s\n", temp);
291 return (bindpwd (no_symlinks));
297 builtin_error ("%s: %s", dirname, strerror (errno));
298 return (EXECUTION_FAILURE);
301 return (bindpwd (no_symlinks));
305 $FUNCTION pwd_builtin
307 Print the current working directory. With the -P option, pwd prints
308 the physical directory, without any symbolic links; the -L option
309 makes pwd follow symbolic links.
312 /* Non-zero means that pwd always prints the physical directory, without
314 static int verbatim_pwd;
316 /* Print the name of the current working directory. */
321 char *directory, *buffer;
324 verbatim_pwd = no_symbolic_links;
325 reset_internal_getopt ();
326 while ((opt = internal_getopt (list, "LP")) != -1)
338 return (EXECUTION_FAILURE);
345 buffer = xmalloc (PATH_MAX);
346 directory = getcwd (buffer, PATH_MAX);
350 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
355 directory = get_working_directory ("pwd");
359 printf ("%s\n", directory);
362 return (EXECUTION_SUCCESS);
365 return (EXECUTION_FAILURE);
368 /* Do the work of changing to the directory NEWDIR. Handle symbolic
369 link following, etc. */
372 change_to_directory (newdir, nolinks)
380 int chdir_return = 0;
381 char *tdir = (char *)NULL;
383 if (the_current_working_directory == 0)
385 t = get_working_directory ("cd_links");
389 if (the_current_working_directory)
390 t = make_absolute (newdir, the_current_working_directory);
392 t = savestring (newdir);
394 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
395 tdir = canonicalize_pathname (t);
397 /* Use the canonicalized version of NEWDIR, or, if canonicalization
398 failed, use the non-canonical form. */
407 if (chdir (tdir) < 0)
416 /* We failed changing to the canonicalized directory name. Try
417 what the user passed verbatim. If we succeed, reinitialize
418 the_current_working_directory. */
419 if (chdir (newdir) == 0)
422 if (the_current_working_directory)
424 free (the_current_working_directory);
425 the_current_working_directory = (char *)NULL;
428 tdir = get_working_directory ("cd");
438 FREE (the_current_working_directory);
439 the_current_working_directory = tdir;
442 return (chdir_return);
445 return (chdir (newdir) == 0);
448 /* Code for cd spelling correction. Original patch submitted by
449 Neil Russel (caret@c-side.com). */
458 n = (strlen (dirname) * 3 + 1) / 2 + 1;
461 switch (spname (dirname, guess))
474 * `spname' and its helpers are inspired by the code in "The UNIX
475 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
480 * `spname' -- return a correctly spelled filename
482 * int spname(char * oldname, char * newname)
483 * Returns: -1 if no reasonable match found
484 * 0 if exact match found
486 * Stores corrected name in `newname'.
489 spname(oldname, newname)
494 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
500 while (*op == '/') /* Skip slashes */
504 if (*op == '\0') /* Exact or corrected */
506 /* `.' is rarely the right thing. */
507 if (oldname[1] == '\0' && newname[1] == '\0' &&
508 oldname[0] != '.' && newname[0] == '.')
510 return strcmp(oldname, newname) != 0;
513 /* Copy next component into guess */
514 for (p = guess; *op != '/' && *op != '\0'; op++)
515 if (p < guess + PATH_MAX)
519 if (mindist(newname, guess, best) >= 3)
520 return -1; /* Hopeless */
523 * Add to end of newname
525 for (p = best; *np = *p++; np++)
531 * Search directory for a guess
534 mindist(dir, guess, best)
543 dist = 3; /* Worst distance */
547 if ((fd = opendir(dir)) == NULL)
550 while ((dp = readdir(fd)) != NULL)
553 * Look for a better guess. If the new guess is as
554 * good as the current one, we take it. This way,
555 * any single character match will be a better match
558 x = spdist(dp->d_name, guess);
559 if (x <= dist && x != 3)
561 strcpy(best, dp->d_name);
563 if (dist == 0) /* Exact match */
569 /* Don't return `.' */
570 if (best[0] == '.' && best[1] == '\0')
576 * `spdist' -- return the "distance" between two names.
578 * int spname(char * oldname, char * newname)
579 * Returns: 0 if strings are identical
580 * 1 if two characters are transposed
581 * 2 if one character is wrong, added or deleted
591 return 0; /* Exact match */
600 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
601 return 1; /* Transposition */
603 if (strcmp (cur + 1, new + 1) == 0)
604 return 2; /* One character mismatch */
607 if (strcmp(&cur[1], &new[0]) == 0)
608 return 2; /* Extra character */
611 if (*new && strcmp(cur, new + 1) == 0)
612 return 2; /* Missing character */